blob: b29a1abe1526fb74ed5c23ec97747ea54cb0b0a1 [file] [log] [blame]
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001// Copyright 2006-2009 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include <stdlib.h>
29
30#include "v8.h"
31
32#include "accessors.h"
33#include "api.h"
34#include "arguments.h"
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000035#include "codegen.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000036#include "compiler.h"
37#include "cpu.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000038#include "dateparser-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000039#include "debug.h"
40#include "execution.h"
41#include "jsregexp.h"
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000042#include "liveedit.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000043#include "parser.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000044#include "platform.h"
45#include "runtime.h"
46#include "scopeinfo.h"
ager@chromium.org7c537e22008-10-16 08:43:32 +000047#include "smart-pointer.h"
ager@chromium.org18ad94b2009-09-02 08:22:29 +000048#include "stub-cache.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000049#include "v8threads.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000050
kasperl@chromium.org71affb52009-05-26 05:44:31 +000051namespace v8 {
52namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000053
54
ager@chromium.org3e875802009-06-29 08:26:34 +000055#define RUNTIME_ASSERT(value) \
56 if (!(value)) return Top::ThrowIllegalOperation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000057
58// Cast the given object to a value of the specified type and store
59// it in a variable with the given name. If the object is not of the
60// expected type call IllegalOperation and return.
61#define CONVERT_CHECKED(Type, name, obj) \
62 RUNTIME_ASSERT(obj->Is##Type()); \
63 Type* name = Type::cast(obj);
64
65#define CONVERT_ARG_CHECKED(Type, name, index) \
66 RUNTIME_ASSERT(args[index]->Is##Type()); \
67 Handle<Type> name = args.at<Type>(index);
68
kasper.lundbd3ec4e2008-07-09 11:06:54 +000069// Cast the given object to a boolean and store it in a variable with
70// the given name. If the object is not a boolean call IllegalOperation
71// and return.
72#define CONVERT_BOOLEAN_CHECKED(name, obj) \
73 RUNTIME_ASSERT(obj->IsBoolean()); \
74 bool name = (obj)->IsTrue();
75
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000076// Cast the given object to a Smi and store its value in an int variable
77// with the given name. If the object is not a Smi call IllegalOperation
78// and return.
79#define CONVERT_SMI_CHECKED(name, obj) \
80 RUNTIME_ASSERT(obj->IsSmi()); \
81 int name = Smi::cast(obj)->value();
82
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000083// Cast the given object to a double and store it in a variable with
84// the given name. If the object is not a number (as opposed to
85// the number not-a-number) call IllegalOperation and return.
86#define CONVERT_DOUBLE_CHECKED(name, obj) \
87 RUNTIME_ASSERT(obj->IsNumber()); \
88 double name = (obj)->Number();
89
90// Call the specified converter on the object *comand store the result in
91// a variable of the specified type with the given name. If the
92// object is not a Number call IllegalOperation and return.
93#define CONVERT_NUMBER_CHECKED(type, name, Type, obj) \
94 RUNTIME_ASSERT(obj->IsNumber()); \
95 type name = NumberTo##Type(obj);
96
97// Non-reentrant string buffer for efficient general use in this file.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000098static StaticResource<StringInputBuffer> runtime_string_input_buffer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000099
100
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000101static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
102 StackLimitCheck check;
103 if (check.HasOverflowed()) return Top::StackOverflow();
104
105 Object* result = Heap::CopyJSObject(boilerplate);
106 if (result->IsFailure()) return result;
107 JSObject* copy = JSObject::cast(result);
108
109 // Deep copy local properties.
110 if (copy->HasFastProperties()) {
111 FixedArray* properties = copy->properties();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000112 for (int i = 0; i < properties->length(); i++) {
113 Object* value = properties->get(i);
114 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000115 JSObject* js_object = JSObject::cast(value);
116 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000117 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000118 properties->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000119 }
120 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000121 int nof = copy->map()->inobject_properties();
122 for (int i = 0; i < nof; i++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000123 Object* value = copy->InObjectPropertyAt(i);
124 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000125 JSObject* js_object = JSObject::cast(value);
126 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000127 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000128 copy->InObjectPropertyAtPut(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000129 }
130 }
131 } else {
132 result = Heap::AllocateFixedArray(copy->NumberOfLocalProperties(NONE));
133 if (result->IsFailure()) return result;
134 FixedArray* names = FixedArray::cast(result);
135 copy->GetLocalPropertyNames(names, 0);
136 for (int i = 0; i < names->length(); i++) {
137 ASSERT(names->get(i)->IsString());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000138 String* key_string = String::cast(names->get(i));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000139 PropertyAttributes attributes =
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000140 copy->GetLocalPropertyAttribute(key_string);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000141 // Only deep copy fields from the object literal expression.
142 // In particular, don't try to copy the length attribute of
143 // an array.
144 if (attributes != NONE) continue;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000145 Object* value = copy->GetProperty(key_string, &attributes);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000146 ASSERT(!value->IsFailure());
147 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000148 JSObject* js_object = JSObject::cast(value);
149 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000150 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000151 result = copy->SetProperty(key_string, result, NONE);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000152 if (result->IsFailure()) return result;
153 }
154 }
155 }
156
157 // Deep copy local elements.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000158 // Pixel elements cannot be created using an object literal.
ager@chromium.org3811b432009-10-28 14:53:37 +0000159 ASSERT(!copy->HasPixelElements() && !copy->HasExternalArrayElements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000160 switch (copy->GetElementsKind()) {
161 case JSObject::FAST_ELEMENTS: {
162 FixedArray* elements = FixedArray::cast(copy->elements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000163 for (int i = 0; i < elements->length(); i++) {
164 Object* value = elements->get(i);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000165 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000166 JSObject* js_object = JSObject::cast(value);
167 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000168 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000169 elements->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000170 }
171 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000172 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000173 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000174 case JSObject::DICTIONARY_ELEMENTS: {
175 NumberDictionary* element_dictionary = copy->element_dictionary();
176 int capacity = element_dictionary->Capacity();
177 for (int i = 0; i < capacity; i++) {
178 Object* k = element_dictionary->KeyAt(i);
179 if (element_dictionary->IsKey(k)) {
180 Object* value = element_dictionary->ValueAt(i);
181 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000182 JSObject* js_object = JSObject::cast(value);
183 result = DeepCopyBoilerplate(js_object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000184 if (result->IsFailure()) return result;
185 element_dictionary->ValueAtPut(i, result);
186 }
187 }
188 }
189 break;
190 }
191 default:
192 UNREACHABLE();
193 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000194 }
195 return copy;
196}
197
198
199static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
200 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
201 return DeepCopyBoilerplate(boilerplate);
202}
203
204
205static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000206 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000207 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000208}
209
210
ager@chromium.org236ad962008-09-25 09:45:57 +0000211static Handle<Map> ComputeObjectLiteralMap(
212 Handle<Context> context,
213 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000214 bool* is_result_from_cache) {
ager@chromium.org32912102009-01-16 10:38:43 +0000215 int number_of_properties = constant_properties->length() / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000216 if (FLAG_canonicalize_object_literal_maps) {
217 // First find prefix of consecutive symbol keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000218 int number_of_symbol_keys = 0;
219 while ((number_of_symbol_keys < number_of_properties) &&
220 (constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
221 number_of_symbol_keys++;
222 }
223 // Based on the number of prefix symbols key we decide whether
224 // to use the map cache in the global context.
225 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000226 if ((number_of_symbol_keys == number_of_properties) &&
227 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000228 // Create the fixed array with the key.
229 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
230 for (int i = 0; i < number_of_symbol_keys; i++) {
231 keys->set(i, constant_properties->get(i*2));
232 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000233 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000234 return Factory::ObjectLiteralMapFromCache(context, keys);
235 }
236 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000237 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000238 return Factory::CopyMap(
239 Handle<Map>(context->object_function()->initial_map()),
240 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000241}
242
243
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000244static Handle<Object> CreateLiteralBoilerplate(
245 Handle<FixedArray> literals,
246 Handle<FixedArray> constant_properties);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000247
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000248
249static Handle<Object> CreateObjectLiteralBoilerplate(
250 Handle<FixedArray> literals,
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000251 Handle<FixedArray> constant_properties,
252 bool should_have_fast_elements) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000253 // Get the global context from the literals array. This is the
254 // context in which the function was created and we use the object
255 // function from this context to create the object literal. We do
256 // not use the object function from the current global context
257 // because this might be the object function from another context
258 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000259 Handle<Context> context =
260 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
261
262 bool is_result_from_cache;
263 Handle<Map> map = ComputeObjectLiteralMap(context,
264 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000265 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000266
ager@chromium.org236ad962008-09-25 09:45:57 +0000267 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000268
269 // Normalize the elements of the boilerplate to save space if needed.
270 if (!should_have_fast_elements) NormalizeElements(boilerplate);
271
ager@chromium.org32912102009-01-16 10:38:43 +0000272 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000273 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000274 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000275 length / 2,
ager@chromium.org236ad962008-09-25 09:45:57 +0000276 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000277 for (int index = 0; index < length; index +=2) {
278 Handle<Object> key(constant_properties->get(index+0));
279 Handle<Object> value(constant_properties->get(index+1));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000280 if (value->IsFixedArray()) {
281 // The value contains the constant_properties of a
282 // simple object literal.
283 Handle<FixedArray> array = Handle<FixedArray>::cast(value);
284 value = CreateLiteralBoilerplate(literals, array);
285 if (value.is_null()) return value;
286 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000287 Handle<Object> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000288 uint32_t element_index = 0;
289 if (key->IsSymbol()) {
290 // If key is a symbol it is not an array element.
291 Handle<String> name(String::cast(*key));
292 ASSERT(!name->AsArrayIndex(&element_index));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000293 result = SetProperty(boilerplate, name, value, NONE);
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000294 } else if (key->ToArrayIndex(&element_index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000295 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000296 result = SetElement(boilerplate, element_index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000297 } else {
298 // Non-uint32 number.
299 ASSERT(key->IsNumber());
300 double num = key->Number();
301 char arr[100];
302 Vector<char> buffer(arr, ARRAY_SIZE(arr));
303 const char* str = DoubleToCString(num, buffer);
304 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000305 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000306 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000307 // If setting the property on the boilerplate throws an
308 // exception, the exception is converted to an empty handle in
309 // the handle based operations. In that case, we need to
310 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000311 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000312 }
313 }
314
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000315 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000316}
317
318
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000319static Handle<Object> CreateArrayLiteralBoilerplate(
320 Handle<FixedArray> literals,
321 Handle<FixedArray> elements) {
322 // Create the JSArray.
323 Handle<JSFunction> constructor(
324 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
325 Handle<Object> object = Factory::NewJSObject(constructor);
326
327 Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
328
329 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
330 for (int i = 0; i < content->length(); i++) {
331 if (content->get(i)->IsFixedArray()) {
332 // The value contains the constant_properties of a
333 // simple object literal.
334 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
335 Handle<Object> result =
336 CreateLiteralBoilerplate(literals, fa);
337 if (result.is_null()) return result;
338 content->set(i, *result);
339 }
340 }
341
342 // Set the elements.
343 Handle<JSArray>::cast(object)->SetContent(*content);
344 return object;
345}
346
347
348static Handle<Object> CreateLiteralBoilerplate(
349 Handle<FixedArray> literals,
350 Handle<FixedArray> array) {
351 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
352 switch (CompileTimeValue::GetType(array)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000353 case CompileTimeValue::OBJECT_LITERAL_FAST_ELEMENTS:
354 return CreateObjectLiteralBoilerplate(literals, elements, true);
355 case CompileTimeValue::OBJECT_LITERAL_SLOW_ELEMENTS:
356 return CreateObjectLiteralBoilerplate(literals, elements, false);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000357 case CompileTimeValue::ARRAY_LITERAL:
358 return CreateArrayLiteralBoilerplate(literals, elements);
359 default:
360 UNREACHABLE();
361 return Handle<Object>::null();
362 }
363}
364
365
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000366static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000367 // Takes a FixedArray of elements containing the literal elements of
368 // the array literal and produces JSArray with those elements.
369 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000370 // which contains the context from which to get the Array function
371 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000372 HandleScope scope;
373 ASSERT(args.length() == 3);
374 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
375 CONVERT_SMI_CHECKED(literals_index, args[1]);
376 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000377
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000378 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
379 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000380
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000381 // Update the functions literal and return the boilerplate.
382 literals->set(literals_index, *object);
383 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000384}
385
386
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000387static Object* Runtime_CreateObjectLiteral(Arguments args) {
388 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000389 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000390 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
391 CONVERT_SMI_CHECKED(literals_index, args[1]);
392 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000393 CONVERT_SMI_CHECKED(fast_elements, args[3]);
394 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000395
396 // Check if boilerplate exists. If not, create it first.
397 Handle<Object> boilerplate(literals->get(literals_index));
398 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000399 boilerplate = CreateObjectLiteralBoilerplate(literals,
400 constant_properties,
401 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000402 if (boilerplate.is_null()) return Failure::Exception();
403 // Update the functions literal and return the boilerplate.
404 literals->set(literals_index, *boilerplate);
405 }
406 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
407}
408
409
410static Object* Runtime_CreateObjectLiteralShallow(Arguments args) {
411 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000412 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000413 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
414 CONVERT_SMI_CHECKED(literals_index, args[1]);
415 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000416 CONVERT_SMI_CHECKED(fast_elements, args[3]);
417 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000418
419 // Check if boilerplate exists. If not, create it first.
420 Handle<Object> boilerplate(literals->get(literals_index));
421 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000422 boilerplate = CreateObjectLiteralBoilerplate(literals,
423 constant_properties,
424 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000425 if (boilerplate.is_null()) return Failure::Exception();
426 // Update the functions literal and return the boilerplate.
427 literals->set(literals_index, *boilerplate);
428 }
429 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
430}
431
432
433static Object* Runtime_CreateArrayLiteral(Arguments args) {
434 HandleScope scope;
435 ASSERT(args.length() == 3);
436 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
437 CONVERT_SMI_CHECKED(literals_index, args[1]);
438 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
439
440 // Check if boilerplate exists. If not, create it first.
441 Handle<Object> boilerplate(literals->get(literals_index));
442 if (*boilerplate == Heap::undefined_value()) {
443 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
444 if (boilerplate.is_null()) return Failure::Exception();
445 // Update the functions literal and return the boilerplate.
446 literals->set(literals_index, *boilerplate);
447 }
448 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
449}
450
451
452static Object* Runtime_CreateArrayLiteralShallow(Arguments args) {
453 HandleScope scope;
454 ASSERT(args.length() == 3);
455 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
456 CONVERT_SMI_CHECKED(literals_index, args[1]);
457 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
458
459 // Check if boilerplate exists. If not, create it first.
460 Handle<Object> boilerplate(literals->get(literals_index));
461 if (*boilerplate == Heap::undefined_value()) {
462 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
463 if (boilerplate.is_null()) return Failure::Exception();
464 // Update the functions literal and return the boilerplate.
465 literals->set(literals_index, *boilerplate);
466 }
467 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
468}
469
470
ager@chromium.org32912102009-01-16 10:38:43 +0000471static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
472 ASSERT(args.length() == 2);
473 CONVERT_CHECKED(String, key, args[0]);
474 Object* value = args[1];
475 // Create a catch context extension object.
476 JSFunction* constructor =
477 Top::context()->global_context()->context_extension_function();
478 Object* object = Heap::AllocateJSObject(constructor);
479 if (object->IsFailure()) return object;
480 // Assign the exception value to the catch variable and make sure
481 // that the catch variable is DontDelete.
482 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
483 if (value->IsFailure()) return value;
484 return object;
485}
486
487
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000488static Object* Runtime_ClassOf(Arguments args) {
489 NoHandleAllocation ha;
490 ASSERT(args.length() == 1);
491 Object* obj = args[0];
492 if (!obj->IsJSObject()) return Heap::null_value();
493 return JSObject::cast(obj)->class_name();
494}
495
ager@chromium.org7c537e22008-10-16 08:43:32 +0000496
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000497static Object* Runtime_IsInPrototypeChain(Arguments args) {
498 NoHandleAllocation ha;
499 ASSERT(args.length() == 2);
500 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
501 Object* O = args[0];
502 Object* V = args[1];
503 while (true) {
504 Object* prototype = V->GetPrototype();
505 if (prototype->IsNull()) return Heap::false_value();
506 if (O == prototype) return Heap::true_value();
507 V = prototype;
508 }
509}
510
511
ager@chromium.org9085a012009-05-11 19:22:57 +0000512// Inserts an object as the hidden prototype of another object.
513static Object* Runtime_SetHiddenPrototype(Arguments args) {
514 NoHandleAllocation ha;
515 ASSERT(args.length() == 2);
516 CONVERT_CHECKED(JSObject, jsobject, args[0]);
517 CONVERT_CHECKED(JSObject, proto, args[1]);
518
519 // Sanity checks. The old prototype (that we are replacing) could
520 // theoretically be null, but if it is not null then check that we
521 // didn't already install a hidden prototype here.
522 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
523 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
524 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
525
526 // Allocate up front before we start altering state in case we get a GC.
527 Object* map_or_failure = proto->map()->CopyDropTransitions();
528 if (map_or_failure->IsFailure()) return map_or_failure;
529 Map* new_proto_map = Map::cast(map_or_failure);
530
531 map_or_failure = jsobject->map()->CopyDropTransitions();
532 if (map_or_failure->IsFailure()) return map_or_failure;
533 Map* new_map = Map::cast(map_or_failure);
534
535 // Set proto's prototype to be the old prototype of the object.
536 new_proto_map->set_prototype(jsobject->GetPrototype());
537 proto->set_map(new_proto_map);
538 new_proto_map->set_is_hidden_prototype();
539
540 // Set the object's prototype to proto.
541 new_map->set_prototype(proto);
542 jsobject->set_map(new_map);
543
544 return Heap::undefined_value();
545}
546
547
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000548static Object* Runtime_IsConstructCall(Arguments args) {
549 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000550 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000551 JavaScriptFrameIterator it;
552 return Heap::ToBoolean(it.frame()->IsConstructor());
553}
554
555
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000556// Recursively traverses hidden prototypes if property is not found
557static void GetOwnPropertyImplementation(JSObject* obj,
558 String* name,
559 LookupResult* result) {
560 obj->LocalLookupRealNamedProperty(name, result);
561
562 if (!result->IsProperty()) {
563 Object* proto = obj->GetPrototype();
564 if (proto->IsJSObject() &&
565 JSObject::cast(proto)->map()->is_hidden_prototype())
566 GetOwnPropertyImplementation(JSObject::cast(proto),
567 name, result);
568 }
569}
570
571
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000572// Enumerator used as indices into the array returned from GetOwnProperty
573enum PropertyDescriptorIndices {
574 IS_ACCESSOR_INDEX,
575 VALUE_INDEX,
576 GETTER_INDEX,
577 SETTER_INDEX,
578 WRITABLE_INDEX,
579 ENUMERABLE_INDEX,
580 CONFIGURABLE_INDEX,
581 DESCRIPTOR_SIZE
582};
583
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000584// Returns an array with the property description:
585// if args[1] is not a property on args[0]
586// returns undefined
587// if args[1] is a data property on args[0]
588// [false, value, Writeable, Enumerable, Configurable]
589// if args[1] is an accessor on args[0]
590// [true, GetFunction, SetFunction, Enumerable, Configurable]
591static Object* Runtime_GetOwnProperty(Arguments args) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000592 ASSERT(args.length() == 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000593 HandleScope scope;
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000594 Handle<FixedArray> elms = Factory::NewFixedArray(DESCRIPTOR_SIZE);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000595 Handle<JSArray> desc = Factory::NewJSArrayWithElements(elms);
596 LookupResult result;
597 CONVERT_CHECKED(JSObject, obj, args[0]);
598 CONVERT_CHECKED(String, name, args[1]);
599
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000600 // This could be an element.
601 uint32_t index;
602 if (name->AsArrayIndex(&index)) {
603 if (!obj->HasLocalElement(index)) {
604 return Heap::undefined_value();
605 }
606
607 // Special handling of string objects according to ECMAScript 5 15.5.5.2.
608 // Note that this might be a string object with elements other than the
609 // actual string value. This is covered by the subsequent cases.
610 if (obj->IsStringObjectWithCharacterAt(index)) {
611 JSValue* js_value = JSValue::cast(obj);
612 String* str = String::cast(js_value->value());
613 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
614 elms->set(VALUE_INDEX, str->SubString(index, index+1));
615 elms->set(WRITABLE_INDEX, Heap::false_value());
616 elms->set(ENUMERABLE_INDEX, Heap::false_value());
617 elms->set(CONFIGURABLE_INDEX, Heap::false_value());
618 return *desc;
619 }
620
621 // This can potentially be an element in the elements dictionary or
622 // a fast element.
623 if (obj->HasDictionaryElements()) {
624 NumberDictionary* dictionary = obj->element_dictionary();
625 int entry = dictionary->FindEntry(index);
626 PropertyDetails details = dictionary->DetailsAt(entry);
627 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
628 elms->set(VALUE_INDEX, dictionary->ValueAt(entry));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000629 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!details.IsReadOnly()));
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000630 elms->set(ENUMERABLE_INDEX, Heap::ToBoolean(!details.IsDontEnum()));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000631 elms->set(CONFIGURABLE_INDEX, Heap::ToBoolean(!details.IsDontDelete()));
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000632 return *desc;
633 } else {
634 // Elements that are stored as array elements always has:
635 // writable: true, configurable: true, enumerable: true.
636 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
637 elms->set(VALUE_INDEX, obj->GetElement(index));
638 elms->set(WRITABLE_INDEX, Heap::true_value());
639 elms->set(ENUMERABLE_INDEX, Heap::true_value());
640 elms->set(CONFIGURABLE_INDEX, Heap::true_value());
641 return *desc;
642 }
643 }
644
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000645 // Use recursive implementation to also traverse hidden prototypes
646 GetOwnPropertyImplementation(obj, name, &result);
647
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000648 if (!result.IsProperty()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000649 return Heap::undefined_value();
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000650 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000651 if (result.type() == CALLBACKS) {
652 Object* structure = result.GetCallbackObject();
ager@chromium.org5c838252010-02-19 08:53:10 +0000653 if (structure->IsProxy() || structure->IsAccessorInfo()) {
654 // Property that is internally implemented as a callback or
655 // an API defined callback.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000656 Object* value = obj->GetPropertyWithCallback(
657 obj, structure, name, result.holder());
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000658 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
659 elms->set(VALUE_INDEX, value);
660 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!result.IsReadOnly()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000661 } else if (structure->IsFixedArray()) {
662 // __defineGetter__/__defineSetter__ callback.
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000663 elms->set(IS_ACCESSOR_INDEX, Heap::true_value());
664 elms->set(GETTER_INDEX, FixedArray::cast(structure)->get(0));
665 elms->set(SETTER_INDEX, FixedArray::cast(structure)->get(1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000666 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000667 return Heap::undefined_value();
668 }
669 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000670 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
671 elms->set(VALUE_INDEX, result.GetLazyValue());
672 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!result.IsReadOnly()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000673 }
674
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000675 elms->set(ENUMERABLE_INDEX, Heap::ToBoolean(!result.IsDontEnum()));
676 elms->set(CONFIGURABLE_INDEX, Heap::ToBoolean(!result.IsDontDelete()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000677 return *desc;
678}
679
680
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000681static Object* Runtime_IsExtensible(Arguments args) {
682 ASSERT(args.length() == 1);
683 CONVERT_CHECKED(JSObject, obj, args[0]);
684 return obj->map()->is_extensible() ? Heap::true_value()
685 : Heap::false_value();
686}
687
688
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000689static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000690 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000691 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000692 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
693 CONVERT_ARG_CHECKED(String, pattern, 1);
694 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000695 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
696 if (result.is_null()) return Failure::Exception();
697 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000698}
699
700
701static Object* Runtime_CreateApiFunction(Arguments args) {
702 HandleScope scope;
703 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000704 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000705 return *Factory::CreateApiFunction(data);
706}
707
708
709static Object* Runtime_IsTemplate(Arguments args) {
710 ASSERT(args.length() == 1);
711 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000712 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000713 return Heap::ToBoolean(result);
714}
715
716
717static Object* Runtime_GetTemplateField(Arguments args) {
718 ASSERT(args.length() == 2);
719 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000720 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000721 int index = field->value();
722 int offset = index * kPointerSize + HeapObject::kHeaderSize;
723 InstanceType type = templ->map()->instance_type();
724 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
725 type == OBJECT_TEMPLATE_INFO_TYPE);
726 RUNTIME_ASSERT(offset > 0);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000727 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000728 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
729 } else {
730 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
731 }
732 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000733}
734
735
ager@chromium.org870a0b62008-11-04 11:43:05 +0000736static Object* Runtime_DisableAccessChecks(Arguments args) {
737 ASSERT(args.length() == 1);
738 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000739 Map* old_map = object->map();
740 bool needs_access_checks = old_map->is_access_check_needed();
741 if (needs_access_checks) {
742 // Copy map so it won't interfere constructor's initial map.
743 Object* new_map = old_map->CopyDropTransitions();
744 if (new_map->IsFailure()) return new_map;
745
746 Map::cast(new_map)->set_is_access_check_needed(false);
747 object->set_map(Map::cast(new_map));
748 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000749 return needs_access_checks ? Heap::true_value() : Heap::false_value();
750}
751
752
753static Object* Runtime_EnableAccessChecks(Arguments args) {
754 ASSERT(args.length() == 1);
755 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000756 Map* old_map = object->map();
757 if (!old_map->is_access_check_needed()) {
758 // Copy map so it won't interfere constructor's initial map.
759 Object* new_map = old_map->CopyDropTransitions();
760 if (new_map->IsFailure()) return new_map;
761
762 Map::cast(new_map)->set_is_access_check_needed(true);
763 object->set_map(Map::cast(new_map));
764 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000765 return Heap::undefined_value();
766}
767
768
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000769static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
770 HandleScope scope;
771 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
772 Handle<Object> args[2] = { type_handle, name };
773 Handle<Object> error =
774 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
775 return Top::Throw(*error);
776}
777
778
779static Object* Runtime_DeclareGlobals(Arguments args) {
780 HandleScope scope;
781 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
782
ager@chromium.org3811b432009-10-28 14:53:37 +0000783 Handle<Context> context = args.at<Context>(0);
784 CONVERT_ARG_CHECKED(FixedArray, pairs, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000785 bool is_eval = Smi::cast(args[2])->value() == 1;
786
787 // Compute the property attributes. According to ECMA-262, section
788 // 13, page 71, the property must be read-only and
789 // non-deletable. However, neither SpiderMonkey nor KJS creates the
790 // property as read-only, so we don't either.
791 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
792
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000793 // Traverse the name/value pairs and set the properties.
794 int length = pairs->length();
795 for (int i = 0; i < length; i += 2) {
796 HandleScope scope;
797 Handle<String> name(String::cast(pairs->get(i)));
798 Handle<Object> value(pairs->get(i + 1));
799
800 // We have to declare a global const property. To capture we only
801 // assign to it when evaluating the assignment for "const x =
802 // <expr>" the initial value is the hole.
803 bool is_const_property = value->IsTheHole();
804
805 if (value->IsUndefined() || is_const_property) {
806 // Lookup the property in the global object, and don't set the
807 // value of the variable if the property is already there.
808 LookupResult lookup;
809 global->Lookup(*name, &lookup);
810 if (lookup.IsProperty()) {
811 // Determine if the property is local by comparing the holder
812 // against the global object. The information will be used to
813 // avoid throwing re-declaration errors when declaring
814 // variables or constants that exist in the prototype chain.
815 bool is_local = (*global == lookup.holder());
816 // Get the property attributes and determine if the property is
817 // read-only.
818 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
819 bool is_read_only = (attributes & READ_ONLY) != 0;
820 if (lookup.type() == INTERCEPTOR) {
821 // If the interceptor says the property is there, we
822 // just return undefined without overwriting the property.
823 // Otherwise, we continue to setting the property.
824 if (attributes != ABSENT) {
825 // Check if the existing property conflicts with regards to const.
826 if (is_local && (is_read_only || is_const_property)) {
827 const char* type = (is_read_only) ? "const" : "var";
828 return ThrowRedeclarationError(type, name);
829 };
830 // The property already exists without conflicting: Go to
831 // the next declaration.
832 continue;
833 }
834 // Fall-through and introduce the absent property by using
835 // SetProperty.
836 } else {
837 if (is_local && (is_read_only || is_const_property)) {
838 const char* type = (is_read_only) ? "const" : "var";
839 return ThrowRedeclarationError(type, name);
840 }
841 // The property already exists without conflicting: Go to
842 // the next declaration.
843 continue;
844 }
845 }
846 } else {
847 // Copy the function and update its context. Use it as value.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000848 Handle<SharedFunctionInfo> shared =
849 Handle<SharedFunctionInfo>::cast(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000850 Handle<JSFunction> function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000851 Factory::NewFunctionFromSharedFunctionInfo(shared, context, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000852 value = function;
853 }
854
855 LookupResult lookup;
856 global->LocalLookup(*name, &lookup);
857
858 PropertyAttributes attributes = is_const_property
859 ? static_cast<PropertyAttributes>(base | READ_ONLY)
860 : base;
861
862 if (lookup.IsProperty()) {
863 // There's a local property that we need to overwrite because
864 // we're either declaring a function or there's an interceptor
865 // that claims the property is absent.
866
867 // Check for conflicting re-declarations. We cannot have
868 // conflicting types in case of intercepted properties because
869 // they are absent.
870 if (lookup.type() != INTERCEPTOR &&
871 (lookup.IsReadOnly() || is_const_property)) {
872 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
873 return ThrowRedeclarationError(type, name);
874 }
875 SetProperty(global, name, value, attributes);
876 } else {
877 // If a property with this name does not already exist on the
878 // global object add the property locally. We take special
879 // precautions to always add it as a local property even in case
880 // of callbacks in the prototype chain (this rules out using
881 // SetProperty). Also, we must use the handle-based version to
882 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000883 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000884 }
885 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000886
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000887 return Heap::undefined_value();
888}
889
890
891static Object* Runtime_DeclareContextSlot(Arguments args) {
892 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000893 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000894
ager@chromium.org7c537e22008-10-16 08:43:32 +0000895 CONVERT_ARG_CHECKED(Context, context, 0);
896 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000897 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000898 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000899 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000900 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000901
902 // Declarations are always done in the function context.
903 context = Handle<Context>(context->fcontext());
904
905 int index;
906 PropertyAttributes attributes;
907 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000908 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000909 context->Lookup(name, flags, &index, &attributes);
910
911 if (attributes != ABSENT) {
912 // The name was declared before; check for conflicting
913 // re-declarations: This is similar to the code in parser.cc in
914 // the AstBuildingParser::Declare function.
915 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
916 // Functions are not read-only.
917 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
918 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
919 return ThrowRedeclarationError(type, name);
920 }
921
922 // Initialize it if necessary.
923 if (*initial_value != NULL) {
924 if (index >= 0) {
925 // The variable or constant context slot should always be in
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000926 // the function context or the arguments object.
927 if (holder->IsContext()) {
928 ASSERT(holder.is_identical_to(context));
929 if (((attributes & READ_ONLY) == 0) ||
930 context->get(index)->IsTheHole()) {
931 context->set(index, *initial_value);
932 }
933 } else {
934 Handle<JSObject>::cast(holder)->SetElement(index, *initial_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000935 }
936 } else {
937 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000938 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000939 SetProperty(context_ext, name, initial_value, mode);
940 }
941 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000942
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000943 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000944 // The property is not in the function context. It needs to be
945 // "declared" in the function context's extension context, or in the
946 // global context.
947 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000948 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000949 // The function context's extension context exists - use it.
950 context_ext = Handle<JSObject>(context->extension());
951 } else {
952 // The function context's extension context does not exists - allocate
953 // it.
954 context_ext = Factory::NewJSObject(Top::context_extension_function());
955 // And store it in the extension slot.
956 context->set_extension(*context_ext);
957 }
958 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000959
ager@chromium.org7c537e22008-10-16 08:43:32 +0000960 // Declare the property by setting it to the initial value if provided,
961 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
962 // constant declarations).
963 ASSERT(!context_ext->HasLocalProperty(*name));
964 Handle<Object> value(Heap::undefined_value());
965 if (*initial_value != NULL) value = initial_value;
966 SetProperty(context_ext, name, value, mode);
967 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
968 }
969
970 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000971}
972
973
974static Object* Runtime_InitializeVarGlobal(Arguments args) {
975 NoHandleAllocation nha;
976
977 // Determine if we need to assign to the variable if it already
978 // exists (based on the number of arguments).
979 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
980 bool assign = args.length() == 2;
981
982 CONVERT_ARG_CHECKED(String, name, 0);
983 GlobalObject* global = Top::context()->global();
984
985 // According to ECMA-262, section 12.2, page 62, the property must
986 // not be deletable.
987 PropertyAttributes attributes = DONT_DELETE;
988
989 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000990 // there, there is a property with this name in the prototype chain.
991 // We follow Safari and Firefox behavior and only set the property
992 // locally if there is an explicit initialization value that we have
993 // to assign to the property. When adding the property we take
994 // special precautions to always add it as a local property even in
995 // case of callbacks in the prototype chain (this rules out using
996 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
997 // this.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000998 // Note that objects can have hidden prototypes, so we need to traverse
999 // the whole chain of hidden prototypes to do a 'local' lookup.
1000 JSObject* real_holder = global;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001001 LookupResult lookup;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001002 while (true) {
1003 real_holder->LocalLookup(*name, &lookup);
1004 if (lookup.IsProperty()) {
1005 // Determine if this is a redeclaration of something read-only.
1006 if (lookup.IsReadOnly()) {
1007 // If we found readonly property on one of hidden prototypes,
1008 // just shadow it.
1009 if (real_holder != Top::context()->global()) break;
1010 return ThrowRedeclarationError("const", name);
1011 }
1012
1013 // Determine if this is a redeclaration of an intercepted read-only
1014 // property and figure out if the property exists at all.
1015 bool found = true;
1016 PropertyType type = lookup.type();
1017 if (type == INTERCEPTOR) {
1018 HandleScope handle_scope;
1019 Handle<JSObject> holder(real_holder);
1020 PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
1021 real_holder = *holder;
1022 if (intercepted == ABSENT) {
1023 // The interceptor claims the property isn't there. We need to
1024 // make sure to introduce it.
1025 found = false;
1026 } else if ((intercepted & READ_ONLY) != 0) {
1027 // The property is present, but read-only. Since we're trying to
1028 // overwrite it with a variable declaration we must throw a
1029 // re-declaration error. However if we found readonly property
1030 // on one of hidden prototypes, just shadow it.
1031 if (real_holder != Top::context()->global()) break;
1032 return ThrowRedeclarationError("const", name);
1033 }
1034 }
1035
1036 if (found && !assign) {
1037 // The global property is there and we're not assigning any value
1038 // to it. Just return.
1039 return Heap::undefined_value();
1040 }
1041
1042 // Assign the value (or undefined) to the property.
1043 Object* value = (assign) ? args[1] : Heap::undefined_value();
1044 return real_holder->SetProperty(&lookup, *name, value, attributes);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001045 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001046
1047 Object* proto = real_holder->GetPrototype();
1048 if (!proto->IsJSObject())
1049 break;
1050
1051 if (!JSObject::cast(proto)->map()->is_hidden_prototype())
1052 break;
1053
1054 real_holder = JSObject::cast(proto);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001055 }
1056
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001057 global = Top::context()->global();
1058 if (assign) {
1059 return global->IgnoreAttributesAndSetLocalProperty(*name,
1060 args[1],
1061 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001062 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001063 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001064}
1065
1066
1067static Object* Runtime_InitializeConstGlobal(Arguments args) {
1068 // All constants are declared with an initial value. The name
1069 // of the constant is the first argument and the initial value
1070 // is the second.
1071 RUNTIME_ASSERT(args.length() == 2);
1072 CONVERT_ARG_CHECKED(String, name, 0);
1073 Handle<Object> value = args.at<Object>(1);
1074
1075 // Get the current global object from top.
1076 GlobalObject* global = Top::context()->global();
1077
1078 // According to ECMA-262, section 12.2, page 62, the property must
1079 // not be deletable. Since it's a const, it must be READ_ONLY too.
1080 PropertyAttributes attributes =
1081 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
1082
1083 // Lookup the property locally in the global object. If it isn't
1084 // there, we add the property and take special precautions to always
1085 // add it as a local property even in case of callbacks in the
1086 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001087 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001088 LookupResult lookup;
1089 global->LocalLookup(*name, &lookup);
1090 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001091 return global->IgnoreAttributesAndSetLocalProperty(*name,
1092 *value,
1093 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001094 }
1095
1096 // Determine if this is a redeclaration of something not
1097 // read-only. In case the result is hidden behind an interceptor we
1098 // need to ask it for the property attributes.
1099 if (!lookup.IsReadOnly()) {
1100 if (lookup.type() != INTERCEPTOR) {
1101 return ThrowRedeclarationError("var", name);
1102 }
1103
1104 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
1105
1106 // Throw re-declaration error if the intercepted property is present
1107 // but not read-only.
1108 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
1109 return ThrowRedeclarationError("var", name);
1110 }
1111
1112 // Restore global object from context (in case of GC) and continue
1113 // with setting the value because the property is either absent or
1114 // read-only. We also have to do redo the lookup.
1115 global = Top::context()->global();
1116
1117 // BUG 1213579: Handle the case where we have to set a read-only
1118 // property through an interceptor and only do it if it's
1119 // uninitialized, e.g. the hole. Nirk...
1120 global->SetProperty(*name, *value, attributes);
1121 return *value;
1122 }
1123
1124 // Set the value, but only we're assigning the initial value to a
1125 // constant. For now, we determine this by checking if the
1126 // current value is the hole.
1127 PropertyType type = lookup.type();
1128 if (type == FIELD) {
1129 FixedArray* properties = global->properties();
1130 int index = lookup.GetFieldIndex();
1131 if (properties->get(index)->IsTheHole()) {
1132 properties->set(index, *value);
1133 }
1134 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001135 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
1136 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001137 }
1138 } else {
1139 // Ignore re-initialization of constants that have already been
1140 // assigned a function value.
1141 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
1142 }
1143
1144 // Use the set value as the result of the operation.
1145 return *value;
1146}
1147
1148
1149static Object* Runtime_InitializeConstContextSlot(Arguments args) {
1150 HandleScope scope;
1151 ASSERT(args.length() == 3);
1152
1153 Handle<Object> value(args[0]);
1154 ASSERT(!value->IsTheHole());
1155 CONVERT_ARG_CHECKED(Context, context, 1);
1156 Handle<String> name(String::cast(args[2]));
1157
1158 // Initializations are always done in the function context.
1159 context = Handle<Context>(context->fcontext());
1160
1161 int index;
1162 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001163 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001164 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001165 context->Lookup(name, flags, &index, &attributes);
1166
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001167 // In most situations, the property introduced by the const
1168 // declaration should be present in the context extension object.
1169 // However, because declaration and initialization are separate, the
1170 // property might have been deleted (if it was introduced by eval)
1171 // before we reach the initialization point.
1172 //
1173 // Example:
1174 //
1175 // function f() { eval("delete x; const x;"); }
1176 //
1177 // In that case, the initialization behaves like a normal assignment
1178 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001179 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001180 // Property was found in a context.
1181 if (holder->IsContext()) {
1182 // The holder cannot be the function context. If it is, there
1183 // should have been a const redeclaration error when declaring
1184 // the const property.
1185 ASSERT(!holder.is_identical_to(context));
1186 if ((attributes & READ_ONLY) == 0) {
1187 Handle<Context>::cast(holder)->set(index, *value);
1188 }
1189 } else {
1190 // The holder is an arguments object.
1191 ASSERT((attributes & READ_ONLY) == 0);
1192 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001193 }
1194 return *value;
1195 }
1196
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001197 // The property could not be found, we introduce it in the global
1198 // context.
1199 if (attributes == ABSENT) {
1200 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1201 SetProperty(global, name, value, NONE);
1202 return *value;
1203 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001204
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001205 // The property was present in a context extension object.
1206 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001207
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001208 if (*context_ext == context->extension()) {
1209 // This is the property that was introduced by the const
1210 // declaration. Set it if it hasn't been set before. NOTE: We
1211 // cannot use GetProperty() to get the current value as it
1212 // 'unholes' the value.
1213 LookupResult lookup;
1214 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1215 ASSERT(lookup.IsProperty()); // the property was declared
1216 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1217
1218 PropertyType type = lookup.type();
1219 if (type == FIELD) {
1220 FixedArray* properties = context_ext->properties();
1221 int index = lookup.GetFieldIndex();
1222 if (properties->get(index)->IsTheHole()) {
1223 properties->set(index, *value);
1224 }
1225 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001226 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
1227 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001228 }
1229 } else {
1230 // We should not reach here. Any real, named property should be
1231 // either a field or a dictionary slot.
1232 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001233 }
1234 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001235 // The property was found in a different context extension object.
1236 // Set it if it is not a read-only property.
1237 if ((attributes & READ_ONLY) == 0) {
1238 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1239 // Setting a property might throw an exception. Exceptions
1240 // are converted to empty handles in handle operations. We
1241 // need to convert back to exceptions here.
1242 if (set.is_null()) {
1243 ASSERT(Top::has_pending_exception());
1244 return Failure::Exception();
1245 }
1246 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001247 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001248
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001249 return *value;
1250}
1251
1252
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001253static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1254 Arguments args) {
1255 HandleScope scope;
1256 ASSERT(args.length() == 2);
1257 CONVERT_ARG_CHECKED(JSObject, object, 0);
1258 CONVERT_SMI_CHECKED(properties, args[1]);
1259 if (object->HasFastProperties()) {
1260 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1261 }
1262 return *object;
1263}
1264
1265
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001266static Object* Runtime_RegExpExec(Arguments args) {
1267 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001268 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001269 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1270 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001271 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001272 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001273 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001274 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001275 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001276 RUNTIME_ASSERT(index >= 0);
1277 RUNTIME_ASSERT(index <= subject->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001278 Counters::regexp_entry_runtime.Increment();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001279 Handle<Object> result = RegExpImpl::Exec(regexp,
1280 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001281 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001282 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001283 if (result.is_null()) return Failure::Exception();
1284 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001285}
1286
1287
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00001288static Object* Runtime_RegExpConstructResult(Arguments args) {
1289 ASSERT(args.length() == 3);
1290 CONVERT_SMI_CHECKED(elements_count, args[0]);
1291 if (elements_count > JSArray::kMaxFastElementsLength) {
1292 return Top::ThrowIllegalOperation();
1293 }
1294 Object* new_object = Heap::AllocateFixedArrayWithHoles(elements_count);
1295 if (new_object->IsFailure()) return new_object;
1296 FixedArray* elements = FixedArray::cast(new_object);
1297 new_object = Heap::AllocateRaw(JSRegExpResult::kSize,
1298 NEW_SPACE,
1299 OLD_POINTER_SPACE);
1300 if (new_object->IsFailure()) return new_object;
1301 {
1302 AssertNoAllocation no_gc;
1303 HandleScope scope;
1304 reinterpret_cast<HeapObject*>(new_object)->
1305 set_map(Top::global_context()->regexp_result_map());
1306 }
1307 JSArray* array = JSArray::cast(new_object);
1308 array->set_properties(Heap::empty_fixed_array());
1309 array->set_elements(elements);
1310 array->set_length(Smi::FromInt(elements_count));
1311 // Write in-object properties after the length of the array.
1312 array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, args[1]);
1313 array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, args[2]);
1314 return array;
1315}
1316
1317
lrn@chromium.org25156de2010-04-06 13:10:27 +00001318static Object* Runtime_RegExpInitializeObject(Arguments args) {
1319 AssertNoAllocation no_alloc;
1320 ASSERT(args.length() == 5);
1321 CONVERT_CHECKED(JSRegExp, regexp, args[0]);
1322 CONVERT_CHECKED(String, source, args[1]);
1323
1324 Object* global = args[2];
1325 if (!global->IsTrue()) global = Heap::false_value();
1326
1327 Object* ignoreCase = args[3];
1328 if (!ignoreCase->IsTrue()) ignoreCase = Heap::false_value();
1329
1330 Object* multiline = args[4];
1331 if (!multiline->IsTrue()) multiline = Heap::false_value();
1332
1333 Map* map = regexp->map();
1334 Object* constructor = map->constructor();
1335 if (constructor->IsJSFunction() &&
1336 JSFunction::cast(constructor)->initial_map() == map) {
1337 // If we still have the original map, set in-object properties directly.
1338 regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, source);
1339 // TODO(lrn): Consider skipping write barrier on booleans as well.
1340 // Both true and false should be in oldspace at all times.
1341 regexp->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, global);
1342 regexp->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex, ignoreCase);
1343 regexp->InObjectPropertyAtPut(JSRegExp::kMultilineFieldIndex, multiline);
1344 regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex,
1345 Smi::FromInt(0),
1346 SKIP_WRITE_BARRIER);
1347 return regexp;
1348 }
1349
1350 // Map has changed, so use generic, but slower, method.
1351 PropertyAttributes final =
1352 static_cast<PropertyAttributes>(READ_ONLY | DONT_ENUM | DONT_DELETE);
1353 PropertyAttributes writable =
1354 static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
1355 regexp->IgnoreAttributesAndSetLocalProperty(Heap::source_symbol(),
1356 source,
1357 final);
1358 regexp->IgnoreAttributesAndSetLocalProperty(Heap::global_symbol(),
1359 global,
1360 final);
1361 regexp->IgnoreAttributesAndSetLocalProperty(Heap::ignore_case_symbol(),
1362 ignoreCase,
1363 final);
1364 regexp->IgnoreAttributesAndSetLocalProperty(Heap::multiline_symbol(),
1365 multiline,
1366 final);
1367 regexp->IgnoreAttributesAndSetLocalProperty(Heap::last_index_symbol(),
1368 Smi::FromInt(0),
1369 writable);
1370 return regexp;
1371}
1372
1373
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001374static Object* Runtime_FinishArrayPrototypeSetup(Arguments args) {
1375 HandleScope scope;
1376 ASSERT(args.length() == 1);
1377 CONVERT_ARG_CHECKED(JSArray, prototype, 0);
1378 // This is necessary to enable fast checks for absence of elements
1379 // on Array.prototype and below.
1380 prototype->set_elements(Heap::empty_fixed_array());
1381 return Smi::FromInt(0);
1382}
1383
1384
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001385static Handle<JSFunction> InstallBuiltin(Handle<JSObject> holder,
1386 const char* name,
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001387 Builtins::Name builtin_name) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001388 Handle<String> key = Factory::LookupAsciiSymbol(name);
1389 Handle<Code> code(Builtins::builtin(builtin_name));
1390 Handle<JSFunction> optimized = Factory::NewFunction(key,
1391 JS_OBJECT_TYPE,
1392 JSObject::kHeaderSize,
1393 code,
1394 false);
1395 optimized->shared()->DontAdaptArguments();
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001396 SetProperty(holder, key, optimized, NONE);
1397 return optimized;
1398}
1399
1400
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001401static Object* Runtime_SpecialArrayFunctions(Arguments args) {
1402 HandleScope scope;
1403 ASSERT(args.length() == 1);
1404 CONVERT_ARG_CHECKED(JSObject, holder, 0);
1405
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001406 InstallBuiltin(holder, "pop", Builtins::ArrayPop);
1407 InstallBuiltin(holder, "push", Builtins::ArrayPush);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001408 InstallBuiltin(holder, "shift", Builtins::ArrayShift);
1409 InstallBuiltin(holder, "unshift", Builtins::ArrayUnshift);
1410 InstallBuiltin(holder, "slice", Builtins::ArraySlice);
1411 InstallBuiltin(holder, "splice", Builtins::ArraySplice);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001412 InstallBuiltin(holder, "concat", Builtins::ArrayConcat);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001413
1414 return *holder;
1415}
1416
1417
ager@chromium.org357bf652010-04-12 11:30:10 +00001418static Object* Runtime_GetGlobalReceiver(Arguments args) {
1419 // Returns a real global receiver, not one of builtins object.
1420 Context* global_context = Top::context()->global()->global_context();
1421 return global_context->global()->global_receiver();
1422}
1423
1424
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001425static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1426 HandleScope scope;
1427 ASSERT(args.length() == 4);
1428 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1429 int index = Smi::cast(args[1])->value();
1430 Handle<String> pattern = args.at<String>(2);
1431 Handle<String> flags = args.at<String>(3);
1432
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001433 // Get the RegExp function from the context in the literals array.
1434 // This is the RegExp function from the context in which the
1435 // function was created. We do not use the RegExp function from the
1436 // current global context because this might be the RegExp function
1437 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001438 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001439 Handle<JSFunction>(
1440 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001441 // Compute the regular expression literal.
1442 bool has_pending_exception;
1443 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001444 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1445 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001446 if (has_pending_exception) {
1447 ASSERT(Top::has_pending_exception());
1448 return Failure::Exception();
1449 }
1450 literals->set(index, *regexp);
1451 return *regexp;
1452}
1453
1454
1455static Object* Runtime_FunctionGetName(Arguments args) {
1456 NoHandleAllocation ha;
1457 ASSERT(args.length() == 1);
1458
1459 CONVERT_CHECKED(JSFunction, f, args[0]);
1460 return f->shared()->name();
1461}
1462
1463
ager@chromium.org236ad962008-09-25 09:45:57 +00001464static Object* Runtime_FunctionSetName(Arguments args) {
1465 NoHandleAllocation ha;
1466 ASSERT(args.length() == 2);
1467
1468 CONVERT_CHECKED(JSFunction, f, args[0]);
1469 CONVERT_CHECKED(String, name, args[1]);
1470 f->shared()->set_name(name);
1471 return Heap::undefined_value();
1472}
1473
1474
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001475static Object* Runtime_FunctionRemovePrototype(Arguments args) {
1476 NoHandleAllocation ha;
1477 ASSERT(args.length() == 1);
1478
1479 CONVERT_CHECKED(JSFunction, f, args[0]);
1480 Object* obj = f->RemovePrototype();
1481 if (obj->IsFailure()) return obj;
1482
1483 return Heap::undefined_value();
1484}
1485
1486
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001487static Object* Runtime_FunctionGetScript(Arguments args) {
1488 HandleScope scope;
1489 ASSERT(args.length() == 1);
1490
1491 CONVERT_CHECKED(JSFunction, fun, args[0]);
1492 Handle<Object> script = Handle<Object>(fun->shared()->script());
1493 if (!script->IsScript()) return Heap::undefined_value();
1494
1495 return *GetScriptWrapper(Handle<Script>::cast(script));
1496}
1497
1498
1499static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1500 NoHandleAllocation ha;
1501 ASSERT(args.length() == 1);
1502
1503 CONVERT_CHECKED(JSFunction, f, args[0]);
1504 return f->shared()->GetSourceCode();
1505}
1506
1507
1508static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1509 NoHandleAllocation ha;
1510 ASSERT(args.length() == 1);
1511
1512 CONVERT_CHECKED(JSFunction, fun, args[0]);
1513 int pos = fun->shared()->start_position();
1514 return Smi::FromInt(pos);
1515}
1516
1517
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001518static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1519 ASSERT(args.length() == 2);
1520
1521 CONVERT_CHECKED(JSFunction, fun, args[0]);
1522 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1523
1524 Code* code = fun->code();
1525 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1526
1527 Address pc = code->address() + offset;
1528 return Smi::FromInt(fun->code()->SourcePosition(pc));
1529}
1530
1531
1532
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001533static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1534 NoHandleAllocation ha;
1535 ASSERT(args.length() == 2);
1536
1537 CONVERT_CHECKED(JSFunction, fun, args[0]);
1538 CONVERT_CHECKED(String, name, args[1]);
1539 fun->SetInstanceClassName(name);
1540 return Heap::undefined_value();
1541}
1542
1543
1544static Object* Runtime_FunctionSetLength(Arguments args) {
1545 NoHandleAllocation ha;
1546 ASSERT(args.length() == 2);
1547
1548 CONVERT_CHECKED(JSFunction, fun, args[0]);
1549 CONVERT_CHECKED(Smi, length, args[1]);
1550 fun->shared()->set_length(length->value());
1551 return length;
1552}
1553
1554
1555static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001556 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001557 ASSERT(args.length() == 2);
1558
1559 CONVERT_CHECKED(JSFunction, fun, args[0]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001560 ASSERT(fun->should_have_prototype());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001561 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1562 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001563 return args[0]; // return TOS
1564}
1565
1566
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001567static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1568 NoHandleAllocation ha;
1569 ASSERT(args.length() == 1);
1570
1571 CONVERT_CHECKED(JSFunction, f, args[0]);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001572 return f->shared()->IsApiFunction() ? Heap::true_value()
1573 : Heap::false_value();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001574}
1575
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001576static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1577 NoHandleAllocation ha;
1578 ASSERT(args.length() == 1);
1579
1580 CONVERT_CHECKED(JSFunction, f, args[0]);
1581 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1582}
1583
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001584
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001585static Object* Runtime_SetCode(Arguments args) {
1586 HandleScope scope;
1587 ASSERT(args.length() == 2);
1588
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001589 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001590 Handle<Object> code = args.at<Object>(1);
1591
1592 Handle<Context> context(target->context());
1593
1594 if (!code->IsNull()) {
1595 RUNTIME_ASSERT(code->IsJSFunction());
1596 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001597 Handle<SharedFunctionInfo> shared(fun->shared());
1598 SetExpectedNofProperties(target, shared->expected_nof_properties());
1599
1600 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001601 return Failure::Exception();
1602 }
1603 // Set the code, formal parameter count, and the length of the target
1604 // function.
1605 target->set_code(fun->code());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001606 target->shared()->set_length(shared->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001607 target->shared()->set_formal_parameter_count(
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001608 shared->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001609 // Set the source code of the target function to undefined.
1610 // SetCode is only used for built-in constructors like String,
1611 // Array, and Object, and some web code
1612 // doesn't like seeing source code for constructors.
1613 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001614 // Clear the optimization hints related to the compiled code as these are no
1615 // longer valid when the code is overwritten.
1616 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001617 context = Handle<Context>(fun->context());
1618
1619 // Make sure we get a fresh copy of the literal vector to avoid
1620 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001621 int number_of_literals = fun->NumberOfLiterals();
1622 Handle<FixedArray> literals =
1623 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001624 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001625 // Insert the object, regexp and array functions in the literals
1626 // array prefix. These are the functions that will be used when
1627 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001628 literals->set(JSFunction::kLiteralGlobalContextIndex,
1629 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001630 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001631 // It's okay to skip the write barrier here because the literals
1632 // are guaranteed to be in old space.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001633 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001634 }
1635
1636 target->set_context(*context);
1637 return *target;
1638}
1639
1640
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001641static Object* CharFromCode(Object* char_code) {
1642 uint32_t code;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001643 if (char_code->ToArrayIndex(&code)) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001644 if (code <= 0xffff) {
1645 return Heap::LookupSingleCharacterStringFromCode(code);
1646 }
1647 }
1648 return Heap::empty_string();
1649}
1650
1651
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001652static Object* Runtime_StringCharCodeAt(Arguments args) {
1653 NoHandleAllocation ha;
1654 ASSERT(args.length() == 2);
1655
1656 CONVERT_CHECKED(String, subject, args[0]);
1657 Object* index = args[1];
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001658 RUNTIME_ASSERT(index->IsNumber());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001659
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001660 uint32_t i = 0;
1661 if (index->IsSmi()) {
1662 int value = Smi::cast(index)->value();
1663 if (value < 0) return Heap::nan_value();
1664 i = value;
1665 } else {
1666 ASSERT(index->IsHeapNumber());
1667 double value = HeapNumber::cast(index)->value();
1668 i = static_cast<uint32_t>(DoubleToInteger(value));
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001669 }
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001670
1671 // Flatten the string. If someone wants to get a char at an index
1672 // in a cons string, it is likely that more indices will be
1673 // accessed.
1674 Object* flat = subject->TryFlatten();
1675 if (flat->IsFailure()) return flat;
1676 subject = String::cast(flat);
1677
1678 if (i >= static_cast<uint32_t>(subject->length())) {
1679 return Heap::nan_value();
1680 }
1681
1682 return Smi::FromInt(subject->Get(i));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001683}
1684
1685
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001686static Object* Runtime_CharFromCode(Arguments args) {
1687 NoHandleAllocation ha;
1688 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001689 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001690}
1691
lrn@chromium.org25156de2010-04-06 13:10:27 +00001692
1693class FixedArrayBuilder {
1694 public:
1695 explicit FixedArrayBuilder(int initial_capacity)
1696 : array_(Factory::NewFixedArrayWithHoles(initial_capacity)),
1697 length_(0) {
1698 // Require a non-zero initial size. Ensures that doubling the size to
1699 // extend the array will work.
1700 ASSERT(initial_capacity > 0);
1701 }
1702
1703 explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
1704 : array_(backing_store),
1705 length_(0) {
1706 // Require a non-zero initial size. Ensures that doubling the size to
1707 // extend the array will work.
1708 ASSERT(backing_store->length() > 0);
1709 }
1710
1711 bool HasCapacity(int elements) {
1712 int length = array_->length();
1713 int required_length = length_ + elements;
1714 return (length >= required_length);
1715 }
1716
1717 void EnsureCapacity(int elements) {
1718 int length = array_->length();
1719 int required_length = length_ + elements;
1720 if (length < required_length) {
1721 int new_length = length;
1722 do {
1723 new_length *= 2;
1724 } while (new_length < required_length);
1725 Handle<FixedArray> extended_array =
1726 Factory::NewFixedArrayWithHoles(new_length);
1727 array_->CopyTo(0, *extended_array, 0, length_);
1728 array_ = extended_array;
1729 }
1730 }
1731
1732 void Add(Object* value) {
1733 ASSERT(length_ < capacity());
1734 array_->set(length_, value);
1735 length_++;
1736 }
1737
1738 void Add(Smi* value) {
1739 ASSERT(length_ < capacity());
1740 array_->set(length_, value);
1741 length_++;
1742 }
1743
1744 Handle<FixedArray> array() {
1745 return array_;
1746 }
1747
1748 int length() {
1749 return length_;
1750 }
1751
1752 int capacity() {
1753 return array_->length();
1754 }
1755
1756 Handle<JSArray> ToJSArray() {
1757 Handle<JSArray> result_array = Factory::NewJSArrayWithElements(array_);
1758 result_array->set_length(Smi::FromInt(length_));
1759 return result_array;
1760 }
1761
1762 Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
1763 target_array->set_elements(*array_);
1764 target_array->set_length(Smi::FromInt(length_));
1765 return target_array;
1766 }
1767
1768 private:
1769 Handle<FixedArray> array_;
1770 int length_;
1771};
1772
1773
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001774// Forward declarations.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001775const int kStringBuilderConcatHelperLengthBits = 11;
1776const int kStringBuilderConcatHelperPositionBits = 19;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001777
1778template <typename schar>
1779static inline void StringBuilderConcatHelper(String*,
1780 schar*,
1781 FixedArray*,
1782 int);
1783
lrn@chromium.org25156de2010-04-06 13:10:27 +00001784typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
1785 StringBuilderSubstringLength;
1786typedef BitField<int,
1787 kStringBuilderConcatHelperLengthBits,
1788 kStringBuilderConcatHelperPositionBits>
1789 StringBuilderSubstringPosition;
1790
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001791
1792class ReplacementStringBuilder {
1793 public:
1794 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
lrn@chromium.org25156de2010-04-06 13:10:27 +00001795 : array_builder_(estimated_part_count),
1796 subject_(subject),
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001797 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001798 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001799 // Require a non-zero initial size. Ensures that doubling the size to
1800 // extend the array will work.
1801 ASSERT(estimated_part_count > 0);
1802 }
1803
lrn@chromium.org25156de2010-04-06 13:10:27 +00001804 static inline void AddSubjectSlice(FixedArrayBuilder* builder,
1805 int from,
1806 int to) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001807 ASSERT(from >= 0);
1808 int length = to - from;
1809 ASSERT(length > 0);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001810 if (StringBuilderSubstringLength::is_valid(length) &&
1811 StringBuilderSubstringPosition::is_valid(from)) {
1812 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1813 StringBuilderSubstringPosition::encode(from);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001814 builder->Add(Smi::FromInt(encoded_slice));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001815 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001816 // Otherwise encode as two smis.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001817 builder->Add(Smi::FromInt(-length));
1818 builder->Add(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001819 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00001820 }
1821
1822
1823 void EnsureCapacity(int elements) {
1824 array_builder_.EnsureCapacity(elements);
1825 }
1826
1827
1828 void AddSubjectSlice(int from, int to) {
1829 AddSubjectSlice(&array_builder_, from, to);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001830 IncrementCharacterCount(to - from);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001831 }
1832
1833
1834 void AddString(Handle<String> string) {
1835 int length = string->length();
1836 ASSERT(length > 0);
1837 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001838 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001839 is_ascii_ = false;
1840 }
1841 IncrementCharacterCount(length);
1842 }
1843
1844
1845 Handle<String> ToString() {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001846 if (array_builder_.length() == 0) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001847 return Factory::empty_string();
1848 }
1849
1850 Handle<String> joined_string;
1851 if (is_ascii_) {
1852 joined_string = NewRawAsciiString(character_count_);
1853 AssertNoAllocation no_alloc;
1854 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1855 char* char_buffer = seq->GetChars();
1856 StringBuilderConcatHelper(*subject_,
1857 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001858 *array_builder_.array(),
1859 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001860 } else {
1861 // Non-ASCII.
1862 joined_string = NewRawTwoByteString(character_count_);
1863 AssertNoAllocation no_alloc;
1864 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1865 uc16* char_buffer = seq->GetChars();
1866 StringBuilderConcatHelper(*subject_,
1867 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001868 *array_builder_.array(),
1869 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001870 }
1871 return joined_string;
1872 }
1873
1874
1875 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001876 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001877 V8::FatalProcessOutOfMemory("String.replace result too large.");
1878 }
1879 character_count_ += by;
1880 }
1881
lrn@chromium.org25156de2010-04-06 13:10:27 +00001882 Handle<JSArray> GetParts() {
1883 Handle<JSArray> result =
1884 Factory::NewJSArrayWithElements(array_builder_.array());
1885 result->set_length(Smi::FromInt(array_builder_.length()));
1886 return result;
1887 }
kmillikin@chromium.orgd9825192010-03-30 08:36:16 +00001888
lrn@chromium.org25156de2010-04-06 13:10:27 +00001889 private:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001890 Handle<String> NewRawAsciiString(int size) {
1891 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1892 }
1893
1894
1895 Handle<String> NewRawTwoByteString(int size) {
1896 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1897 }
1898
1899
1900 void AddElement(Object* element) {
1901 ASSERT(element->IsSmi() || element->IsString());
lrn@chromium.org25156de2010-04-06 13:10:27 +00001902 ASSERT(array_builder_.capacity() > array_builder_.length());
1903 array_builder_.Add(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001904 }
1905
lrn@chromium.org25156de2010-04-06 13:10:27 +00001906 FixedArrayBuilder array_builder_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001907 Handle<String> subject_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001908 int character_count_;
1909 bool is_ascii_;
1910};
1911
1912
1913class CompiledReplacement {
1914 public:
1915 CompiledReplacement()
1916 : parts_(1), replacement_substrings_(0) {}
1917
1918 void Compile(Handle<String> replacement,
1919 int capture_count,
1920 int subject_length);
1921
1922 void Apply(ReplacementStringBuilder* builder,
1923 int match_from,
1924 int match_to,
1925 Handle<JSArray> last_match_info);
1926
1927 // Number of distinct parts of the replacement pattern.
1928 int parts() {
1929 return parts_.length();
1930 }
1931 private:
1932 enum PartType {
1933 SUBJECT_PREFIX = 1,
1934 SUBJECT_SUFFIX,
1935 SUBJECT_CAPTURE,
1936 REPLACEMENT_SUBSTRING,
1937 REPLACEMENT_STRING,
1938
1939 NUMBER_OF_PART_TYPES
1940 };
1941
1942 struct ReplacementPart {
1943 static inline ReplacementPart SubjectMatch() {
1944 return ReplacementPart(SUBJECT_CAPTURE, 0);
1945 }
1946 static inline ReplacementPart SubjectCapture(int capture_index) {
1947 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1948 }
1949 static inline ReplacementPart SubjectPrefix() {
1950 return ReplacementPart(SUBJECT_PREFIX, 0);
1951 }
1952 static inline ReplacementPart SubjectSuffix(int subject_length) {
1953 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1954 }
1955 static inline ReplacementPart ReplacementString() {
1956 return ReplacementPart(REPLACEMENT_STRING, 0);
1957 }
1958 static inline ReplacementPart ReplacementSubString(int from, int to) {
1959 ASSERT(from >= 0);
1960 ASSERT(to > from);
1961 return ReplacementPart(-from, to);
1962 }
1963
1964 // If tag <= 0 then it is the negation of a start index of a substring of
1965 // the replacement pattern, otherwise it's a value from PartType.
1966 ReplacementPart(int tag, int data)
1967 : tag(tag), data(data) {
1968 // Must be non-positive or a PartType value.
1969 ASSERT(tag < NUMBER_OF_PART_TYPES);
1970 }
1971 // Either a value of PartType or a non-positive number that is
1972 // the negation of an index into the replacement string.
1973 int tag;
1974 // The data value's interpretation depends on the value of tag:
1975 // tag == SUBJECT_PREFIX ||
1976 // tag == SUBJECT_SUFFIX: data is unused.
1977 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1978 // tag == REPLACEMENT_SUBSTRING ||
1979 // tag == REPLACEMENT_STRING: data is index into array of substrings
1980 // of the replacement string.
1981 // tag <= 0: Temporary representation of the substring of the replacement
1982 // string ranging over -tag .. data.
1983 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1984 // substring objects.
1985 int data;
1986 };
1987
1988 template<typename Char>
1989 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1990 Vector<Char> characters,
1991 int capture_count,
1992 int subject_length) {
1993 int length = characters.length();
1994 int last = 0;
1995 for (int i = 0; i < length; i++) {
1996 Char c = characters[i];
1997 if (c == '$') {
1998 int next_index = i + 1;
1999 if (next_index == length) { // No next character!
2000 break;
2001 }
2002 Char c2 = characters[next_index];
2003 switch (c2) {
2004 case '$':
2005 if (i > last) {
2006 // There is a substring before. Include the first "$".
2007 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
2008 last = next_index + 1; // Continue after the second "$".
2009 } else {
2010 // Let the next substring start with the second "$".
2011 last = next_index;
2012 }
2013 i = next_index;
2014 break;
2015 case '`':
2016 if (i > last) {
2017 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2018 }
2019 parts->Add(ReplacementPart::SubjectPrefix());
2020 i = next_index;
2021 last = i + 1;
2022 break;
2023 case '\'':
2024 if (i > last) {
2025 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2026 }
2027 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
2028 i = next_index;
2029 last = i + 1;
2030 break;
2031 case '&':
2032 if (i > last) {
2033 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2034 }
2035 parts->Add(ReplacementPart::SubjectMatch());
2036 i = next_index;
2037 last = i + 1;
2038 break;
2039 case '0':
2040 case '1':
2041 case '2':
2042 case '3':
2043 case '4':
2044 case '5':
2045 case '6':
2046 case '7':
2047 case '8':
2048 case '9': {
2049 int capture_ref = c2 - '0';
2050 if (capture_ref > capture_count) {
2051 i = next_index;
2052 continue;
2053 }
2054 int second_digit_index = next_index + 1;
2055 if (second_digit_index < length) {
2056 // Peek ahead to see if we have two digits.
2057 Char c3 = characters[second_digit_index];
2058 if ('0' <= c3 && c3 <= '9') { // Double digits.
2059 int double_digit_ref = capture_ref * 10 + c3 - '0';
2060 if (double_digit_ref <= capture_count) {
2061 next_index = second_digit_index;
2062 capture_ref = double_digit_ref;
2063 }
2064 }
2065 }
2066 if (capture_ref > 0) {
2067 if (i > last) {
2068 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2069 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002070 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002071 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
2072 last = next_index + 1;
2073 }
2074 i = next_index;
2075 break;
2076 }
2077 default:
2078 i = next_index;
2079 break;
2080 }
2081 }
2082 }
2083 if (length > last) {
2084 if (last == 0) {
2085 parts->Add(ReplacementPart::ReplacementString());
2086 } else {
2087 parts->Add(ReplacementPart::ReplacementSubString(last, length));
2088 }
2089 }
2090 }
2091
2092 ZoneList<ReplacementPart> parts_;
2093 ZoneList<Handle<String> > replacement_substrings_;
2094};
2095
2096
2097void CompiledReplacement::Compile(Handle<String> replacement,
2098 int capture_count,
2099 int subject_length) {
2100 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00002101 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002102 AssertNoAllocation no_alloc;
2103 ParseReplacementPattern(&parts_,
2104 replacement->ToAsciiVector(),
2105 capture_count,
2106 subject_length);
2107 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00002108 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002109 AssertNoAllocation no_alloc;
2110
2111 ParseReplacementPattern(&parts_,
2112 replacement->ToUC16Vector(),
2113 capture_count,
2114 subject_length);
2115 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002116 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002117 int substring_index = 0;
2118 for (int i = 0, n = parts_.length(); i < n; i++) {
2119 int tag = parts_[i].tag;
2120 if (tag <= 0) { // A replacement string slice.
2121 int from = -tag;
2122 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002123 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002124 parts_[i].tag = REPLACEMENT_SUBSTRING;
2125 parts_[i].data = substring_index;
2126 substring_index++;
2127 } else if (tag == REPLACEMENT_STRING) {
2128 replacement_substrings_.Add(replacement);
2129 parts_[i].data = substring_index;
2130 substring_index++;
2131 }
2132 }
2133}
2134
2135
2136void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
2137 int match_from,
2138 int match_to,
2139 Handle<JSArray> last_match_info) {
2140 for (int i = 0, n = parts_.length(); i < n; i++) {
2141 ReplacementPart part = parts_[i];
2142 switch (part.tag) {
2143 case SUBJECT_PREFIX:
2144 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
2145 break;
2146 case SUBJECT_SUFFIX: {
2147 int subject_length = part.data;
2148 if (match_to < subject_length) {
2149 builder->AddSubjectSlice(match_to, subject_length);
2150 }
2151 break;
2152 }
2153 case SUBJECT_CAPTURE: {
2154 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002155 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002156 int from = RegExpImpl::GetCapture(match_info, capture * 2);
2157 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
2158 if (from >= 0 && to > from) {
2159 builder->AddSubjectSlice(from, to);
2160 }
2161 break;
2162 }
2163 case REPLACEMENT_SUBSTRING:
2164 case REPLACEMENT_STRING:
2165 builder->AddString(replacement_substrings_[part.data]);
2166 break;
2167 default:
2168 UNREACHABLE();
2169 }
2170 }
2171}
2172
2173
2174
2175static Object* StringReplaceRegExpWithString(String* subject,
2176 JSRegExp* regexp,
2177 String* replacement,
2178 JSArray* last_match_info) {
2179 ASSERT(subject->IsFlat());
2180 ASSERT(replacement->IsFlat());
2181
2182 HandleScope handles;
2183
2184 int length = subject->length();
2185 Handle<String> subject_handle(subject);
2186 Handle<JSRegExp> regexp_handle(regexp);
2187 Handle<String> replacement_handle(replacement);
2188 Handle<JSArray> last_match_info_handle(last_match_info);
2189 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2190 subject_handle,
2191 0,
2192 last_match_info_handle);
2193 if (match.is_null()) {
2194 return Failure::Exception();
2195 }
2196 if (match->IsNull()) {
2197 return *subject_handle;
2198 }
2199
2200 int capture_count = regexp_handle->CaptureCount();
2201
2202 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002203 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002204 CompiledReplacement compiled_replacement;
2205 compiled_replacement.Compile(replacement_handle,
2206 capture_count,
2207 length);
2208
2209 bool is_global = regexp_handle->GetFlags().is_global();
2210
2211 // Guessing the number of parts that the final result string is built
2212 // from. Global regexps can match any number of times, so we guess
2213 // conservatively.
2214 int expected_parts =
2215 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
2216 ReplacementStringBuilder builder(subject_handle, expected_parts);
2217
2218 // Index of end of last match.
2219 int prev = 0;
2220
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002221 // Number of parts added by compiled replacement plus preceeding
2222 // string and possibly suffix after last match. It is possible for
2223 // all components to use two elements when encoded as two smis.
2224 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002225 bool matched = true;
2226 do {
2227 ASSERT(last_match_info_handle->HasFastElements());
2228 // Increase the capacity of the builder before entering local handle-scope,
2229 // so its internal buffer can safely allocate a new handle if it grows.
2230 builder.EnsureCapacity(parts_added_per_loop);
2231
2232 HandleScope loop_scope;
2233 int start, end;
2234 {
2235 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002236 FixedArray* match_info_array =
2237 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002238
2239 ASSERT_EQ(capture_count * 2 + 2,
2240 RegExpImpl::GetLastCaptureCount(match_info_array));
2241 start = RegExpImpl::GetCapture(match_info_array, 0);
2242 end = RegExpImpl::GetCapture(match_info_array, 1);
2243 }
2244
2245 if (prev < start) {
2246 builder.AddSubjectSlice(prev, start);
2247 }
2248 compiled_replacement.Apply(&builder,
2249 start,
2250 end,
2251 last_match_info_handle);
2252 prev = end;
2253
2254 // Only continue checking for global regexps.
2255 if (!is_global) break;
2256
2257 // Continue from where the match ended, unless it was an empty match.
2258 int next = end;
2259 if (start == end) {
2260 next = end + 1;
2261 if (next > length) break;
2262 }
2263
2264 match = RegExpImpl::Exec(regexp_handle,
2265 subject_handle,
2266 next,
2267 last_match_info_handle);
2268 if (match.is_null()) {
2269 return Failure::Exception();
2270 }
2271 matched = !match->IsNull();
2272 } while (matched);
2273
2274 if (prev < length) {
2275 builder.AddSubjectSlice(prev, length);
2276 }
2277
2278 return *(builder.ToString());
2279}
2280
2281
2282static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2283 ASSERT(args.length() == 4);
2284
2285 CONVERT_CHECKED(String, subject, args[0]);
2286 if (!subject->IsFlat()) {
2287 Object* flat_subject = subject->TryFlatten();
2288 if (flat_subject->IsFailure()) {
2289 return flat_subject;
2290 }
2291 subject = String::cast(flat_subject);
2292 }
2293
2294 CONVERT_CHECKED(String, replacement, args[2]);
2295 if (!replacement->IsFlat()) {
2296 Object* flat_replacement = replacement->TryFlatten();
2297 if (flat_replacement->IsFailure()) {
2298 return flat_replacement;
2299 }
2300 replacement = String::cast(flat_replacement);
2301 }
2302
2303 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2304 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2305
2306 ASSERT(last_match_info->HasFastElements());
2307
2308 return StringReplaceRegExpWithString(subject,
2309 regexp,
2310 replacement,
2311 last_match_info);
2312}
2313
2314
ager@chromium.org7c537e22008-10-16 08:43:32 +00002315// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2316// limit, we can fix the size of tables.
2317static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002318// Reduce alphabet to this size.
2319static const int kBMAlphabetSize = 0x100;
2320// For patterns below this length, the skip length of Boyer-Moore is too short
2321// to compensate for the algorithmic overhead compared to simple brute force.
2322static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002323
ager@chromium.org7c537e22008-10-16 08:43:32 +00002324// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2325// shift. Only allows the last kBMMaxShift characters of the needle
2326// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002327class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002328 public:
2329 BMGoodSuffixBuffers() {}
2330 inline void init(int needle_length) {
2331 ASSERT(needle_length > 1);
2332 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2333 int len = needle_length - start;
2334 biased_suffixes_ = suffixes_ - start;
2335 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2336 for (int i = 0; i <= len; i++) {
2337 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002338 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002339 }
2340 inline int& suffix(int index) {
2341 ASSERT(biased_suffixes_ + index >= suffixes_);
2342 return biased_suffixes_[index];
2343 }
2344 inline int& shift(int index) {
2345 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2346 return biased_good_suffix_shift_[index];
2347 }
2348 private:
2349 int suffixes_[kBMMaxShift + 1];
2350 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002351 int* biased_suffixes_;
2352 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002353 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2354};
2355
2356// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002357static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002358static BMGoodSuffixBuffers bmgs_buffers;
2359
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002360// State of the string match tables.
2361// SIMPLE: No usable content in the buffers.
2362// BOYER_MOORE_HORSPOOL: The bad_char_occurences table has been populated.
2363// BOYER_MOORE: The bmgs_buffers tables have also been populated.
2364// Whenever starting with a new needle, one should call InitializeStringSearch
2365// to determine which search strategy to use, and in the case of a long-needle
2366// strategy, the call also initializes the algorithm to SIMPLE.
2367enum StringSearchAlgorithm { SIMPLE_SEARCH, BOYER_MOORE_HORSPOOL, BOYER_MOORE };
2368static StringSearchAlgorithm algorithm;
2369
2370
ager@chromium.org7c537e22008-10-16 08:43:32 +00002371// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002372template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002373static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern) {
2374 // Only preprocess at most kBMMaxShift last characters of pattern.
2375 int start = pattern.length() < kBMMaxShift ? 0
2376 : pattern.length() - kBMMaxShift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002377 // Run forwards to populate bad_char_table, so that *last* instance
2378 // of character equivalence class is the one registered.
2379 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002380 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2381 : kBMAlphabetSize;
2382 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002383 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002384 } else {
2385 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002386 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002387 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002388 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002389 for (int i = start; i < pattern.length() - 1; i++) {
2390 pchar c = pattern[i];
2391 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002392 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002393 }
2394}
2395
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002396
ager@chromium.org7c537e22008-10-16 08:43:32 +00002397template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002398static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002399 int m = pattern.length();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002400 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002401 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002402 // Compute Good Suffix tables.
2403 bmgs_buffers.init(m);
2404
2405 bmgs_buffers.shift(m-1) = 1;
2406 bmgs_buffers.suffix(m) = m + 1;
2407 pchar last_char = pattern[m - 1];
2408 int suffix = m + 1;
2409 for (int i = m; i > start;) {
2410 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2411 if (bmgs_buffers.shift(suffix) == len) {
2412 bmgs_buffers.shift(suffix) = suffix - i;
2413 }
2414 suffix = bmgs_buffers.suffix(suffix);
2415 }
2416 i--;
2417 suffix--;
2418 bmgs_buffers.suffix(i) = suffix;
2419 if (suffix == m) {
2420 // No suffix to extend, so we check against last_char only.
2421 while (i > start && pattern[i - 1] != last_char) {
2422 if (bmgs_buffers.shift(m) == len) {
2423 bmgs_buffers.shift(m) = m - i;
2424 }
2425 i--;
2426 bmgs_buffers.suffix(i) = m;
2427 }
2428 if (i > start) {
2429 i--;
2430 suffix--;
2431 bmgs_buffers.suffix(i) = suffix;
2432 }
2433 }
2434 }
2435 if (suffix < m) {
2436 for (int i = start; i <= m; i++) {
2437 if (bmgs_buffers.shift(i) == len) {
2438 bmgs_buffers.shift(i) = suffix - start;
2439 }
2440 if (i == suffix) {
2441 suffix = bmgs_buffers.suffix(suffix);
2442 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002443 }
2444 }
2445}
2446
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002447
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002448template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002449static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002450 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002451 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002452 }
2453 if (sizeof(pchar) == 1) {
2454 if (char_code > String::kMaxAsciiCharCode) {
2455 return -1;
2456 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002457 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002458 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002459 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002460}
2461
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002462
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002463// Restricted simplified Boyer-Moore string matching.
2464// Uses only the bad-shift table of Boyer-Moore and only uses it
2465// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002466template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002467static int BoyerMooreHorspool(Vector<const schar> subject,
2468 Vector<const pchar> pattern,
2469 int start_index,
2470 bool* complete) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002471 ASSERT(algorithm <= BOYER_MOORE_HORSPOOL);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002472 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002473 int m = pattern.length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002474
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002475 int badness = -m;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002476
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002477 // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002478 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002479 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002480 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002481 // Perform search
2482 for (idx = start_index; idx <= n - m;) {
2483 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002484 int c;
2485 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002486 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002487 int shift = j - bc_occ;
2488 idx += shift;
2489 badness += 1 - shift; // at most zero, so badness cannot increase.
2490 if (idx > n - m) {
2491 *complete = true;
2492 return -1;
2493 }
2494 }
2495 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002496 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002497 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002498 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002499 return idx;
2500 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002501 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002502 // Badness increases by the number of characters we have
2503 // checked, and decreases by the number of characters we
2504 // can skip by shifting. It's a measure of how we are doing
2505 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002506 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002507 if (badness > 0) {
2508 *complete = false;
2509 return idx;
2510 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002511 }
2512 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002513 *complete = true;
2514 return -1;
2515}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002516
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002517
2518template <typename schar, typename pchar>
2519static int BoyerMooreIndexOf(Vector<const schar> subject,
2520 Vector<const pchar> pattern,
2521 int idx) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002522 ASSERT(algorithm <= BOYER_MOORE);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002523 int n = subject.length();
2524 int m = pattern.length();
2525 // Only preprocess at most kBMMaxShift last characters of pattern.
2526 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2527
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002528 pchar last_char = pattern[m - 1];
2529 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002530 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002531 int j = m - 1;
2532 schar c;
2533 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002534 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002535 idx += shift;
2536 if (idx > n - m) {
2537 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002538 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002539 }
2540 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2541 if (j < 0) {
2542 return idx;
2543 } else if (j < start) {
2544 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002545 // Fall back on BMH shift.
2546 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002547 } else {
2548 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002549 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002550 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002551 if (gs_shift > shift) {
2552 shift = gs_shift;
2553 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002554 idx += shift;
2555 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002556 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002557
2558 return -1;
2559}
2560
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002561
2562template <typename schar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002563static inline int SingleCharIndexOf(Vector<const schar> string,
2564 schar pattern_char,
2565 int start_index) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002566 if (sizeof(schar) == 1) {
2567 const schar* pos = reinterpret_cast<const schar*>(
2568 memchr(string.start() + start_index,
2569 pattern_char,
2570 string.length() - start_index));
2571 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002572 return static_cast<int>(pos - string.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002573 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002574 for (int i = start_index, n = string.length(); i < n; i++) {
2575 if (pattern_char == string[i]) {
2576 return i;
2577 }
2578 }
2579 return -1;
2580}
2581
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002582
2583template <typename schar>
2584static int SingleCharLastIndexOf(Vector<const schar> string,
2585 schar pattern_char,
2586 int start_index) {
2587 for (int i = start_index; i >= 0; i--) {
2588 if (pattern_char == string[i]) {
2589 return i;
2590 }
2591 }
2592 return -1;
2593}
2594
2595
ager@chromium.org7c537e22008-10-16 08:43:32 +00002596// Trivial string search for shorter strings.
2597// On return, if "complete" is set to true, the return value is the
2598// final result of searching for the patter in the subject.
2599// If "complete" is set to false, the return value is the index where
2600// further checking should start, i.e., it's guaranteed that the pattern
2601// does not occur at a position prior to the returned index.
2602template <typename pchar, typename schar>
2603static int SimpleIndexOf(Vector<const schar> subject,
2604 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002605 int idx,
2606 bool* complete) {
2607 // Badness is a count of how much work we have done. When we have
2608 // done enough work we decide it's probably worth switching to a better
2609 // algorithm.
2610 int badness = -10 - (pattern.length() << 2);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002611
ager@chromium.org7c537e22008-10-16 08:43:32 +00002612 // We know our pattern is at least 2 characters, we cache the first so
2613 // the common case of the first character not matching is faster.
2614 pchar pattern_first_char = pattern[0];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002615 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2616 badness++;
2617 if (badness > 0) {
2618 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002619 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002620 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002621 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2622 const schar* pos = reinterpret_cast<const schar*>(
2623 memchr(subject.start() + i,
2624 pattern_first_char,
2625 n - i + 1));
2626 if (pos == NULL) {
2627 *complete = true;
2628 return -1;
2629 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002630 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002631 } else {
2632 if (subject[i] != pattern_first_char) continue;
2633 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002634 int j = 1;
2635 do {
2636 if (pattern[j] != subject[i+j]) {
2637 break;
2638 }
2639 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002640 } while (j < pattern.length());
2641 if (j == pattern.length()) {
2642 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002643 return i;
2644 }
2645 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002646 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002647 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002648 return -1;
2649}
2650
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002651// Simple indexOf that never bails out. For short patterns only.
2652template <typename pchar, typename schar>
2653static int SimpleIndexOf(Vector<const schar> subject,
2654 Vector<const pchar> pattern,
2655 int idx) {
2656 pchar pattern_first_char = pattern[0];
2657 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002658 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2659 const schar* pos = reinterpret_cast<const schar*>(
2660 memchr(subject.start() + i,
2661 pattern_first_char,
2662 n - i + 1));
2663 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002664 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002665 } else {
2666 if (subject[i] != pattern_first_char) continue;
2667 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002668 int j = 1;
2669 do {
2670 if (pattern[j] != subject[i+j]) {
2671 break;
2672 }
2673 j++;
2674 } while (j < pattern.length());
2675 if (j == pattern.length()) {
2676 return i;
2677 }
2678 }
2679 return -1;
2680}
2681
2682
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002683// Strategy for searching for a string in another string.
2684enum StringSearchStrategy { SEARCH_FAIL, SEARCH_SHORT, SEARCH_LONG };
ager@chromium.org7c537e22008-10-16 08:43:32 +00002685
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002686
2687template <typename pchar>
2688static inline StringSearchStrategy InitializeStringSearch(
2689 Vector<const pchar> pat, bool ascii_subject) {
2690 ASSERT(pat.length() > 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002691 // We have an ASCII haystack and a non-ASCII needle. Check if there
2692 // really is a non-ASCII character in the needle and bail out if there
2693 // is.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002694 if (ascii_subject && sizeof(pchar) > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002695 for (int i = 0; i < pat.length(); i++) {
2696 uc16 c = pat[i];
2697 if (c > String::kMaxAsciiCharCode) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002698 return SEARCH_FAIL;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002699 }
2700 }
2701 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002702 if (pat.length() < kBMMinPatternLength) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002703 return SEARCH_SHORT;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002704 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002705 algorithm = SIMPLE_SEARCH;
2706 return SEARCH_LONG;
2707}
2708
2709
2710// Dispatch long needle searches to different algorithms.
2711template <typename schar, typename pchar>
2712static int ComplexIndexOf(Vector<const schar> sub,
2713 Vector<const pchar> pat,
2714 int start_index) {
2715 ASSERT(pat.length() >= kBMMinPatternLength);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002716 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002717 bool complete;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002718 int idx = start_index;
2719 switch (algorithm) {
2720 case SIMPLE_SEARCH:
2721 idx = SimpleIndexOf(sub, pat, idx, &complete);
2722 if (complete) return idx;
2723 BoyerMoorePopulateBadCharTable(pat);
2724 algorithm = BOYER_MOORE_HORSPOOL;
2725 // FALLTHROUGH.
2726 case BOYER_MOORE_HORSPOOL:
2727 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
2728 if (complete) return idx;
2729 // Build the Good Suffix table and continue searching.
2730 BoyerMoorePopulateGoodSuffixTable(pat);
2731 algorithm = BOYER_MOORE;
2732 // FALLTHROUGH.
2733 case BOYER_MOORE:
2734 return BoyerMooreIndexOf(sub, pat, idx);
2735 }
2736 UNREACHABLE();
2737 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002738}
2739
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002740
2741// Dispatch to different search strategies for a single search.
2742// If searching multiple times on the same needle, the search
2743// strategy should only be computed once and then dispatch to different
2744// loops.
2745template <typename schar, typename pchar>
2746static int StringSearch(Vector<const schar> sub,
2747 Vector<const pchar> pat,
2748 int start_index) {
2749 bool ascii_subject = (sizeof(schar) == 1);
2750 StringSearchStrategy strategy = InitializeStringSearch(pat, ascii_subject);
2751 switch (strategy) {
2752 case SEARCH_FAIL: return -1;
2753 case SEARCH_SHORT: return SimpleIndexOf(sub, pat, start_index);
2754 case SEARCH_LONG: return ComplexIndexOf(sub, pat, start_index);
2755 }
2756 UNREACHABLE();
2757 return -1;
2758}
2759
2760
ager@chromium.org7c537e22008-10-16 08:43:32 +00002761// Perform string match of pattern on subject, starting at start index.
2762// Caller must ensure that 0 <= start_index <= sub->length(),
2763// and should check that pat->length() + start_index <= sub->length()
2764int Runtime::StringMatch(Handle<String> sub,
2765 Handle<String> pat,
2766 int start_index) {
2767 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002768 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002769
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002770 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002771 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002772
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002773 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002774 if (start_index + pattern_length > subject_length) return -1;
2775
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002776 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002777 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002778 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002779
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002780 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002781 // character patterns linear search is necessary, so any smart
2782 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002783 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002784 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002785 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002786 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002787 if (pchar > String::kMaxAsciiCharCode) {
2788 return -1;
2789 }
2790 Vector<const char> ascii_vector =
2791 sub->ToAsciiVector().SubVector(start_index, subject_length);
2792 const void* pos = memchr(ascii_vector.start(),
2793 static_cast<const char>(pchar),
2794 static_cast<size_t>(ascii_vector.length()));
2795 if (pos == NULL) {
2796 return -1;
2797 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002798 return static_cast<int>(reinterpret_cast<const char*>(pos)
2799 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002800 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002801 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002802 }
2803
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002804 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002805 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002806 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002807
ager@chromium.org7c537e22008-10-16 08:43:32 +00002808 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2809 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002810 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002811 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002812 if (sub->IsAsciiRepresentation()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002813 return StringSearch(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002814 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002815 return StringSearch(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002816 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002817 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002818 if (sub->IsAsciiRepresentation()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002819 return StringSearch(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002820 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002821 return StringSearch(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002822}
2823
2824
2825static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002826 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002827 ASSERT(args.length() == 3);
2828
ager@chromium.org7c537e22008-10-16 08:43:32 +00002829 CONVERT_ARG_CHECKED(String, sub, 0);
2830 CONVERT_ARG_CHECKED(String, pat, 1);
2831
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002832 Object* index = args[2];
2833 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002834 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002835
ager@chromium.org870a0b62008-11-04 11:43:05 +00002836 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002837 int position = Runtime::StringMatch(sub, pat, start_index);
2838 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002839}
2840
2841
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002842template <typename schar, typename pchar>
2843static int StringMatchBackwards(Vector<const schar> sub,
2844 Vector<const pchar> pat,
2845 int idx) {
2846 ASSERT(pat.length() >= 1);
2847 ASSERT(idx + pat.length() <= sub.length());
2848
2849 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
2850 for (int i = 0; i < pat.length(); i++) {
2851 uc16 c = pat[i];
2852 if (c > String::kMaxAsciiCharCode) {
2853 return -1;
2854 }
2855 }
2856 }
2857
2858 pchar pattern_first_char = pat[0];
2859 for (int i = idx; i >= 0; i--) {
2860 if (sub[i] != pattern_first_char) continue;
2861 int j = 1;
2862 while (j < pat.length()) {
2863 if (pat[j] != sub[i+j]) {
2864 break;
2865 }
2866 j++;
2867 }
2868 if (j == pat.length()) {
2869 return i;
2870 }
2871 }
2872 return -1;
2873}
2874
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002875static Object* Runtime_StringLastIndexOf(Arguments args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002876 HandleScope scope; // create a new handle scope
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002877 ASSERT(args.length() == 3);
2878
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002879 CONVERT_ARG_CHECKED(String, sub, 0);
2880 CONVERT_ARG_CHECKED(String, pat, 1);
2881
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002882 Object* index = args[2];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002883 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002884 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002885
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002886 uint32_t pat_length = pat->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002887 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002888
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002889 if (start_index + pat_length > sub_length) {
2890 start_index = sub_length - pat_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002891 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002892
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002893 if (pat_length == 0) {
2894 return Smi::FromInt(start_index);
2895 }
2896
2897 if (!sub->IsFlat()) {
2898 FlattenString(sub);
2899 }
2900
2901 if (pat_length == 1) {
2902 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2903 if (sub->IsAsciiRepresentation()) {
2904 uc16 pchar = pat->Get(0);
2905 if (pchar > String::kMaxAsciiCharCode) {
2906 return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002907 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002908 return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
2909 static_cast<char>(pat->Get(0)),
2910 start_index));
2911 } else {
2912 return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
2913 pat->Get(0),
2914 start_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002915 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002916 }
2917
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002918 if (!pat->IsFlat()) {
2919 FlattenString(pat);
2920 }
2921
2922 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2923
2924 int position = -1;
2925
2926 if (pat->IsAsciiRepresentation()) {
2927 Vector<const char> pat_vector = pat->ToAsciiVector();
2928 if (sub->IsAsciiRepresentation()) {
2929 position = StringMatchBackwards(sub->ToAsciiVector(),
2930 pat_vector,
2931 start_index);
2932 } else {
2933 position = StringMatchBackwards(sub->ToUC16Vector(),
2934 pat_vector,
2935 start_index);
2936 }
2937 } else {
2938 Vector<const uc16> pat_vector = pat->ToUC16Vector();
2939 if (sub->IsAsciiRepresentation()) {
2940 position = StringMatchBackwards(sub->ToAsciiVector(),
2941 pat_vector,
2942 start_index);
2943 } else {
2944 position = StringMatchBackwards(sub->ToUC16Vector(),
2945 pat_vector,
2946 start_index);
2947 }
2948 }
2949
2950 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002951}
2952
2953
2954static Object* Runtime_StringLocaleCompare(Arguments args) {
2955 NoHandleAllocation ha;
2956 ASSERT(args.length() == 2);
2957
2958 CONVERT_CHECKED(String, str1, args[0]);
2959 CONVERT_CHECKED(String, str2, args[1]);
2960
2961 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002962 int str1_length = str1->length();
2963 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002964
2965 // Decide trivial cases without flattening.
2966 if (str1_length == 0) {
2967 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2968 return Smi::FromInt(-str2_length);
2969 } else {
2970 if (str2_length == 0) return Smi::FromInt(str1_length);
2971 }
2972
2973 int end = str1_length < str2_length ? str1_length : str2_length;
2974
2975 // No need to flatten if we are going to find the answer on the first
2976 // character. At this point we know there is at least one character
2977 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002978 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002979 if (d != 0) return Smi::FromInt(d);
2980
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002981 str1->TryFlatten();
2982 str2->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002983
2984 static StringInputBuffer buf1;
2985 static StringInputBuffer buf2;
2986
2987 buf1.Reset(str1);
2988 buf2.Reset(str2);
2989
2990 for (int i = 0; i < end; i++) {
2991 uint16_t char1 = buf1.GetNext();
2992 uint16_t char2 = buf2.GetNext();
2993 if (char1 != char2) return Smi::FromInt(char1 - char2);
2994 }
2995
2996 return Smi::FromInt(str1_length - str2_length);
2997}
2998
2999
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003000static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003001 NoHandleAllocation ha;
3002 ASSERT(args.length() == 3);
3003
3004 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00003005 Object* from = args[1];
3006 Object* to = args[2];
3007 int start, end;
3008 // We have a fast integer-only case here to avoid a conversion to double in
3009 // the common case where from and to are Smis.
3010 if (from->IsSmi() && to->IsSmi()) {
3011 start = Smi::cast(from)->value();
3012 end = Smi::cast(to)->value();
3013 } else {
3014 CONVERT_DOUBLE_CHECKED(from_number, from);
3015 CONVERT_DOUBLE_CHECKED(to_number, to);
3016 start = FastD2I(from_number);
3017 end = FastD2I(to_number);
3018 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003019 RUNTIME_ASSERT(end >= start);
3020 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003021 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003022 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003023 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003024}
3025
3026
ager@chromium.org41826e72009-03-30 13:30:57 +00003027static Object* Runtime_StringMatch(Arguments args) {
3028 ASSERT_EQ(3, args.length());
3029
3030 CONVERT_ARG_CHECKED(String, subject, 0);
3031 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
3032 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
3033 HandleScope handles;
3034
3035 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
3036
3037 if (match.is_null()) {
3038 return Failure::Exception();
3039 }
3040 if (match->IsNull()) {
3041 return Heap::null_value();
3042 }
3043 int length = subject->length();
3044
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00003045 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00003046 ZoneList<int> offsets(8);
3047 do {
3048 int start;
3049 int end;
3050 {
3051 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003052 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00003053 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
3054 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
3055 }
3056 offsets.Add(start);
3057 offsets.Add(end);
3058 int index = start < end ? end : end + 1;
3059 if (index > length) break;
3060 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
3061 if (match.is_null()) {
3062 return Failure::Exception();
3063 }
3064 } while (!match->IsNull());
3065 int matches = offsets.length() / 2;
3066 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
3067 for (int i = 0; i < matches ; i++) {
3068 int from = offsets.at(i * 2);
3069 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003070 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00003071 }
3072 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
3073 result->set_length(Smi::FromInt(matches));
3074 return *result;
3075}
3076
3077
lrn@chromium.org25156de2010-04-06 13:10:27 +00003078// Two smis before and after the match, for very long strings.
3079const int kMaxBuilderEntriesPerRegExpMatch = 5;
3080
3081
3082static void SetLastMatchInfoNoCaptures(Handle<String> subject,
3083 Handle<JSArray> last_match_info,
3084 int match_start,
3085 int match_end) {
3086 // Fill last_match_info with a single capture.
3087 last_match_info->EnsureSize(2 + RegExpImpl::kLastMatchOverhead);
3088 AssertNoAllocation no_gc;
3089 FixedArray* elements = FixedArray::cast(last_match_info->elements());
3090 RegExpImpl::SetLastCaptureCount(elements, 2);
3091 RegExpImpl::SetLastInput(elements, *subject);
3092 RegExpImpl::SetLastSubject(elements, *subject);
3093 RegExpImpl::SetCapture(elements, 0, match_start);
3094 RegExpImpl::SetCapture(elements, 1, match_end);
3095}
3096
3097
3098template <typename schar>
3099static bool SearchCharMultiple(Vector<schar> subject,
3100 String* pattern,
3101 schar pattern_char,
3102 FixedArrayBuilder* builder,
3103 int* match_pos) {
3104 // Position of last match.
3105 int pos = *match_pos;
3106 int subject_length = subject.length();
3107 while (pos < subject_length) {
3108 int match_end = pos + 1;
3109 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3110 *match_pos = pos;
3111 return false;
3112 }
3113 int new_pos = SingleCharIndexOf(subject, pattern_char, match_end);
3114 if (new_pos >= 0) {
3115 // Match has been found.
3116 if (new_pos > match_end) {
3117 ReplacementStringBuilder::AddSubjectSlice(builder, match_end, new_pos);
3118 }
3119 pos = new_pos;
3120 builder->Add(pattern);
3121 } else {
3122 break;
3123 }
3124 }
3125 if (pos + 1 < subject_length) {
3126 ReplacementStringBuilder::AddSubjectSlice(builder, pos + 1, subject_length);
3127 }
3128 *match_pos = pos;
3129 return true;
3130}
3131
3132
3133static bool SearchCharMultiple(Handle<String> subject,
3134 Handle<String> pattern,
3135 Handle<JSArray> last_match_info,
3136 FixedArrayBuilder* builder) {
3137 ASSERT(subject->IsFlat());
3138 ASSERT_EQ(1, pattern->length());
3139 uc16 pattern_char = pattern->Get(0);
3140 // Treating position before first as initial "previous match position".
3141 int match_pos = -1;
3142
3143 for (;;) { // Break when search complete.
3144 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3145 AssertNoAllocation no_gc;
3146 if (subject->IsAsciiRepresentation()) {
3147 if (pattern_char > String::kMaxAsciiCharCode) {
3148 break;
3149 }
3150 Vector<const char> subject_vector = subject->ToAsciiVector();
3151 char pattern_ascii_char = static_cast<char>(pattern_char);
3152 bool complete = SearchCharMultiple<const char>(subject_vector,
3153 *pattern,
3154 pattern_ascii_char,
3155 builder,
3156 &match_pos);
3157 if (complete) break;
3158 } else {
3159 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3160 bool complete = SearchCharMultiple<const uc16>(subject_vector,
3161 *pattern,
3162 pattern_char,
3163 builder,
3164 &match_pos);
3165 if (complete) break;
3166 }
3167 }
3168
3169 if (match_pos >= 0) {
3170 SetLastMatchInfoNoCaptures(subject,
3171 last_match_info,
3172 match_pos,
3173 match_pos + 1);
3174 return true;
3175 }
3176 return false; // No matches at all.
3177}
3178
3179
3180template <typename schar, typename pchar>
3181static bool SearchStringMultiple(Vector<schar> subject,
3182 String* pattern,
3183 Vector<pchar> pattern_string,
3184 FixedArrayBuilder* builder,
3185 int* match_pos) {
3186 int pos = *match_pos;
3187 int subject_length = subject.length();
3188 int pattern_length = pattern_string.length();
3189 int max_search_start = subject_length - pattern_length;
3190 bool is_ascii = (sizeof(schar) == 1);
3191 StringSearchStrategy strategy =
3192 InitializeStringSearch(pattern_string, is_ascii);
3193 switch (strategy) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003194 case SEARCH_FAIL: break;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003195 case SEARCH_SHORT:
3196 while (pos <= max_search_start) {
3197 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3198 *match_pos = pos;
3199 return false;
3200 }
3201 // Position of end of previous match.
3202 int match_end = pos + pattern_length;
3203 int new_pos = SimpleIndexOf(subject, pattern_string, match_end);
3204 if (new_pos >= 0) {
3205 // A match.
3206 if (new_pos > match_end) {
3207 ReplacementStringBuilder::AddSubjectSlice(builder,
3208 match_end,
3209 new_pos);
3210 }
3211 pos = new_pos;
3212 builder->Add(pattern);
3213 } else {
3214 break;
3215 }
3216 }
3217 break;
3218 case SEARCH_LONG:
3219 while (pos <= max_search_start) {
3220 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003221 *match_pos = pos;
3222 return false;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003223 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003224 int match_end = pos + pattern_length;
3225 int new_pos = ComplexIndexOf(subject, pattern_string, match_end);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003226 if (new_pos >= 0) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003227 // A match has been found.
3228 if (new_pos > match_end) {
3229 ReplacementStringBuilder::AddSubjectSlice(builder,
3230 match_end,
3231 new_pos);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003232 }
3233 pos = new_pos;
3234 builder->Add(pattern);
3235 } else {
3236 break;
3237 }
3238 }
3239 break;
3240 }
3241 if (pos < max_search_start) {
3242 ReplacementStringBuilder::AddSubjectSlice(builder,
3243 pos + pattern_length,
3244 subject_length);
3245 }
3246 *match_pos = pos;
3247 return true;
3248}
3249
3250
3251static bool SearchStringMultiple(Handle<String> subject,
3252 Handle<String> pattern,
3253 Handle<JSArray> last_match_info,
3254 FixedArrayBuilder* builder) {
3255 ASSERT(subject->IsFlat());
3256 ASSERT(pattern->IsFlat());
3257 ASSERT(pattern->length() > 1);
3258
3259 // Treating as if a previous match was before first character.
3260 int match_pos = -pattern->length();
3261
3262 for (;;) { // Break when search complete.
3263 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3264 AssertNoAllocation no_gc;
3265 if (subject->IsAsciiRepresentation()) {
3266 Vector<const char> subject_vector = subject->ToAsciiVector();
3267 if (pattern->IsAsciiRepresentation()) {
3268 if (SearchStringMultiple(subject_vector,
3269 *pattern,
3270 pattern->ToAsciiVector(),
3271 builder,
3272 &match_pos)) break;
3273 } else {
3274 if (SearchStringMultiple(subject_vector,
3275 *pattern,
3276 pattern->ToUC16Vector(),
3277 builder,
3278 &match_pos)) break;
3279 }
3280 } else {
3281 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3282 if (pattern->IsAsciiRepresentation()) {
3283 if (SearchStringMultiple(subject_vector,
3284 *pattern,
3285 pattern->ToAsciiVector(),
3286 builder,
3287 &match_pos)) break;
3288 } else {
3289 if (SearchStringMultiple(subject_vector,
3290 *pattern,
3291 pattern->ToUC16Vector(),
3292 builder,
3293 &match_pos)) break;
3294 }
3295 }
3296 }
3297
3298 if (match_pos >= 0) {
3299 SetLastMatchInfoNoCaptures(subject,
3300 last_match_info,
3301 match_pos,
3302 match_pos + pattern->length());
3303 return true;
3304 }
3305 return false; // No matches at all.
3306}
3307
3308
3309static RegExpImpl::IrregexpResult SearchRegExpNoCaptureMultiple(
3310 Handle<String> subject,
3311 Handle<JSRegExp> regexp,
3312 Handle<JSArray> last_match_array,
3313 FixedArrayBuilder* builder) {
3314 ASSERT(subject->IsFlat());
3315 int match_start = -1;
3316 int match_end = 0;
3317 int pos = 0;
3318 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3319 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3320
3321 OffsetsVector registers(required_registers);
3322 Vector<int> register_vector(registers.vector(), registers.length());
3323 int subject_length = subject->length();
3324
3325 for (;;) { // Break on failure, return on exception.
3326 RegExpImpl::IrregexpResult result =
3327 RegExpImpl::IrregexpExecOnce(regexp,
3328 subject,
3329 pos,
3330 register_vector);
3331 if (result == RegExpImpl::RE_SUCCESS) {
3332 match_start = register_vector[0];
3333 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3334 if (match_end < match_start) {
3335 ReplacementStringBuilder::AddSubjectSlice(builder,
3336 match_end,
3337 match_start);
3338 }
3339 match_end = register_vector[1];
3340 HandleScope loop_scope;
3341 builder->Add(*Factory::NewSubString(subject, match_start, match_end));
3342 if (match_start != match_end) {
3343 pos = match_end;
3344 } else {
3345 pos = match_end + 1;
3346 if (pos > subject_length) break;
3347 }
3348 } else if (result == RegExpImpl::RE_FAILURE) {
3349 break;
3350 } else {
3351 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3352 return result;
3353 }
3354 }
3355
3356 if (match_start >= 0) {
3357 if (match_end < subject_length) {
3358 ReplacementStringBuilder::AddSubjectSlice(builder,
3359 match_end,
3360 subject_length);
3361 }
3362 SetLastMatchInfoNoCaptures(subject,
3363 last_match_array,
3364 match_start,
3365 match_end);
3366 return RegExpImpl::RE_SUCCESS;
3367 } else {
3368 return RegExpImpl::RE_FAILURE; // No matches at all.
3369 }
3370}
3371
3372
3373static RegExpImpl::IrregexpResult SearchRegExpMultiple(
3374 Handle<String> subject,
3375 Handle<JSRegExp> regexp,
3376 Handle<JSArray> last_match_array,
3377 FixedArrayBuilder* builder) {
3378
3379 ASSERT(subject->IsFlat());
3380 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3381 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3382
3383 OffsetsVector registers(required_registers);
3384 Vector<int> register_vector(registers.vector(), registers.length());
3385
3386 RegExpImpl::IrregexpResult result =
3387 RegExpImpl::IrregexpExecOnce(regexp,
3388 subject,
3389 0,
3390 register_vector);
3391
3392 int capture_count = regexp->CaptureCount();
3393 int subject_length = subject->length();
3394
3395 // Position to search from.
3396 int pos = 0;
3397 // End of previous match. Differs from pos if match was empty.
3398 int match_end = 0;
3399 if (result == RegExpImpl::RE_SUCCESS) {
3400 // Need to keep a copy of the previous match for creating last_match_info
3401 // at the end, so we have two vectors that we swap between.
3402 OffsetsVector registers2(required_registers);
3403 Vector<int> prev_register_vector(registers2.vector(), registers2.length());
3404
3405 do {
3406 int match_start = register_vector[0];
3407 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3408 if (match_end < match_start) {
3409 ReplacementStringBuilder::AddSubjectSlice(builder,
3410 match_end,
3411 match_start);
3412 }
3413 match_end = register_vector[1];
3414
3415 {
3416 // Avoid accumulating new handles inside loop.
3417 HandleScope temp_scope;
3418 // Arguments array to replace function is match, captures, index and
3419 // subject, i.e., 3 + capture count in total.
3420 Handle<FixedArray> elements = Factory::NewFixedArray(3 + capture_count);
3421 elements->set(0, *Factory::NewSubString(subject,
3422 match_start,
3423 match_end));
3424 for (int i = 1; i <= capture_count; i++) {
3425 int start = register_vector[i * 2];
3426 if (start >= 0) {
3427 int end = register_vector[i * 2 + 1];
3428 ASSERT(start <= end);
3429 Handle<String> substring = Factory::NewSubString(subject,
3430 start,
3431 end);
3432 elements->set(i, *substring);
3433 } else {
3434 ASSERT(register_vector[i * 2 + 1] < 0);
3435 elements->set(i, Heap::undefined_value());
3436 }
3437 }
3438 elements->set(capture_count + 1, Smi::FromInt(match_start));
3439 elements->set(capture_count + 2, *subject);
3440 builder->Add(*Factory::NewJSArrayWithElements(elements));
3441 }
3442 // Swap register vectors, so the last successful match is in
3443 // prev_register_vector.
3444 Vector<int> tmp = prev_register_vector;
3445 prev_register_vector = register_vector;
3446 register_vector = tmp;
3447
3448 if (match_end > match_start) {
3449 pos = match_end;
3450 } else {
3451 pos = match_end + 1;
3452 if (pos > subject_length) {
3453 break;
3454 }
3455 }
3456
3457 result = RegExpImpl::IrregexpExecOnce(regexp,
3458 subject,
3459 pos,
3460 register_vector);
3461 } while (result == RegExpImpl::RE_SUCCESS);
3462
3463 if (result != RegExpImpl::RE_EXCEPTION) {
3464 // Finished matching, with at least one match.
3465 if (match_end < subject_length) {
3466 ReplacementStringBuilder::AddSubjectSlice(builder,
3467 match_end,
3468 subject_length);
3469 }
3470
3471 int last_match_capture_count = (capture_count + 1) * 2;
3472 int last_match_array_size =
3473 last_match_capture_count + RegExpImpl::kLastMatchOverhead;
3474 last_match_array->EnsureSize(last_match_array_size);
3475 AssertNoAllocation no_gc;
3476 FixedArray* elements = FixedArray::cast(last_match_array->elements());
3477 RegExpImpl::SetLastCaptureCount(elements, last_match_capture_count);
3478 RegExpImpl::SetLastSubject(elements, *subject);
3479 RegExpImpl::SetLastInput(elements, *subject);
3480 for (int i = 0; i < last_match_capture_count; i++) {
3481 RegExpImpl::SetCapture(elements, i, prev_register_vector[i]);
3482 }
3483 return RegExpImpl::RE_SUCCESS;
3484 }
3485 }
3486 // No matches at all, return failure or exception result directly.
3487 return result;
3488}
3489
3490
3491static Object* Runtime_RegExpExecMultiple(Arguments args) {
3492 ASSERT(args.length() == 4);
3493 HandleScope handles;
3494
3495 CONVERT_ARG_CHECKED(String, subject, 1);
3496 if (!subject->IsFlat()) { FlattenString(subject); }
3497 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
3498 CONVERT_ARG_CHECKED(JSArray, last_match_info, 2);
3499 CONVERT_ARG_CHECKED(JSArray, result_array, 3);
3500
3501 ASSERT(last_match_info->HasFastElements());
3502 ASSERT(regexp->GetFlags().is_global());
3503 Handle<FixedArray> result_elements;
3504 if (result_array->HasFastElements()) {
3505 result_elements =
3506 Handle<FixedArray>(FixedArray::cast(result_array->elements()));
3507 } else {
3508 result_elements = Factory::NewFixedArrayWithHoles(16);
3509 }
3510 FixedArrayBuilder builder(result_elements);
3511
3512 if (regexp->TypeTag() == JSRegExp::ATOM) {
3513 Handle<String> pattern(
3514 String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex)));
3515 int pattern_length = pattern->length();
3516 if (pattern_length == 1) {
3517 if (SearchCharMultiple(subject, pattern, last_match_info, &builder)) {
3518 return *builder.ToJSArray(result_array);
3519 }
3520 return Heap::null_value();
3521 }
3522
3523 if (!pattern->IsFlat()) FlattenString(pattern);
3524 if (SearchStringMultiple(subject, pattern, last_match_info, &builder)) {
3525 return *builder.ToJSArray(result_array);
3526 }
3527 return Heap::null_value();
3528 }
3529
3530 ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
3531
3532 RegExpImpl::IrregexpResult result;
3533 if (regexp->CaptureCount() == 0) {
3534 result = SearchRegExpNoCaptureMultiple(subject,
3535 regexp,
3536 last_match_info,
3537 &builder);
3538 } else {
3539 result = SearchRegExpMultiple(subject, regexp, last_match_info, &builder);
3540 }
3541 if (result == RegExpImpl::RE_SUCCESS) return *builder.ToJSArray(result_array);
3542 if (result == RegExpImpl::RE_FAILURE) return Heap::null_value();
3543 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3544 return Failure::Exception();
3545}
3546
3547
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003548static Object* Runtime_NumberToRadixString(Arguments args) {
3549 NoHandleAllocation ha;
3550 ASSERT(args.length() == 2);
3551
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003552 // Fast case where the result is a one character string.
3553 if (args[0]->IsSmi() && args[1]->IsSmi()) {
3554 int value = Smi::cast(args[0])->value();
3555 int radix = Smi::cast(args[1])->value();
3556 if (value >= 0 && value < radix) {
3557 RUNTIME_ASSERT(radix <= 36);
3558 // Character array used for conversion.
3559 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
3560 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
3561 }
3562 }
3563
3564 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003565 CONVERT_DOUBLE_CHECKED(value, args[0]);
3566 if (isnan(value)) {
3567 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3568 }
3569 if (isinf(value)) {
3570 if (value < 0) {
3571 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3572 }
3573 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3574 }
3575 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
3576 int radix = FastD2I(radix_number);
3577 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3578 char* str = DoubleToRadixCString(value, radix);
3579 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3580 DeleteArray(str);
3581 return result;
3582}
3583
3584
3585static Object* Runtime_NumberToFixed(Arguments args) {
3586 NoHandleAllocation ha;
3587 ASSERT(args.length() == 2);
3588
3589 CONVERT_DOUBLE_CHECKED(value, args[0]);
3590 if (isnan(value)) {
3591 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3592 }
3593 if (isinf(value)) {
3594 if (value < 0) {
3595 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3596 }
3597 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3598 }
3599 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3600 int f = FastD2I(f_number);
3601 RUNTIME_ASSERT(f >= 0);
3602 char* str = DoubleToFixedCString(value, f);
3603 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3604 DeleteArray(str);
3605 return res;
3606}
3607
3608
3609static Object* Runtime_NumberToExponential(Arguments args) {
3610 NoHandleAllocation ha;
3611 ASSERT(args.length() == 2);
3612
3613 CONVERT_DOUBLE_CHECKED(value, args[0]);
3614 if (isnan(value)) {
3615 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3616 }
3617 if (isinf(value)) {
3618 if (value < 0) {
3619 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3620 }
3621 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3622 }
3623 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3624 int f = FastD2I(f_number);
3625 RUNTIME_ASSERT(f >= -1 && f <= 20);
3626 char* str = DoubleToExponentialCString(value, f);
3627 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3628 DeleteArray(str);
3629 return res;
3630}
3631
3632
3633static Object* Runtime_NumberToPrecision(Arguments args) {
3634 NoHandleAllocation ha;
3635 ASSERT(args.length() == 2);
3636
3637 CONVERT_DOUBLE_CHECKED(value, args[0]);
3638 if (isnan(value)) {
3639 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3640 }
3641 if (isinf(value)) {
3642 if (value < 0) {
3643 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3644 }
3645 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3646 }
3647 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3648 int f = FastD2I(f_number);
3649 RUNTIME_ASSERT(f >= 1 && f <= 21);
3650 char* str = DoubleToPrecisionCString(value, f);
3651 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3652 DeleteArray(str);
3653 return res;
3654}
3655
3656
3657// Returns a single character string where first character equals
3658// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003659static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003660 if (index < static_cast<uint32_t>(string->length())) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003661 string->TryFlatten();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003662 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003663 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003664 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003665 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003666}
3667
3668
3669Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
3670 // Handle [] indexing on Strings
3671 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003672 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
3673 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003674 }
3675
3676 // Handle [] indexing on String objects
3677 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003678 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
3679 Handle<Object> result =
3680 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
3681 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003682 }
3683
3684 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003685 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003686 return prototype->GetElement(index);
3687 }
3688
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003689 return GetElement(object, index);
3690}
3691
3692
3693Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003694 return object->GetElement(index);
3695}
3696
3697
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003698Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
3699 HandleScope scope;
3700
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003701 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003702 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003703 Handle<Object> error =
3704 Factory::NewTypeError("non_object_property_load",
3705 HandleVector(args, 2));
3706 return Top::Throw(*error);
3707 }
3708
3709 // Check if the given key is an array index.
3710 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003711 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003712 return GetElementOrCharAt(object, index);
3713 }
3714
3715 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003716 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003717 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003718 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003719 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003720 bool has_pending_exception = false;
3721 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003722 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003723 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003724 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003725 }
3726
ager@chromium.org32912102009-01-16 10:38:43 +00003727 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003728 // the element if so.
3729 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003730 return GetElementOrCharAt(object, index);
3731 } else {
3732 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003733 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003734 }
3735}
3736
3737
3738static Object* Runtime_GetProperty(Arguments args) {
3739 NoHandleAllocation ha;
3740 ASSERT(args.length() == 2);
3741
3742 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003743 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003744
3745 return Runtime::GetObjectProperty(object, key);
3746}
3747
3748
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003749// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003750static Object* Runtime_KeyedGetProperty(Arguments args) {
3751 NoHandleAllocation ha;
3752 ASSERT(args.length() == 2);
3753
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003754 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00003755 // itself.
3756 //
3757 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00003758 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00003759 // global proxy object never has properties. This is the case
3760 // because the global proxy object forwards everything to its hidden
3761 // prototype including local lookups.
3762 //
3763 // Additionally, we need to make sure that we do not cache results
3764 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003765 if (args[0]->IsJSObject() &&
3766 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00003767 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003768 args[1]->IsString()) {
3769 JSObject* receiver = JSObject::cast(args[0]);
3770 String* key = String::cast(args[1]);
3771 if (receiver->HasFastProperties()) {
3772 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003773 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003774 int offset = KeyedLookupCache::Lookup(receiver_map, key);
3775 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003776 Object* value = receiver->FastPropertyAt(offset);
3777 return value->IsTheHole() ? Heap::undefined_value() : value;
3778 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003779 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003780 LookupResult result;
3781 receiver->LocalLookup(key, &result);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00003782 if (result.IsProperty() && result.type() == FIELD) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003783 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003784 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003785 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003786 }
3787 } else {
3788 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003789 StringDictionary* dictionary = receiver->property_dictionary();
3790 int entry = dictionary->FindEntry(key);
3791 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003792 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003793 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003794 if (!receiver->IsGlobalObject()) return value;
3795 value = JSGlobalPropertyCell::cast(value)->value();
3796 if (!value->IsTheHole()) return value;
3797 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003798 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003799 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003800 } else if (args[0]->IsString() && args[1]->IsSmi()) {
3801 // Fast case for string indexing using [] with a smi index.
3802 HandleScope scope;
3803 Handle<String> str = args.at<String>(0);
3804 int index = Smi::cast(args[1])->value();
3805 Handle<Object> result = GetCharAt(str, index);
3806 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003807 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003808
3809 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003810 return Runtime::GetObjectProperty(args.at<Object>(0),
3811 args.at<Object>(1));
3812}
3813
3814
ager@chromium.org5c838252010-02-19 08:53:10 +00003815static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
3816 ASSERT(args.length() == 5);
3817 HandleScope scope;
3818 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3819 CONVERT_CHECKED(String, name, args[1]);
3820 CONVERT_CHECKED(Smi, flag_setter, args[2]);
3821 CONVERT_CHECKED(JSFunction, fun, args[3]);
3822 CONVERT_CHECKED(Smi, flag_attr, args[4]);
3823 int unchecked = flag_attr->value();
3824 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3825 RUNTIME_ASSERT(!obj->IsNull());
3826 LookupResult result;
3827 obj->LocalLookupRealNamedProperty(name, &result);
3828
3829 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3830 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
3831 // delete it to avoid running into trouble in DefineAccessor, which
3832 // handles this incorrectly if the property is readonly (does nothing)
3833 if (result.IsProperty() &&
3834 (result.type() == FIELD || result.type() == NORMAL
3835 || result.type() == CONSTANT_FUNCTION)) {
3836 obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
3837 }
3838 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
3839}
3840
3841static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
3842 ASSERT(args.length() == 4);
3843 HandleScope scope;
3844 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
3845 CONVERT_ARG_CHECKED(String, name, 1);
3846 Handle<Object> obj_value = args.at<Object>(2);
3847
3848 CONVERT_CHECKED(Smi, flag, args[3]);
3849 int unchecked = flag->value();
3850 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3851
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00003852 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3853
3854 // Check if this is an element.
3855 uint32_t index;
3856 bool is_element = name->AsArrayIndex(&index);
3857
3858 // Special case for elements if any of the flags are true.
3859 // If elements are in fast case we always implicitly assume that:
3860 // DONT_DELETE: false, DONT_ENUM: false, READ_ONLY: false.
3861 if (((unchecked & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) &&
3862 is_element) {
3863 // Normalize the elements to enable attributes on the property.
3864 js_object->NormalizeElements();
3865 NumberDictionary* dictionary = js_object->element_dictionary();
3866 // Make sure that we never go back to fast case.
3867 dictionary->set_requires_slow_elements();
3868 PropertyDetails details = PropertyDetails(attr, NORMAL);
3869 dictionary->Set(index, *obj_value, details);
3870 }
3871
ager@chromium.org5c838252010-02-19 08:53:10 +00003872 LookupResult result;
3873 js_object->LocalLookupRealNamedProperty(*name, &result);
3874
ager@chromium.org5c838252010-02-19 08:53:10 +00003875 // Take special care when attributes are different and there is already
3876 // a property. For simplicity we normalize the property which enables us
3877 // to not worry about changing the instance_descriptor and creating a new
3878 // map. The current version of SetObjectProperty does not handle attributes
3879 // correctly in the case where a property is a field and is reset with
3880 // new attributes.
3881 if (result.IsProperty() && attr != result.GetAttributes()) {
3882 // New attributes - normalize to avoid writing to instance descriptor
3883 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3884 // Use IgnoreAttributes version since a readonly property may be
3885 // overridden and SetProperty does not allow this.
3886 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
3887 *obj_value,
3888 attr);
3889 }
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00003890
ager@chromium.org5c838252010-02-19 08:53:10 +00003891 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
3892}
3893
3894
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003895Object* Runtime::SetObjectProperty(Handle<Object> object,
3896 Handle<Object> key,
3897 Handle<Object> value,
3898 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003899 HandleScope scope;
3900
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003901 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003902 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003903 Handle<Object> error =
3904 Factory::NewTypeError("non_object_property_store",
3905 HandleVector(args, 2));
3906 return Top::Throw(*error);
3907 }
3908
3909 // If the object isn't a JavaScript object, we ignore the store.
3910 if (!object->IsJSObject()) return *value;
3911
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003912 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3913
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003914 // Check if the given key is an array index.
3915 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003916 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003917 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3918 // of a string using [] notation. We need to support this too in
3919 // JavaScript.
3920 // In the case of a String object we just need to redirect the assignment to
3921 // the underlying string if the index is in range. Since the underlying
3922 // string does nothing with the assignment then we can ignore such
3923 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003924 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003925 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003926 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003927
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003928 Handle<Object> result = SetElement(js_object, index, value);
3929 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003930 return *value;
3931 }
3932
3933 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003934 Handle<Object> result;
3935 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003936 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003937 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003938 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003939 key_string->TryFlatten();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003940 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003941 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003942 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003943 return *value;
3944 }
3945
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003946 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003947 bool has_pending_exception = false;
3948 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3949 if (has_pending_exception) return Failure::Exception();
3950 Handle<String> name = Handle<String>::cast(converted);
3951
3952 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003953 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003954 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003955 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003956 }
3957}
3958
3959
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003960Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
3961 Handle<Object> key,
3962 Handle<Object> value,
3963 PropertyAttributes attr) {
3964 HandleScope scope;
3965
3966 // Check if the given key is an array index.
3967 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003968 if (key->ToArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003969 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3970 // of a string using [] notation. We need to support this too in
3971 // JavaScript.
3972 // In the case of a String object we just need to redirect the assignment to
3973 // the underlying string if the index is in range. Since the underlying
3974 // string does nothing with the assignment then we can ignore such
3975 // assignments.
3976 if (js_object->IsStringObjectWithCharacterAt(index)) {
3977 return *value;
3978 }
3979
3980 return js_object->SetElement(index, *value);
3981 }
3982
3983 if (key->IsString()) {
3984 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003985 return js_object->SetElement(index, *value);
3986 } else {
3987 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003988 key_string->TryFlatten();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003989 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
3990 *value,
3991 attr);
3992 }
3993 }
3994
3995 // Call-back into JavaScript to convert the key to a string.
3996 bool has_pending_exception = false;
3997 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3998 if (has_pending_exception) return Failure::Exception();
3999 Handle<String> name = Handle<String>::cast(converted);
4000
4001 if (name->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004002 return js_object->SetElement(index, *value);
4003 } else {
4004 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
4005 }
4006}
4007
4008
ager@chromium.orge2902be2009-06-08 12:21:35 +00004009Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
4010 Handle<Object> key) {
4011 HandleScope scope;
4012
4013 // Check if the given key is an array index.
4014 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004015 if (key->ToArrayIndex(&index)) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00004016 // In Firefox/SpiderMonkey, Safari and Opera you can access the
4017 // characters of a string using [] notation. In the case of a
4018 // String object we just need to redirect the deletion to the
4019 // underlying string if the index is in range. Since the
4020 // underlying string does nothing with the deletion, we can ignore
4021 // such deletions.
4022 if (js_object->IsStringObjectWithCharacterAt(index)) {
4023 return Heap::true_value();
4024 }
4025
4026 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
4027 }
4028
4029 Handle<String> key_string;
4030 if (key->IsString()) {
4031 key_string = Handle<String>::cast(key);
4032 } else {
4033 // Call-back into JavaScript to convert the key to a string.
4034 bool has_pending_exception = false;
4035 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4036 if (has_pending_exception) return Failure::Exception();
4037 key_string = Handle<String>::cast(converted);
4038 }
4039
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004040 key_string->TryFlatten();
ager@chromium.orge2902be2009-06-08 12:21:35 +00004041 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
4042}
4043
4044
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004045static Object* Runtime_SetProperty(Arguments args) {
4046 NoHandleAllocation ha;
4047 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
4048
4049 Handle<Object> object = args.at<Object>(0);
4050 Handle<Object> key = args.at<Object>(1);
4051 Handle<Object> value = args.at<Object>(2);
4052
4053 // Compute attributes.
4054 PropertyAttributes attributes = NONE;
4055 if (args.length() == 4) {
4056 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004057 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004058 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004059 RUNTIME_ASSERT(
4060 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4061 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004062 }
4063 return Runtime::SetObjectProperty(object, key, value, attributes);
4064}
4065
4066
4067// Set a local property, even if it is READ_ONLY. If the property does not
4068// exist, it will be added with attributes NONE.
4069static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
4070 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004071 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004072 CONVERT_CHECKED(JSObject, object, args[0]);
4073 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004074 // Compute attributes.
4075 PropertyAttributes attributes = NONE;
4076 if (args.length() == 4) {
4077 CONVERT_CHECKED(Smi, value_obj, args[3]);
4078 int unchecked_value = value_obj->value();
4079 // Only attribute bits should be set.
4080 RUNTIME_ASSERT(
4081 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4082 attributes = static_cast<PropertyAttributes>(unchecked_value);
4083 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004084
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004085 return object->
4086 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004087}
4088
4089
4090static Object* Runtime_DeleteProperty(Arguments args) {
4091 NoHandleAllocation ha;
4092 ASSERT(args.length() == 2);
4093
4094 CONVERT_CHECKED(JSObject, object, args[0]);
4095 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00004096 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004097}
4098
4099
ager@chromium.org9085a012009-05-11 19:22:57 +00004100static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
4101 Handle<String> key) {
4102 if (object->HasLocalProperty(*key)) return Heap::true_value();
4103 // Handle hidden prototypes. If there's a hidden prototype above this thing
4104 // then we have to check it for properties, because they are supposed to
4105 // look like they are on this object.
4106 Handle<Object> proto(object->GetPrototype());
4107 if (proto->IsJSObject() &&
4108 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
4109 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
4110 }
4111 return Heap::false_value();
4112}
4113
4114
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004115static Object* Runtime_HasLocalProperty(Arguments args) {
4116 NoHandleAllocation ha;
4117 ASSERT(args.length() == 2);
4118 CONVERT_CHECKED(String, key, args[1]);
4119
ager@chromium.org9085a012009-05-11 19:22:57 +00004120 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004121 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00004122 if (obj->IsJSObject()) {
4123 JSObject* object = JSObject::cast(obj);
4124 // Fast case - no interceptors.
4125 if (object->HasRealNamedProperty(key)) return Heap::true_value();
4126 // Slow case. Either it's not there or we have an interceptor. We should
4127 // have handles for this kind of deal.
4128 HandleScope scope;
4129 return HasLocalPropertyImplementation(Handle<JSObject>(object),
4130 Handle<String>(key));
4131 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004132 // Well, there is one exception: Handle [] on strings.
4133 uint32_t index;
4134 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00004135 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004136 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004137 return Heap::true_value();
4138 }
4139 }
4140 return Heap::false_value();
4141}
4142
4143
4144static Object* Runtime_HasProperty(Arguments args) {
4145 NoHandleAllocation na;
4146 ASSERT(args.length() == 2);
4147
4148 // Only JS objects can have properties.
4149 if (args[0]->IsJSObject()) {
4150 JSObject* object = JSObject::cast(args[0]);
4151 CONVERT_CHECKED(String, key, args[1]);
4152 if (object->HasProperty(key)) return Heap::true_value();
4153 }
4154 return Heap::false_value();
4155}
4156
4157
4158static Object* Runtime_HasElement(Arguments args) {
4159 NoHandleAllocation na;
4160 ASSERT(args.length() == 2);
4161
4162 // Only JS objects can have elements.
4163 if (args[0]->IsJSObject()) {
4164 JSObject* object = JSObject::cast(args[0]);
4165 CONVERT_CHECKED(Smi, index_obj, args[1]);
4166 uint32_t index = index_obj->value();
4167 if (object->HasElement(index)) return Heap::true_value();
4168 }
4169 return Heap::false_value();
4170}
4171
4172
4173static Object* Runtime_IsPropertyEnumerable(Arguments args) {
4174 NoHandleAllocation ha;
4175 ASSERT(args.length() == 2);
4176
4177 CONVERT_CHECKED(JSObject, object, args[0]);
4178 CONVERT_CHECKED(String, key, args[1]);
4179
4180 uint32_t index;
4181 if (key->AsArrayIndex(&index)) {
4182 return Heap::ToBoolean(object->HasElement(index));
4183 }
4184
ager@chromium.org870a0b62008-11-04 11:43:05 +00004185 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
4186 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004187}
4188
4189
4190static Object* Runtime_GetPropertyNames(Arguments args) {
4191 HandleScope scope;
4192 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004193 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004194 return *GetKeysFor(object);
4195}
4196
4197
4198// Returns either a FixedArray as Runtime_GetPropertyNames,
4199// or, if the given object has an enum cache that contains
4200// all enumerable properties of the object and its prototypes
4201// have none, the map of the object. This is used to speed up
4202// the check for deletions during a for-in.
4203static Object* Runtime_GetPropertyNamesFast(Arguments args) {
4204 ASSERT(args.length() == 1);
4205
4206 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4207
4208 if (raw_object->IsSimpleEnum()) return raw_object->map();
4209
4210 HandleScope scope;
4211 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004212 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
4213 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004214
4215 // Test again, since cache may have been built by preceding call.
4216 if (object->IsSimpleEnum()) return object->map();
4217
4218 return *content;
4219}
4220
4221
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004222// Find the length of the prototype chain that is to to handled as one. If a
4223// prototype object is hidden it is to be viewed as part of the the object it
4224// is prototype for.
4225static int LocalPrototypeChainLength(JSObject* obj) {
4226 int count = 1;
4227 Object* proto = obj->GetPrototype();
4228 while (proto->IsJSObject() &&
4229 JSObject::cast(proto)->map()->is_hidden_prototype()) {
4230 count++;
4231 proto = JSObject::cast(proto)->GetPrototype();
4232 }
4233 return count;
4234}
4235
4236
4237// Return the names of the local named properties.
4238// args[0]: object
4239static Object* Runtime_GetLocalPropertyNames(Arguments args) {
4240 HandleScope scope;
4241 ASSERT(args.length() == 1);
4242 if (!args[0]->IsJSObject()) {
4243 return Heap::undefined_value();
4244 }
4245 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4246
4247 // Skip the global proxy as it has no properties and always delegates to the
4248 // real global object.
4249 if (obj->IsJSGlobalProxy()) {
4250 // Only collect names if access is permitted.
4251 if (obj->IsAccessCheckNeeded() &&
4252 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
4253 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
4254 return *Factory::NewJSArray(0);
4255 }
4256 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
4257 }
4258
4259 // Find the number of objects making up this.
4260 int length = LocalPrototypeChainLength(*obj);
4261
4262 // Find the number of local properties for each of the objects.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004263 ScopedVector<int> local_property_count(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004264 int total_property_count = 0;
4265 Handle<JSObject> jsproto = obj;
4266 for (int i = 0; i < length; i++) {
4267 // Only collect names if access is permitted.
4268 if (jsproto->IsAccessCheckNeeded() &&
4269 !Top::MayNamedAccess(*jsproto,
4270 Heap::undefined_value(),
4271 v8::ACCESS_KEYS)) {
4272 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
4273 return *Factory::NewJSArray(0);
4274 }
4275 int n;
4276 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4277 local_property_count[i] = n;
4278 total_property_count += n;
4279 if (i < length - 1) {
4280 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4281 }
4282 }
4283
4284 // Allocate an array with storage for all the property names.
4285 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
4286
4287 // Get the property names.
4288 jsproto = obj;
4289 int proto_with_hidden_properties = 0;
4290 for (int i = 0; i < length; i++) {
4291 jsproto->GetLocalPropertyNames(*names,
4292 i == 0 ? 0 : local_property_count[i - 1]);
4293 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
4294 proto_with_hidden_properties++;
4295 }
4296 if (i < length - 1) {
4297 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4298 }
4299 }
4300
4301 // Filter out name of hidden propeties object.
4302 if (proto_with_hidden_properties > 0) {
4303 Handle<FixedArray> old_names = names;
4304 names = Factory::NewFixedArray(
4305 names->length() - proto_with_hidden_properties);
4306 int dest_pos = 0;
4307 for (int i = 0; i < total_property_count; i++) {
4308 Object* name = old_names->get(i);
4309 if (name == Heap::hidden_symbol()) {
4310 continue;
4311 }
4312 names->set(dest_pos++, name);
4313 }
4314 }
4315
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004316 return *Factory::NewJSArrayWithElements(names);
4317}
4318
4319
4320// Return the names of the local indexed properties.
4321// args[0]: object
4322static Object* Runtime_GetLocalElementNames(Arguments args) {
4323 HandleScope scope;
4324 ASSERT(args.length() == 1);
4325 if (!args[0]->IsJSObject()) {
4326 return Heap::undefined_value();
4327 }
4328 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4329
4330 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4331 Handle<FixedArray> names = Factory::NewFixedArray(n);
4332 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4333 return *Factory::NewJSArrayWithElements(names);
4334}
4335
4336
4337// Return information on whether an object has a named or indexed interceptor.
4338// args[0]: object
4339static Object* Runtime_GetInterceptorInfo(Arguments args) {
4340 HandleScope scope;
4341 ASSERT(args.length() == 1);
4342 if (!args[0]->IsJSObject()) {
4343 return Smi::FromInt(0);
4344 }
4345 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4346
4347 int result = 0;
4348 if (obj->HasNamedInterceptor()) result |= 2;
4349 if (obj->HasIndexedInterceptor()) result |= 1;
4350
4351 return Smi::FromInt(result);
4352}
4353
4354
4355// Return property names from named interceptor.
4356// args[0]: object
4357static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
4358 HandleScope scope;
4359 ASSERT(args.length() == 1);
4360 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4361
4362 if (obj->HasNamedInterceptor()) {
4363 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4364 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4365 }
4366 return Heap::undefined_value();
4367}
4368
4369
4370// Return element names from indexed interceptor.
4371// args[0]: object
4372static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
4373 HandleScope scope;
4374 ASSERT(args.length() == 1);
4375 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4376
4377 if (obj->HasIndexedInterceptor()) {
4378 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4379 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4380 }
4381 return Heap::undefined_value();
4382}
4383
4384
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004385static Object* Runtime_LocalKeys(Arguments args) {
4386 ASSERT_EQ(args.length(), 1);
4387 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4388 HandleScope scope;
4389 Handle<JSObject> object(raw_object);
4390 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
4391 LOCAL_ONLY);
4392 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
4393 // property array and since the result is mutable we have to create
4394 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004395 int length = contents->length();
4396 Handle<FixedArray> copy = Factory::NewFixedArray(length);
4397 for (int i = 0; i < length; i++) {
4398 Object* entry = contents->get(i);
4399 if (entry->IsString()) {
4400 copy->set(i, entry);
4401 } else {
4402 ASSERT(entry->IsNumber());
4403 HandleScope scope;
4404 Handle<Object> entry_handle(entry);
4405 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
4406 copy->set(i, *entry_str);
4407 }
4408 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004409 return *Factory::NewJSArrayWithElements(copy);
4410}
4411
4412
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004413static Object* Runtime_GetArgumentsProperty(Arguments args) {
4414 NoHandleAllocation ha;
4415 ASSERT(args.length() == 1);
4416
4417 // Compute the frame holding the arguments.
4418 JavaScriptFrameIterator it;
4419 it.AdvanceToArgumentsFrame();
4420 JavaScriptFrame* frame = it.frame();
4421
4422 // Get the actual number of provided arguments.
4423 const uint32_t n = frame->GetProvidedParametersCount();
4424
4425 // Try to convert the key to an index. If successful and within
4426 // index return the the argument from the frame.
4427 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004428 if (args[0]->ToArrayIndex(&index) && index < n) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004429 return frame->GetParameter(index);
4430 }
4431
4432 // Convert the key to a string.
4433 HandleScope scope;
4434 bool exception = false;
4435 Handle<Object> converted =
4436 Execution::ToString(args.at<Object>(0), &exception);
4437 if (exception) return Failure::Exception();
4438 Handle<String> key = Handle<String>::cast(converted);
4439
4440 // Try to convert the string key into an array index.
4441 if (key->AsArrayIndex(&index)) {
4442 if (index < n) {
4443 return frame->GetParameter(index);
4444 } else {
4445 return Top::initial_object_prototype()->GetElement(index);
4446 }
4447 }
4448
4449 // Handle special arguments properties.
4450 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
4451 if (key->Equals(Heap::callee_symbol())) return frame->function();
4452
4453 // Lookup in the initial Object.prototype object.
4454 return Top::initial_object_prototype()->GetProperty(*key);
4455}
4456
4457
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004458static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004459 HandleScope scope;
4460
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004461 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004462 Handle<Object> object = args.at<Object>(0);
4463 if (object->IsJSObject()) {
4464 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00004465 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
4466 js_object->TransformToFastProperties(0);
4467 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004468 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004469 return *object;
4470}
4471
4472
4473static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004474 HandleScope scope;
4475
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004476 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004477 Handle<Object> object = args.at<Object>(0);
4478 if (object->IsJSObject()) {
4479 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004480 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004481 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004482 return *object;
4483}
4484
4485
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004486static Object* Runtime_ToBool(Arguments args) {
4487 NoHandleAllocation ha;
4488 ASSERT(args.length() == 1);
4489
4490 return args[0]->ToBoolean();
4491}
4492
4493
4494// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
4495// Possible optimizations: put the type string into the oddballs.
4496static Object* Runtime_Typeof(Arguments args) {
4497 NoHandleAllocation ha;
4498
4499 Object* obj = args[0];
4500 if (obj->IsNumber()) return Heap::number_symbol();
4501 HeapObject* heap_obj = HeapObject::cast(obj);
4502
4503 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004504 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004505
4506 InstanceType instance_type = heap_obj->map()->instance_type();
4507 if (instance_type < FIRST_NONSTRING_TYPE) {
4508 return Heap::string_symbol();
4509 }
4510
4511 switch (instance_type) {
4512 case ODDBALL_TYPE:
4513 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
4514 return Heap::boolean_symbol();
4515 }
4516 if (heap_obj->IsNull()) {
4517 return Heap::object_symbol();
4518 }
4519 ASSERT(heap_obj->IsUndefined());
4520 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004521 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004522 return Heap::function_symbol();
4523 default:
4524 // For any kind of object not handled above, the spec rule for
4525 // host objects gives that it is okay to return "object"
4526 return Heap::object_symbol();
4527 }
4528}
4529
4530
lrn@chromium.org25156de2010-04-06 13:10:27 +00004531static bool AreDigits(const char*s, int from, int to) {
4532 for (int i = from; i < to; i++) {
4533 if (s[i] < '0' || s[i] > '9') return false;
4534 }
4535
4536 return true;
4537}
4538
4539
4540static int ParseDecimalInteger(const char*s, int from, int to) {
4541 ASSERT(to - from < 10); // Overflow is not possible.
4542 ASSERT(from < to);
4543 int d = s[from] - '0';
4544
4545 for (int i = from + 1; i < to; i++) {
4546 d = 10 * d + (s[i] - '0');
4547 }
4548
4549 return d;
4550}
4551
4552
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004553static Object* Runtime_StringToNumber(Arguments args) {
4554 NoHandleAllocation ha;
4555 ASSERT(args.length() == 1);
4556 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004557 subject->TryFlatten();
lrn@chromium.org25156de2010-04-06 13:10:27 +00004558
4559 // Fast case: short integer or some sorts of junk values.
4560 int len = subject->length();
4561 if (subject->IsSeqAsciiString()) {
4562 if (len == 0) return Smi::FromInt(0);
4563
4564 char const* data = SeqAsciiString::cast(subject)->GetChars();
4565 bool minus = (data[0] == '-');
4566 int start_pos = (minus ? 1 : 0);
4567
4568 if (start_pos == len) {
4569 return Heap::nan_value();
4570 } else if (data[start_pos] > '9') {
4571 // Fast check for a junk value. A valid string may start from a
4572 // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit or
4573 // the 'I' character ('Infinity'). All of that have codes not greater than
4574 // '9' except 'I'.
4575 if (data[start_pos] != 'I') {
4576 return Heap::nan_value();
4577 }
4578 } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
4579 // The maximal/minimal smi has 10 digits. If the string has less digits we
4580 // know it will fit into the smi-data type.
4581 int d = ParseDecimalInteger(data, start_pos, len);
4582 if (minus) {
4583 if (d == 0) return Heap::minus_zero_value();
4584 d = -d;
4585 }
4586 return Smi::FromInt(d);
4587 }
4588 }
4589
4590 // Slower case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004591 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
4592}
4593
4594
4595static Object* Runtime_StringFromCharCodeArray(Arguments args) {
4596 NoHandleAllocation ha;
4597 ASSERT(args.length() == 1);
4598
4599 CONVERT_CHECKED(JSArray, codes, args[0]);
4600 int length = Smi::cast(codes->length())->value();
4601
4602 // Check if the string can be ASCII.
4603 int i;
4604 for (i = 0; i < length; i++) {
4605 Object* element = codes->GetElement(i);
4606 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
4607 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
4608 break;
4609 }
4610
4611 Object* object = NULL;
4612 if (i == length) { // The string is ASCII.
4613 object = Heap::AllocateRawAsciiString(length);
4614 } else { // The string is not ASCII.
4615 object = Heap::AllocateRawTwoByteString(length);
4616 }
4617
4618 if (object->IsFailure()) return object;
4619 String* result = String::cast(object);
4620 for (int i = 0; i < length; i++) {
4621 Object* element = codes->GetElement(i);
4622 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004623 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004624 }
4625 return result;
4626}
4627
4628
4629// kNotEscaped is generated by the following:
4630//
4631// #!/bin/perl
4632// for (my $i = 0; $i < 256; $i++) {
4633// print "\n" if $i % 16 == 0;
4634// my $c = chr($i);
4635// my $escaped = 1;
4636// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
4637// print $escaped ? "0, " : "1, ";
4638// }
4639
4640
4641static bool IsNotEscaped(uint16_t character) {
4642 // Only for 8 bit characters, the rest are always escaped (in a different way)
4643 ASSERT(character < 256);
4644 static const char kNotEscaped[256] = {
4645 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4646 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4647 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
4648 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
4649 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4650 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
4651 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4652 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
4653 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4654 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4655 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4656 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4657 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4658 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4659 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4660 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4661 };
4662 return kNotEscaped[character] != 0;
4663}
4664
4665
4666static Object* Runtime_URIEscape(Arguments args) {
4667 const char hex_chars[] = "0123456789ABCDEF";
4668 NoHandleAllocation ha;
4669 ASSERT(args.length() == 1);
4670 CONVERT_CHECKED(String, source, args[0]);
4671
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004672 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004673
4674 int escaped_length = 0;
4675 int length = source->length();
4676 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004677 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004678 buffer->Reset(source);
4679 while (buffer->has_more()) {
4680 uint16_t character = buffer->GetNext();
4681 if (character >= 256) {
4682 escaped_length += 6;
4683 } else if (IsNotEscaped(character)) {
4684 escaped_length++;
4685 } else {
4686 escaped_length += 3;
4687 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004688 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004689 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004690 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004691 Top::context()->mark_out_of_memory();
4692 return Failure::OutOfMemoryException();
4693 }
4694 }
4695 }
4696 // No length change implies no change. Return original string if no change.
4697 if (escaped_length == length) {
4698 return source;
4699 }
4700 Object* o = Heap::AllocateRawAsciiString(escaped_length);
4701 if (o->IsFailure()) return o;
4702 String* destination = String::cast(o);
4703 int dest_position = 0;
4704
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004705 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004706 buffer->Rewind();
4707 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00004708 uint16_t chr = buffer->GetNext();
4709 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004710 destination->Set(dest_position, '%');
4711 destination->Set(dest_position+1, 'u');
4712 destination->Set(dest_position+2, hex_chars[chr >> 12]);
4713 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
4714 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
4715 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004716 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00004717 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004718 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004719 dest_position++;
4720 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004721 destination->Set(dest_position, '%');
4722 destination->Set(dest_position+1, hex_chars[chr >> 4]);
4723 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004724 dest_position += 3;
4725 }
4726 }
4727 return destination;
4728}
4729
4730
4731static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
4732 static const signed char kHexValue['g'] = {
4733 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4734 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4735 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4736 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
4737 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4738 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4739 -1, 10, 11, 12, 13, 14, 15 };
4740
4741 if (character1 > 'f') return -1;
4742 int hi = kHexValue[character1];
4743 if (hi == -1) return -1;
4744 if (character2 > 'f') return -1;
4745 int lo = kHexValue[character2];
4746 if (lo == -1) return -1;
4747 return (hi << 4) + lo;
4748}
4749
4750
ager@chromium.org870a0b62008-11-04 11:43:05 +00004751static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004752 int i,
4753 int length,
4754 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004755 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00004756 int32_t hi = 0;
4757 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004758 if (character == '%' &&
4759 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004760 source->Get(i + 1) == 'u' &&
4761 (hi = TwoDigitHex(source->Get(i + 2),
4762 source->Get(i + 3))) != -1 &&
4763 (lo = TwoDigitHex(source->Get(i + 4),
4764 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004765 *step = 6;
4766 return (hi << 8) + lo;
4767 } else if (character == '%' &&
4768 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004769 (lo = TwoDigitHex(source->Get(i + 1),
4770 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004771 *step = 3;
4772 return lo;
4773 } else {
4774 *step = 1;
4775 return character;
4776 }
4777}
4778
4779
4780static Object* Runtime_URIUnescape(Arguments args) {
4781 NoHandleAllocation ha;
4782 ASSERT(args.length() == 1);
4783 CONVERT_CHECKED(String, source, args[0]);
4784
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004785 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004786
4787 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004788 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004789
4790 int unescaped_length = 0;
4791 for (int i = 0; i < length; unescaped_length++) {
4792 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004793 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004794 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004795 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004796 i += step;
4797 }
4798
4799 // No length change implies no change. Return original string if no change.
4800 if (unescaped_length == length)
4801 return source;
4802
4803 Object* o = ascii ?
4804 Heap::AllocateRawAsciiString(unescaped_length) :
4805 Heap::AllocateRawTwoByteString(unescaped_length);
4806 if (o->IsFailure()) return o;
4807 String* destination = String::cast(o);
4808
4809 int dest_position = 0;
4810 for (int i = 0; i < length; dest_position++) {
4811 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004812 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004813 i += step;
4814 }
4815 return destination;
4816}
4817
4818
4819static Object* Runtime_StringParseInt(Arguments args) {
4820 NoHandleAllocation ha;
4821
4822 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004823 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004824
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004825 s->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004826
lrn@chromium.org25156de2010-04-06 13:10:27 +00004827 RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
4828 double value = StringToInt(s, radix);
4829 return Heap::NumberFromDouble(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004830 return Heap::nan_value();
4831}
4832
4833
4834static Object* Runtime_StringParseFloat(Arguments args) {
4835 NoHandleAllocation ha;
4836 CONVERT_CHECKED(String, str, args[0]);
4837
4838 // ECMA-262 section 15.1.2.3, empty string is NaN
4839 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
4840
4841 // Create a number object from the value.
4842 return Heap::NumberFromDouble(value);
4843}
4844
4845
4846static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
4847static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
4848
4849
4850template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004851static Object* ConvertCaseHelper(String* s,
4852 int length,
4853 int input_string_length,
4854 unibrow::Mapping<Converter, 128>* mapping) {
4855 // We try this twice, once with the assumption that the result is no longer
4856 // than the input and, if that assumption breaks, again with the exact
4857 // length. This may not be pretty, but it is nicer than what was here before
4858 // and I hereby claim my vaffel-is.
4859 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004860 // Allocate the resulting string.
4861 //
4862 // NOTE: This assumes that the upper/lower case of an ascii
4863 // character is also ascii. This is currently the case, but it
4864 // might break in the future if we implement more context and locale
4865 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00004866 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004867 ? Heap::AllocateRawAsciiString(length)
4868 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004869 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004870 String* result = String::cast(o);
4871 bool has_changed_character = false;
4872
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004873 // Convert all characters to upper case, assuming that they will fit
4874 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004875 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004876 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004877 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004878 // We can assume that the string is not empty
4879 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004880 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00004881 bool has_next = buffer->has_more();
4882 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004883 int char_length = mapping->get(current, next, chars);
4884 if (char_length == 0) {
4885 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004886 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004887 i++;
4888 } else if (char_length == 1) {
4889 // Common case: converting the letter resulted in one character.
4890 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004891 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004892 has_changed_character = true;
4893 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004894 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004895 // We've assumed that the result would be as long as the
4896 // input but here is a character that converts to several
4897 // characters. No matter, we calculate the exact length
4898 // of the result and try the whole thing again.
4899 //
4900 // Note that this leaves room for optimization. We could just
4901 // memcpy what we already have to the result string. Also,
4902 // the result string is the last object allocated we could
4903 // "realloc" it and probably, in the vast majority of cases,
4904 // extend the existing string to be able to hold the full
4905 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00004906 int next_length = 0;
4907 if (has_next) {
4908 next_length = mapping->get(next, 0, chars);
4909 if (next_length == 0) next_length = 1;
4910 }
4911 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004912 while (buffer->has_more()) {
4913 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00004914 // NOTE: we use 0 as the next character here because, while
4915 // the next character may affect what a character converts to,
4916 // it does not in any case affect the length of what it convert
4917 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004918 int char_length = mapping->get(current, 0, chars);
4919 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00004920 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004921 if (current_length > Smi::kMaxValue) {
4922 Top::context()->mark_out_of_memory();
4923 return Failure::OutOfMemoryException();
4924 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004925 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004926 // Try again with the real length.
4927 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004928 } else {
4929 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004930 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004931 i++;
4932 }
4933 has_changed_character = true;
4934 }
4935 current = next;
4936 }
4937 if (has_changed_character) {
4938 return result;
4939 } else {
4940 // If we didn't actually change anything in doing the conversion
4941 // we simple return the result and let the converted string
4942 // become garbage; there is no reason to keep two identical strings
4943 // alive.
4944 return s;
4945 }
4946}
4947
4948
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004949static inline SeqAsciiString* TryGetSeqAsciiString(String* s) {
4950 if (!s->IsFlat() || !s->IsAsciiRepresentation()) return NULL;
4951 if (s->IsConsString()) {
4952 ASSERT(ConsString::cast(s)->second()->length() == 0);
4953 return SeqAsciiString::cast(ConsString::cast(s)->first());
4954 }
4955 return SeqAsciiString::cast(s);
4956}
4957
4958
4959namespace {
4960
4961struct ToLowerTraits {
4962 typedef unibrow::ToLowercase UnibrowConverter;
4963
4964 static bool ConvertAscii(char* dst, char* src, int length) {
4965 bool changed = false;
4966 for (int i = 0; i < length; ++i) {
4967 char c = src[i];
4968 if ('A' <= c && c <= 'Z') {
4969 c += ('a' - 'A');
4970 changed = true;
4971 }
4972 dst[i] = c;
4973 }
4974 return changed;
4975 }
4976};
4977
4978
4979struct ToUpperTraits {
4980 typedef unibrow::ToUppercase UnibrowConverter;
4981
4982 static bool ConvertAscii(char* dst, char* src, int length) {
4983 bool changed = false;
4984 for (int i = 0; i < length; ++i) {
4985 char c = src[i];
4986 if ('a' <= c && c <= 'z') {
4987 c -= ('a' - 'A');
4988 changed = true;
4989 }
4990 dst[i] = c;
4991 }
4992 return changed;
4993 }
4994};
4995
4996} // namespace
4997
4998
4999template <typename ConvertTraits>
5000static Object* ConvertCase(
5001 Arguments args,
5002 unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005003 NoHandleAllocation ha;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005004 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005005 s->TryFlatten();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005006
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005007 const int length = s->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005008 // Assume that the string is not empty; we need this assumption later
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005009 if (length == 0) return s;
5010
5011 // Simpler handling of ascii strings.
5012 //
5013 // NOTE: This assumes that the upper/lower case of an ascii
5014 // character is also ascii. This is currently the case, but it
5015 // might break in the future if we implement more context and locale
5016 // dependent upper/lower conversions.
5017 SeqAsciiString* seq_ascii = TryGetSeqAsciiString(s);
5018 if (seq_ascii != NULL) {
5019 Object* o = Heap::AllocateRawAsciiString(length);
5020 if (o->IsFailure()) return o;
5021 SeqAsciiString* result = SeqAsciiString::cast(o);
5022 bool has_changed_character = ConvertTraits::ConvertAscii(
5023 result->GetChars(), seq_ascii->GetChars(), length);
5024 return has_changed_character ? result : s;
5025 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005026
5027 Object* answer = ConvertCaseHelper(s, length, length, mapping);
5028 if (answer->IsSmi()) {
5029 // Retry with correct length.
5030 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
5031 }
5032 return answer; // This may be a failure.
5033}
5034
5035
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005036static Object* Runtime_StringToLowerCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005037 return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005038}
5039
5040
5041static Object* Runtime_StringToUpperCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005042 return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005043}
5044
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005045
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005046static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
5047 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
5048}
5049
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005050
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005051static Object* Runtime_StringTrim(Arguments args) {
5052 NoHandleAllocation ha;
5053 ASSERT(args.length() == 3);
5054
5055 CONVERT_CHECKED(String, s, args[0]);
5056 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
5057 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
5058
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005059 s->TryFlatten();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005060 int length = s->length();
5061
5062 int left = 0;
5063 if (trimLeft) {
5064 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
5065 left++;
5066 }
5067 }
5068
5069 int right = length;
5070 if (trimRight) {
5071 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
5072 right--;
5073 }
5074 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005075 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005076}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005077
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005078
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005079template <typename schar, typename pchar>
5080void FindStringIndices(Vector<const schar> subject,
5081 Vector<const pchar> pattern,
5082 ZoneList<int>* indices,
5083 unsigned int limit) {
5084 ASSERT(limit > 0);
5085 // Collect indices of pattern in subject, and the end-of-string index.
5086 // Stop after finding at most limit values.
5087 StringSearchStrategy strategy =
5088 InitializeStringSearch(pattern, sizeof(schar) == 1);
5089 switch (strategy) {
5090 case SEARCH_FAIL: return;
5091 case SEARCH_SHORT: {
5092 int pattern_length = pattern.length();
5093 int index = 0;
5094 while (limit > 0) {
5095 index = SimpleIndexOf(subject, pattern, index);
5096 if (index < 0) return;
5097 indices->Add(index);
5098 index += pattern_length;
5099 limit--;
5100 }
5101 return;
5102 }
5103 case SEARCH_LONG: {
5104 int pattern_length = pattern.length();
5105 int index = 0;
5106 while (limit > 0) {
5107 index = ComplexIndexOf(subject, pattern, index);
5108 if (index < 0) return;
5109 indices->Add(index);
5110 index += pattern_length;
5111 limit--;
5112 }
5113 return;
5114 }
5115 default:
5116 UNREACHABLE();
5117 return;
5118 }
5119}
5120
5121template <typename schar>
5122inline void FindCharIndices(Vector<const schar> subject,
5123 const schar pattern_char,
5124 ZoneList<int>* indices,
5125 unsigned int limit) {
5126 // Collect indices of pattern_char in subject, and the end-of-string index.
5127 // Stop after finding at most limit values.
5128 int index = 0;
5129 while (limit > 0) {
5130 index = SingleCharIndexOf(subject, pattern_char, index);
5131 if (index < 0) return;
5132 indices->Add(index);
5133 index++;
5134 limit--;
5135 }
5136}
5137
5138
5139static Object* Runtime_StringSplit(Arguments args) {
5140 ASSERT(args.length() == 3);
5141 HandleScope handle_scope;
5142 CONVERT_ARG_CHECKED(String, subject, 0);
5143 CONVERT_ARG_CHECKED(String, pattern, 1);
5144 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
5145
5146 int subject_length = subject->length();
5147 int pattern_length = pattern->length();
5148 RUNTIME_ASSERT(pattern_length > 0);
5149
5150 // The limit can be very large (0xffffffffu), but since the pattern
5151 // isn't empty, we can never create more parts than ~half the length
5152 // of the subject.
5153
5154 if (!subject->IsFlat()) FlattenString(subject);
5155
5156 static const int kMaxInitialListCapacity = 16;
5157
5158 ZoneScope scope(DELETE_ON_EXIT);
5159
5160 // Find (up to limit) indices of separator and end-of-string in subject
5161 int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
5162 ZoneList<int> indices(initial_capacity);
5163 if (pattern_length == 1) {
5164 // Special case, go directly to fast single-character split.
5165 AssertNoAllocation nogc;
5166 uc16 pattern_char = pattern->Get(0);
5167 if (subject->IsTwoByteRepresentation()) {
5168 FindCharIndices(subject->ToUC16Vector(), pattern_char,
5169 &indices,
5170 limit);
5171 } else if (pattern_char <= String::kMaxAsciiCharCode) {
5172 FindCharIndices(subject->ToAsciiVector(),
5173 static_cast<char>(pattern_char),
5174 &indices,
5175 limit);
5176 }
5177 } else {
5178 if (!pattern->IsFlat()) FlattenString(pattern);
5179 AssertNoAllocation nogc;
5180 if (subject->IsAsciiRepresentation()) {
5181 Vector<const char> subject_vector = subject->ToAsciiVector();
5182 if (pattern->IsAsciiRepresentation()) {
5183 FindStringIndices(subject_vector,
5184 pattern->ToAsciiVector(),
5185 &indices,
5186 limit);
5187 } else {
5188 FindStringIndices(subject_vector,
5189 pattern->ToUC16Vector(),
5190 &indices,
5191 limit);
5192 }
5193 } else {
5194 Vector<const uc16> subject_vector = subject->ToUC16Vector();
5195 if (pattern->IsAsciiRepresentation()) {
5196 FindStringIndices(subject_vector,
5197 pattern->ToAsciiVector(),
5198 &indices,
5199 limit);
5200 } else {
5201 FindStringIndices(subject_vector,
5202 pattern->ToUC16Vector(),
5203 &indices,
5204 limit);
5205 }
5206 }
5207 }
5208 if (static_cast<uint32_t>(indices.length()) < limit) {
5209 indices.Add(subject_length);
5210 }
5211 // The list indices now contains the end of each part to create.
5212
5213
5214 // Create JSArray of substrings separated by separator.
5215 int part_count = indices.length();
5216
5217 Handle<JSArray> result = Factory::NewJSArray(part_count);
5218 result->set_length(Smi::FromInt(part_count));
5219
5220 ASSERT(result->HasFastElements());
5221
5222 if (part_count == 1 && indices.at(0) == subject_length) {
5223 FixedArray::cast(result->elements())->set(0, *subject);
5224 return *result;
5225 }
5226
5227 Handle<FixedArray> elements(FixedArray::cast(result->elements()));
5228 int part_start = 0;
5229 for (int i = 0; i < part_count; i++) {
5230 HandleScope local_loop_handle;
5231 int part_end = indices.at(i);
5232 Handle<String> substring =
5233 Factory::NewSubString(subject, part_start, part_end);
5234 elements->set(i, *substring);
5235 part_start = part_end + pattern_length;
5236 }
5237
5238 return *result;
5239}
5240
5241
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005242// Copies ascii characters to the given fixed array looking up
5243// one-char strings in the cache. Gives up on the first char that is
5244// not in the cache and fills the remainder with smi zeros. Returns
5245// the length of the successfully copied prefix.
5246static int CopyCachedAsciiCharsToArray(const char* chars,
5247 FixedArray* elements,
5248 int length) {
5249 AssertNoAllocation nogc;
5250 FixedArray* ascii_cache = Heap::single_character_string_cache();
5251 Object* undefined = Heap::undefined_value();
5252 int i;
5253 for (i = 0; i < length; ++i) {
5254 Object* value = ascii_cache->get(chars[i]);
5255 if (value == undefined) break;
5256 ASSERT(!Heap::InNewSpace(value));
5257 elements->set(i, value, SKIP_WRITE_BARRIER);
5258 }
5259 if (i < length) {
5260 ASSERT(Smi::FromInt(0) == 0);
5261 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
5262 }
5263#ifdef DEBUG
5264 for (int j = 0; j < length; ++j) {
5265 Object* element = elements->get(j);
5266 ASSERT(element == Smi::FromInt(0) ||
5267 (element->IsString() && String::cast(element)->LooksValid()));
5268 }
5269#endif
5270 return i;
5271}
5272
5273
5274// Converts a String to JSArray.
5275// For example, "foo" => ["f", "o", "o"].
5276static Object* Runtime_StringToArray(Arguments args) {
5277 HandleScope scope;
5278 ASSERT(args.length() == 1);
5279 CONVERT_ARG_CHECKED(String, s, 0);
5280
5281 s->TryFlatten();
5282 const int length = s->length();
5283
5284 Handle<FixedArray> elements;
5285 if (s->IsFlat() && s->IsAsciiRepresentation()) {
5286 Object* obj = Heap::AllocateUninitializedFixedArray(length);
5287 if (obj->IsFailure()) return obj;
5288 elements = Handle<FixedArray>(FixedArray::cast(obj));
5289
5290 Vector<const char> chars = s->ToAsciiVector();
5291 // Note, this will initialize all elements (not only the prefix)
5292 // to prevent GC from seeing partially initialized array.
5293 int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
5294 *elements,
5295 length);
5296
5297 for (int i = num_copied_from_cache; i < length; ++i) {
5298 elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
5299 }
5300 } else {
5301 elements = Factory::NewFixedArray(length);
5302 for (int i = 0; i < length; ++i) {
5303 elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
5304 }
5305 }
5306
5307#ifdef DEBUG
5308 for (int i = 0; i < length; ++i) {
5309 ASSERT(String::cast(elements->get(i))->length() == 1);
5310 }
5311#endif
5312
5313 return *Factory::NewJSArrayWithElements(elements);
5314}
5315
5316
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00005317bool Runtime::IsUpperCaseChar(uint16_t ch) {
5318 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
5319 int char_length = to_upper_mapping.get(ch, 0, chars);
5320 return char_length == 0;
5321}
5322
5323
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005324static Object* Runtime_NumberToString(Arguments args) {
5325 NoHandleAllocation ha;
5326 ASSERT(args.length() == 1);
5327
5328 Object* number = args[0];
5329 RUNTIME_ASSERT(number->IsNumber());
5330
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005331 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005332}
5333
5334
ager@chromium.org357bf652010-04-12 11:30:10 +00005335static Object* Runtime_NumberToStringSkipCache(Arguments args) {
5336 NoHandleAllocation ha;
5337 ASSERT(args.length() == 1);
5338
5339 Object* number = args[0];
5340 RUNTIME_ASSERT(number->IsNumber());
5341
5342 return Heap::NumberToString(number, false);
5343}
5344
5345
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005346static Object* Runtime_NumberToInteger(Arguments args) {
5347 NoHandleAllocation ha;
5348 ASSERT(args.length() == 1);
5349
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005350 CONVERT_DOUBLE_CHECKED(number, args[0]);
5351
5352 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5353 if (number > 0 && number <= Smi::kMaxValue) {
5354 return Smi::FromInt(static_cast<int>(number));
5355 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005356 return Heap::NumberFromDouble(DoubleToInteger(number));
5357}
5358
5359
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005360
5361
5362
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005363static Object* Runtime_NumberToIntegerMapMinusZero(Arguments args) {
5364 NoHandleAllocation ha;
5365 ASSERT(args.length() == 1);
5366
5367 CONVERT_DOUBLE_CHECKED(number, args[0]);
5368
5369 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5370 if (number > 0 && number <= Smi::kMaxValue) {
5371 return Smi::FromInt(static_cast<int>(number));
5372 }
5373
5374 double double_value = DoubleToInteger(number);
5375 // Map both -0 and +0 to +0.
5376 if (double_value == 0) double_value = 0;
5377
5378 return Heap::NumberFromDouble(double_value);
5379}
5380
5381
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005382static Object* Runtime_NumberToJSUint32(Arguments args) {
5383 NoHandleAllocation ha;
5384 ASSERT(args.length() == 1);
5385
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005386 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005387 return Heap::NumberFromUint32(number);
5388}
5389
5390
5391static Object* Runtime_NumberToJSInt32(Arguments args) {
5392 NoHandleAllocation ha;
5393 ASSERT(args.length() == 1);
5394
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005395 CONVERT_DOUBLE_CHECKED(number, args[0]);
5396
5397 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5398 if (number > 0 && number <= Smi::kMaxValue) {
5399 return Smi::FromInt(static_cast<int>(number));
5400 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005401 return Heap::NumberFromInt32(DoubleToInt32(number));
5402}
5403
5404
ager@chromium.org870a0b62008-11-04 11:43:05 +00005405// Converts a Number to a Smi, if possible. Returns NaN if the number is not
5406// a small integer.
5407static Object* Runtime_NumberToSmi(Arguments args) {
5408 NoHandleAllocation ha;
5409 ASSERT(args.length() == 1);
5410
5411 Object* obj = args[0];
5412 if (obj->IsSmi()) {
5413 return obj;
5414 }
5415 if (obj->IsHeapNumber()) {
5416 double value = HeapNumber::cast(obj)->value();
5417 int int_value = FastD2I(value);
5418 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
5419 return Smi::FromInt(int_value);
5420 }
5421 }
5422 return Heap::nan_value();
5423}
5424
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005425
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005426static Object* Runtime_NumberAdd(Arguments args) {
5427 NoHandleAllocation ha;
5428 ASSERT(args.length() == 2);
5429
5430 CONVERT_DOUBLE_CHECKED(x, args[0]);
5431 CONVERT_DOUBLE_CHECKED(y, args[1]);
5432 return Heap::AllocateHeapNumber(x + y);
5433}
5434
5435
5436static Object* Runtime_NumberSub(Arguments args) {
5437 NoHandleAllocation ha;
5438 ASSERT(args.length() == 2);
5439
5440 CONVERT_DOUBLE_CHECKED(x, args[0]);
5441 CONVERT_DOUBLE_CHECKED(y, args[1]);
5442 return Heap::AllocateHeapNumber(x - y);
5443}
5444
5445
5446static Object* Runtime_NumberMul(Arguments args) {
5447 NoHandleAllocation ha;
5448 ASSERT(args.length() == 2);
5449
5450 CONVERT_DOUBLE_CHECKED(x, args[0]);
5451 CONVERT_DOUBLE_CHECKED(y, args[1]);
5452 return Heap::AllocateHeapNumber(x * y);
5453}
5454
5455
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005456static Object* Runtime_NumberUnaryMinus(Arguments args) {
5457 NoHandleAllocation ha;
5458 ASSERT(args.length() == 1);
5459
5460 CONVERT_DOUBLE_CHECKED(x, args[0]);
5461 return Heap::AllocateHeapNumber(-x);
5462}
5463
5464
5465static Object* Runtime_NumberDiv(Arguments args) {
5466 NoHandleAllocation ha;
5467 ASSERT(args.length() == 2);
5468
5469 CONVERT_DOUBLE_CHECKED(x, args[0]);
5470 CONVERT_DOUBLE_CHECKED(y, args[1]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005471 return Heap::NumberFromDouble(x / y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005472}
5473
5474
5475static Object* Runtime_NumberMod(Arguments args) {
5476 NoHandleAllocation ha;
5477 ASSERT(args.length() == 2);
5478
5479 CONVERT_DOUBLE_CHECKED(x, args[0]);
5480 CONVERT_DOUBLE_CHECKED(y, args[1]);
5481
ager@chromium.org3811b432009-10-28 14:53:37 +00005482 x = modulo(x, y);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005483 // NumberFromDouble may return a Smi instead of a Number object
5484 return Heap::NumberFromDouble(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005485}
5486
5487
5488static Object* Runtime_StringAdd(Arguments args) {
5489 NoHandleAllocation ha;
5490 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005491 CONVERT_CHECKED(String, str1, args[0]);
5492 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005493 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00005494 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005495}
5496
5497
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005498template <typename sinkchar>
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005499static inline void StringBuilderConcatHelper(String* special,
5500 sinkchar* sink,
5501 FixedArray* fixed_array,
5502 int array_length) {
5503 int position = 0;
5504 for (int i = 0; i < array_length; i++) {
5505 Object* element = fixed_array->get(i);
5506 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005507 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005508 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005509 int pos;
5510 int len;
5511 if (encoded_slice > 0) {
5512 // Position and length encoded in one smi.
5513 pos = StringBuilderSubstringPosition::decode(encoded_slice);
5514 len = StringBuilderSubstringLength::decode(encoded_slice);
5515 } else {
5516 // Position and length encoded in two smis.
5517 Object* obj = fixed_array->get(++i);
5518 ASSERT(obj->IsSmi());
5519 pos = Smi::cast(obj)->value();
5520 len = -encoded_slice;
5521 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00005522 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00005523 sink + position,
5524 pos,
5525 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005526 position += len;
5527 } else {
5528 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005529 int element_length = string->length();
5530 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005531 position += element_length;
5532 }
5533 }
5534}
5535
5536
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005537static Object* Runtime_StringBuilderConcat(Arguments args) {
5538 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005539 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005540 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005541 if (!args[1]->IsSmi()) {
5542 Top::context()->mark_out_of_memory();
5543 return Failure::OutOfMemoryException();
5544 }
5545 int array_length = Smi::cast(args[1])->value();
5546 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005547
5548 // This assumption is used by the slice encoding in one or two smis.
5549 ASSERT(Smi::kMaxValue >= String::kMaxLength);
5550
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005551 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005552 if (!array->HasFastElements()) {
5553 return Top::Throw(Heap::illegal_argument_symbol());
5554 }
5555 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005556 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005557 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005558 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005559
5560 if (array_length == 0) {
5561 return Heap::empty_string();
5562 } else if (array_length == 1) {
5563 Object* first = fixed_array->get(0);
5564 if (first->IsString()) return first;
5565 }
5566
ager@chromium.org5ec48922009-05-05 07:25:34 +00005567 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005568 int position = 0;
5569 for (int i = 0; i < array_length; i++) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005570 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005571 Object* elt = fixed_array->get(i);
5572 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005573 // Smi encoding of position and length.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005574 int smi_value = Smi::cast(elt)->value();
5575 int pos;
5576 int len;
5577 if (smi_value > 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005578 // Position and length encoded in one smi.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005579 pos = StringBuilderSubstringPosition::decode(smi_value);
5580 len = StringBuilderSubstringLength::decode(smi_value);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005581 } else {
5582 // Position and length encoded in two smis.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005583 len = -smi_value;
5584 // Get the position and check that it is a positive smi.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005585 i++;
5586 if (i >= array_length) {
5587 return Top::Throw(Heap::illegal_argument_symbol());
5588 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005589 Object* next_smi = fixed_array->get(i);
5590 if (!next_smi->IsSmi()) {
5591 return Top::Throw(Heap::illegal_argument_symbol());
5592 }
5593 pos = Smi::cast(next_smi)->value();
5594 if (pos < 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005595 return Top::Throw(Heap::illegal_argument_symbol());
5596 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005597 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005598 ASSERT(pos >= 0);
5599 ASSERT(len >= 0);
5600 if (pos > special_length || len > special_length - pos) {
5601 return Top::Throw(Heap::illegal_argument_symbol());
5602 }
5603 increment = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005604 } else if (elt->IsString()) {
5605 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005606 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005607 increment = element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005608 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005609 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005610 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005611 } else {
5612 return Top::Throw(Heap::illegal_argument_symbol());
5613 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005614 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005615 Top::context()->mark_out_of_memory();
5616 return Failure::OutOfMemoryException();
5617 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005618 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005619 }
5620
5621 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005622 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005623
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005624 if (ascii) {
5625 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005626 if (object->IsFailure()) return object;
5627 SeqAsciiString* answer = SeqAsciiString::cast(object);
5628 StringBuilderConcatHelper(special,
5629 answer->GetChars(),
5630 fixed_array,
5631 array_length);
5632 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005633 } else {
5634 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005635 if (object->IsFailure()) return object;
5636 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
5637 StringBuilderConcatHelper(special,
5638 answer->GetChars(),
5639 fixed_array,
5640 array_length);
5641 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005642 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005643}
5644
5645
5646static Object* Runtime_NumberOr(Arguments args) {
5647 NoHandleAllocation ha;
5648 ASSERT(args.length() == 2);
5649
5650 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5651 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5652 return Heap::NumberFromInt32(x | y);
5653}
5654
5655
5656static Object* Runtime_NumberAnd(Arguments args) {
5657 NoHandleAllocation ha;
5658 ASSERT(args.length() == 2);
5659
5660 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5661 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5662 return Heap::NumberFromInt32(x & y);
5663}
5664
5665
5666static Object* Runtime_NumberXor(Arguments args) {
5667 NoHandleAllocation ha;
5668 ASSERT(args.length() == 2);
5669
5670 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5671 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5672 return Heap::NumberFromInt32(x ^ y);
5673}
5674
5675
5676static Object* Runtime_NumberNot(Arguments args) {
5677 NoHandleAllocation ha;
5678 ASSERT(args.length() == 1);
5679
5680 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5681 return Heap::NumberFromInt32(~x);
5682}
5683
5684
5685static Object* Runtime_NumberShl(Arguments args) {
5686 NoHandleAllocation ha;
5687 ASSERT(args.length() == 2);
5688
5689 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5690 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5691 return Heap::NumberFromInt32(x << (y & 0x1f));
5692}
5693
5694
5695static Object* Runtime_NumberShr(Arguments args) {
5696 NoHandleAllocation ha;
5697 ASSERT(args.length() == 2);
5698
5699 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
5700 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5701 return Heap::NumberFromUint32(x >> (y & 0x1f));
5702}
5703
5704
5705static Object* Runtime_NumberSar(Arguments args) {
5706 NoHandleAllocation ha;
5707 ASSERT(args.length() == 2);
5708
5709 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5710 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5711 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
5712}
5713
5714
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005715static Object* Runtime_NumberEquals(Arguments args) {
5716 NoHandleAllocation ha;
5717 ASSERT(args.length() == 2);
5718
5719 CONVERT_DOUBLE_CHECKED(x, args[0]);
5720 CONVERT_DOUBLE_CHECKED(y, args[1]);
5721 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
5722 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
5723 if (x == y) return Smi::FromInt(EQUAL);
5724 Object* result;
5725 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
5726 result = Smi::FromInt(EQUAL);
5727 } else {
5728 result = Smi::FromInt(NOT_EQUAL);
5729 }
5730 return result;
5731}
5732
5733
5734static Object* Runtime_StringEquals(Arguments args) {
5735 NoHandleAllocation ha;
5736 ASSERT(args.length() == 2);
5737
5738 CONVERT_CHECKED(String, x, args[0]);
5739 CONVERT_CHECKED(String, y, args[1]);
5740
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005741 bool not_equal = !x->Equals(y);
5742 // This is slightly convoluted because the value that signifies
5743 // equality is 0 and inequality is 1 so we have to negate the result
5744 // from String::Equals.
5745 ASSERT(not_equal == 0 || not_equal == 1);
5746 STATIC_CHECK(EQUAL == 0);
5747 STATIC_CHECK(NOT_EQUAL == 1);
5748 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005749}
5750
5751
5752static Object* Runtime_NumberCompare(Arguments args) {
5753 NoHandleAllocation ha;
5754 ASSERT(args.length() == 3);
5755
5756 CONVERT_DOUBLE_CHECKED(x, args[0]);
5757 CONVERT_DOUBLE_CHECKED(y, args[1]);
5758 if (isnan(x) || isnan(y)) return args[2];
5759 if (x == y) return Smi::FromInt(EQUAL);
5760 if (isless(x, y)) return Smi::FromInt(LESS);
5761 return Smi::FromInt(GREATER);
5762}
5763
5764
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005765// Compare two Smis as if they were converted to strings and then
5766// compared lexicographically.
5767static Object* Runtime_SmiLexicographicCompare(Arguments args) {
5768 NoHandleAllocation ha;
5769 ASSERT(args.length() == 2);
5770
5771 // Arrays for the individual characters of the two Smis. Smis are
5772 // 31 bit integers and 10 decimal digits are therefore enough.
5773 static int x_elms[10];
5774 static int y_elms[10];
5775
5776 // Extract the integer values from the Smis.
5777 CONVERT_CHECKED(Smi, x, args[0]);
5778 CONVERT_CHECKED(Smi, y, args[1]);
5779 int x_value = x->value();
5780 int y_value = y->value();
5781
5782 // If the integers are equal so are the string representations.
5783 if (x_value == y_value) return Smi::FromInt(EQUAL);
5784
5785 // If one of the integers are zero the normal integer order is the
5786 // same as the lexicographic order of the string representations.
5787 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
5788
ager@chromium.org32912102009-01-16 10:38:43 +00005789 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005790 // smallest because the char code of '-' is less than the char code
5791 // of any digit. Otherwise, we make both values positive.
5792 if (x_value < 0 || y_value < 0) {
5793 if (y_value >= 0) return Smi::FromInt(LESS);
5794 if (x_value >= 0) return Smi::FromInt(GREATER);
5795 x_value = -x_value;
5796 y_value = -y_value;
5797 }
5798
5799 // Convert the integers to arrays of their decimal digits.
5800 int x_index = 0;
5801 int y_index = 0;
5802 while (x_value > 0) {
5803 x_elms[x_index++] = x_value % 10;
5804 x_value /= 10;
5805 }
5806 while (y_value > 0) {
5807 y_elms[y_index++] = y_value % 10;
5808 y_value /= 10;
5809 }
5810
5811 // Loop through the arrays of decimal digits finding the first place
5812 // where they differ.
5813 while (--x_index >= 0 && --y_index >= 0) {
5814 int diff = x_elms[x_index] - y_elms[y_index];
5815 if (diff != 0) return Smi::FromInt(diff);
5816 }
5817
5818 // If one array is a suffix of the other array, the longest array is
5819 // the representation of the largest of the Smis in the
5820 // lexicographic ordering.
5821 return Smi::FromInt(x_index - y_index);
5822}
5823
5824
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005825static Object* StringInputBufferCompare(String* x, String* y) {
5826 static StringInputBuffer bufx;
5827 static StringInputBuffer bufy;
5828 bufx.Reset(x);
5829 bufy.Reset(y);
5830 while (bufx.has_more() && bufy.has_more()) {
5831 int d = bufx.GetNext() - bufy.GetNext();
5832 if (d < 0) return Smi::FromInt(LESS);
5833 else if (d > 0) return Smi::FromInt(GREATER);
5834 }
5835
5836 // x is (non-trivial) prefix of y:
5837 if (bufy.has_more()) return Smi::FromInt(LESS);
5838 // y is prefix of x:
5839 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
5840}
5841
5842
5843static Object* FlatStringCompare(String* x, String* y) {
5844 ASSERT(x->IsFlat());
5845 ASSERT(y->IsFlat());
5846 Object* equal_prefix_result = Smi::FromInt(EQUAL);
5847 int prefix_length = x->length();
5848 if (y->length() < prefix_length) {
5849 prefix_length = y->length();
5850 equal_prefix_result = Smi::FromInt(GREATER);
5851 } else if (y->length() > prefix_length) {
5852 equal_prefix_result = Smi::FromInt(LESS);
5853 }
5854 int r;
5855 if (x->IsAsciiRepresentation()) {
5856 Vector<const char> x_chars = x->ToAsciiVector();
5857 if (y->IsAsciiRepresentation()) {
5858 Vector<const char> y_chars = y->ToAsciiVector();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005859 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005860 } else {
5861 Vector<const uc16> y_chars = y->ToUC16Vector();
5862 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5863 }
5864 } else {
5865 Vector<const uc16> x_chars = x->ToUC16Vector();
5866 if (y->IsAsciiRepresentation()) {
5867 Vector<const char> y_chars = y->ToAsciiVector();
5868 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5869 } else {
5870 Vector<const uc16> y_chars = y->ToUC16Vector();
5871 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5872 }
5873 }
5874 Object* result;
5875 if (r == 0) {
5876 result = equal_prefix_result;
5877 } else {
5878 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
5879 }
5880 ASSERT(result == StringInputBufferCompare(x, y));
5881 return result;
5882}
5883
5884
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005885static Object* Runtime_StringCompare(Arguments args) {
5886 NoHandleAllocation ha;
5887 ASSERT(args.length() == 2);
5888
5889 CONVERT_CHECKED(String, x, args[0]);
5890 CONVERT_CHECKED(String, y, args[1]);
5891
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005892 Counters::string_compare_runtime.Increment();
5893
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005894 // A few fast case tests before we flatten.
5895 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005896 if (y->length() == 0) {
5897 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005898 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005899 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005900 return Smi::FromInt(LESS);
5901 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005902
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005903 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005904 if (d < 0) return Smi::FromInt(LESS);
5905 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005906
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005907 Object* obj = Heap::PrepareForCompare(x);
5908 if (obj->IsFailure()) return obj;
5909 obj = Heap::PrepareForCompare(y);
5910 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005911
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005912 return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
5913 : StringInputBufferCompare(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005914}
5915
5916
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005917static Object* Runtime_Math_acos(Arguments args) {
5918 NoHandleAllocation ha;
5919 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005920 Counters::math_acos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005921
5922 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005923 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005924}
5925
5926
5927static Object* Runtime_Math_asin(Arguments args) {
5928 NoHandleAllocation ha;
5929 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005930 Counters::math_asin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005931
5932 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005933 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005934}
5935
5936
5937static Object* Runtime_Math_atan(Arguments args) {
5938 NoHandleAllocation ha;
5939 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005940 Counters::math_atan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005941
5942 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005943 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005944}
5945
5946
5947static Object* Runtime_Math_atan2(Arguments args) {
5948 NoHandleAllocation ha;
5949 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005950 Counters::math_atan2.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005951
5952 CONVERT_DOUBLE_CHECKED(x, args[0]);
5953 CONVERT_DOUBLE_CHECKED(y, args[1]);
5954 double result;
5955 if (isinf(x) && isinf(y)) {
5956 // Make sure that the result in case of two infinite arguments
5957 // is a multiple of Pi / 4. The sign of the result is determined
5958 // by the first argument (x) and the sign of the second argument
5959 // determines the multiplier: one or three.
5960 static double kPiDividedBy4 = 0.78539816339744830962;
5961 int multiplier = (x < 0) ? -1 : 1;
5962 if (y < 0) multiplier *= 3;
5963 result = multiplier * kPiDividedBy4;
5964 } else {
5965 result = atan2(x, y);
5966 }
5967 return Heap::AllocateHeapNumber(result);
5968}
5969
5970
5971static Object* Runtime_Math_ceil(Arguments args) {
5972 NoHandleAllocation ha;
5973 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005974 Counters::math_ceil.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005975
5976 CONVERT_DOUBLE_CHECKED(x, args[0]);
5977 return Heap::NumberFromDouble(ceiling(x));
5978}
5979
5980
5981static Object* Runtime_Math_cos(Arguments args) {
5982 NoHandleAllocation ha;
5983 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005984 Counters::math_cos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005985
5986 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005987 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005988}
5989
5990
5991static Object* Runtime_Math_exp(Arguments args) {
5992 NoHandleAllocation ha;
5993 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005994 Counters::math_exp.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005995
5996 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005997 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005998}
5999
6000
6001static Object* Runtime_Math_floor(Arguments args) {
6002 NoHandleAllocation ha;
6003 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006004 Counters::math_floor.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006005
6006 CONVERT_DOUBLE_CHECKED(x, args[0]);
6007 return Heap::NumberFromDouble(floor(x));
6008}
6009
6010
6011static Object* Runtime_Math_log(Arguments args) {
6012 NoHandleAllocation ha;
6013 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006014 Counters::math_log.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006015
6016 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006017 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006018}
6019
6020
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006021// Helper function to compute x^y, where y is known to be an
6022// integer. Uses binary decomposition to limit the number of
6023// multiplications; see the discussion in "Hacker's Delight" by Henry
6024// S. Warren, Jr., figure 11-6, page 213.
6025static double powi(double x, int y) {
6026 ASSERT(y != kMinInt);
6027 unsigned n = (y < 0) ? -y : y;
6028 double m = x;
6029 double p = 1;
6030 while (true) {
6031 if ((n & 1) != 0) p *= m;
6032 n >>= 1;
6033 if (n == 0) {
6034 if (y < 0) {
6035 // Unfortunately, we have to be careful when p has reached
6036 // infinity in the computation, because sometimes the higher
6037 // internal precision in the pow() implementation would have
6038 // given us a finite p. This happens very rarely.
6039 double result = 1.0 / p;
6040 return (result == 0 && isinf(p))
6041 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
6042 : result;
6043 } else {
6044 return p;
6045 }
6046 }
6047 m *= m;
6048 }
6049}
6050
6051
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006052static Object* Runtime_Math_pow(Arguments args) {
6053 NoHandleAllocation ha;
6054 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006055 Counters::math_pow.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006056
6057 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006058
6059 // If the second argument is a smi, it is much faster to call the
6060 // custom powi() function than the generic pow().
6061 if (args[1]->IsSmi()) {
6062 int y = Smi::cast(args[1])->value();
6063 return Heap::AllocateHeapNumber(powi(x, y));
6064 }
6065
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006066 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00006067
6068 if (!isinf(x)) {
6069 if (y == 0.5) {
6070 // It's not uncommon to use Math.pow(x, 0.5) to compute the
6071 // square root of a number. To speed up such computations, we
6072 // explictly check for this case and use the sqrt() function
6073 // which is faster than pow().
6074 return Heap::AllocateHeapNumber(sqrt(x));
6075 } else if (y == -0.5) {
6076 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
6077 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
6078 }
6079 }
6080
6081 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006082 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006083 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6084 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006085 } else {
6086 return Heap::AllocateHeapNumber(pow(x, y));
6087 }
6088}
6089
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006090// Fast version of Math.pow if we know that y is not an integer and
6091// y is not -0.5 or 0.5. Used as slowcase from codegen.
6092static Object* Runtime_Math_pow_cfunction(Arguments args) {
6093 NoHandleAllocation ha;
6094 ASSERT(args.length() == 2);
6095 CONVERT_DOUBLE_CHECKED(x, args[0]);
6096 CONVERT_DOUBLE_CHECKED(y, args[1]);
6097 if (y == 0) {
6098 return Smi::FromInt(1);
6099 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6100 return Heap::nan_value();
6101 } else {
6102 return Heap::AllocateHeapNumber(pow(x, y));
6103 }
6104}
6105
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006106
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006107static Object* Runtime_RoundNumber(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006108 NoHandleAllocation ha;
6109 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006110 Counters::math_round.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006111
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006112 if (!args[0]->IsHeapNumber()) {
6113 // Must be smi. Return the argument unchanged for all the other types
6114 // to make fuzz-natives test happy.
6115 return args[0];
6116 }
6117
6118 HeapNumber* number = reinterpret_cast<HeapNumber*>(args[0]);
6119
6120 double value = number->value();
6121 int exponent = number->get_exponent();
6122 int sign = number->get_sign();
6123
6124 // We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
6125 // should be rounded to 2^30, which is not smi.
6126 if (!sign && exponent <= kSmiValueSize - 3) {
6127 return Smi::FromInt(static_cast<int>(value + 0.5));
6128 }
6129
6130 // If the magnitude is big enough, there's no place for fraction part. If we
6131 // try to add 0.5 to this number, 1.0 will be added instead.
6132 if (exponent >= 52) {
6133 return number;
6134 }
6135
6136 if (sign && value >= -0.5) return Heap::minus_zero_value();
6137
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006138 // Do not call NumberFromDouble() to avoid extra checks.
6139 return Heap::AllocateHeapNumber(floor(value + 0.5));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006140}
6141
6142
6143static Object* Runtime_Math_sin(Arguments args) {
6144 NoHandleAllocation ha;
6145 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006146 Counters::math_sin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006147
6148 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006149 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006150}
6151
6152
6153static Object* Runtime_Math_sqrt(Arguments args) {
6154 NoHandleAllocation ha;
6155 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006156 Counters::math_sqrt.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006157
6158 CONVERT_DOUBLE_CHECKED(x, args[0]);
6159 return Heap::AllocateHeapNumber(sqrt(x));
6160}
6161
6162
6163static Object* Runtime_Math_tan(Arguments args) {
6164 NoHandleAllocation ha;
6165 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006166 Counters::math_tan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006167
6168 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006169 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006170}
6171
6172
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006173static int MakeDay(int year, int month, int day) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006174 static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
6175 181, 212, 243, 273, 304, 334};
6176 static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
6177 182, 213, 244, 274, 305, 335};
6178
6179 year += month / 12;
6180 month %= 12;
6181 if (month < 0) {
6182 year--;
6183 month += 12;
6184 }
6185
6186 ASSERT(month >= 0);
6187 ASSERT(month < 12);
6188
6189 // year_delta is an arbitrary number such that:
6190 // a) year_delta = -1 (mod 400)
6191 // b) year + year_delta > 0 for years in the range defined by
6192 // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
6193 // Jan 1 1970. This is required so that we don't run into integer
6194 // division of negative numbers.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006195 // c) there shouldn't be an overflow for 32-bit integers in the following
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006196 // operations.
6197 static const int year_delta = 399999;
6198 static const int base_day = 365 * (1970 + year_delta) +
6199 (1970 + year_delta) / 4 -
6200 (1970 + year_delta) / 100 +
6201 (1970 + year_delta) / 400;
6202
6203 int year1 = year + year_delta;
6204 int day_from_year = 365 * year1 +
6205 year1 / 4 -
6206 year1 / 100 +
6207 year1 / 400 -
6208 base_day;
6209
6210 if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006211 return day_from_year + day_from_month[month] + day - 1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006212 }
6213
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006214 return day_from_year + day_from_month_leap[month] + day - 1;
6215}
6216
6217
6218static Object* Runtime_DateMakeDay(Arguments args) {
6219 NoHandleAllocation ha;
6220 ASSERT(args.length() == 3);
6221
6222 CONVERT_SMI_CHECKED(year, args[0]);
6223 CONVERT_SMI_CHECKED(month, args[1]);
6224 CONVERT_SMI_CHECKED(date, args[2]);
6225
6226 return Smi::FromInt(MakeDay(year, month, date));
6227}
6228
6229
6230static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
6231static const int kDaysIn4Years = 4 * 365 + 1;
6232static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
6233static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
6234static const int kDays1970to2000 = 30 * 365 + 7;
6235static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
6236 kDays1970to2000;
6237static const int kYearsOffset = 400000;
6238
6239static const char kDayInYear[] = {
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, 31,
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,
6244 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6245 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6246 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6247 22, 23, 24, 25, 26, 27, 28, 29, 30,
6248 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6249 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6250 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6251 22, 23, 24, 25, 26, 27, 28, 29, 30,
6252 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6253 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6254 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6255 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6256 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6257 22, 23, 24, 25, 26, 27, 28, 29, 30,
6258 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6259 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6260 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6261 22, 23, 24, 25, 26, 27, 28, 29, 30,
6262 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6263 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6264
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, 31,
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,
6269 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6270 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6271 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6272 22, 23, 24, 25, 26, 27, 28, 29, 30,
6273 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6274 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6275 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6276 22, 23, 24, 25, 26, 27, 28, 29, 30,
6277 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6278 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6279 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6280 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6281 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6282 22, 23, 24, 25, 26, 27, 28, 29, 30,
6283 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6284 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6285 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6286 22, 23, 24, 25, 26, 27, 28, 29, 30,
6287 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6288 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6289
6290 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6291 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6292 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6293 22, 23, 24, 25, 26, 27, 28, 29,
6294 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6295 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6296 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6297 22, 23, 24, 25, 26, 27, 28, 29, 30,
6298 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6299 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6300 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6301 22, 23, 24, 25, 26, 27, 28, 29, 30,
6302 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6303 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6304 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6305 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6306 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6307 22, 23, 24, 25, 26, 27, 28, 29, 30,
6308 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6309 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6310 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6311 22, 23, 24, 25, 26, 27, 28, 29, 30,
6312 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6313 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6314
6315 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6316 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6317 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6318 22, 23, 24, 25, 26, 27, 28,
6319 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6320 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6321 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6322 22, 23, 24, 25, 26, 27, 28, 29, 30,
6323 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6324 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6325 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6326 22, 23, 24, 25, 26, 27, 28, 29, 30,
6327 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6328 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6329 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6330 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6331 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6332 22, 23, 24, 25, 26, 27, 28, 29, 30,
6333 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6334 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6335 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6336 22, 23, 24, 25, 26, 27, 28, 29, 30,
6337 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6338 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
6339
6340static const char kMonthInYear[] = {
6341 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,
6342 0, 0, 0, 0, 0, 0,
6343 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,
6344 1, 1, 1,
6345 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,
6346 2, 2, 2, 2, 2, 2,
6347 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,
6348 3, 3, 3, 3, 3,
6349 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,
6350 4, 4, 4, 4, 4, 4,
6351 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,
6352 5, 5, 5, 5, 5,
6353 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,
6354 6, 6, 6, 6, 6, 6,
6355 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,
6356 7, 7, 7, 7, 7, 7,
6357 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,
6358 8, 8, 8, 8, 8,
6359 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,
6360 9, 9, 9, 9, 9, 9,
6361 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6362 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6363 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6364 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6365
6366 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,
6367 0, 0, 0, 0, 0, 0,
6368 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,
6369 1, 1, 1,
6370 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,
6371 2, 2, 2, 2, 2, 2,
6372 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,
6373 3, 3, 3, 3, 3,
6374 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,
6375 4, 4, 4, 4, 4, 4,
6376 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,
6377 5, 5, 5, 5, 5,
6378 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,
6379 6, 6, 6, 6, 6, 6,
6380 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,
6381 7, 7, 7, 7, 7, 7,
6382 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,
6383 8, 8, 8, 8, 8,
6384 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,
6385 9, 9, 9, 9, 9, 9,
6386 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6387 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6388 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6389 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6390
6391 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,
6392 0, 0, 0, 0, 0, 0,
6393 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,
6394 1, 1, 1, 1,
6395 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,
6396 2, 2, 2, 2, 2, 2,
6397 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,
6398 3, 3, 3, 3, 3,
6399 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,
6400 4, 4, 4, 4, 4, 4,
6401 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,
6402 5, 5, 5, 5, 5,
6403 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,
6404 6, 6, 6, 6, 6, 6,
6405 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,
6406 7, 7, 7, 7, 7, 7,
6407 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,
6408 8, 8, 8, 8, 8,
6409 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,
6410 9, 9, 9, 9, 9, 9,
6411 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6412 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6413 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6414 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6415
6416 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,
6417 0, 0, 0, 0, 0, 0,
6418 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,
6419 1, 1, 1,
6420 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,
6421 2, 2, 2, 2, 2, 2,
6422 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,
6423 3, 3, 3, 3, 3,
6424 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,
6425 4, 4, 4, 4, 4, 4,
6426 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,
6427 5, 5, 5, 5, 5,
6428 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,
6429 6, 6, 6, 6, 6, 6,
6430 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,
6431 7, 7, 7, 7, 7, 7,
6432 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,
6433 8, 8, 8, 8, 8,
6434 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,
6435 9, 9, 9, 9, 9, 9,
6436 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6437 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6438 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6439 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
6440
6441
6442// This function works for dates from 1970 to 2099.
6443static inline void DateYMDFromTimeAfter1970(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006444 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006445#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006446 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006447#endif
6448
6449 year = 1970 + (4 * date + 2) / kDaysIn4Years;
6450 date %= kDaysIn4Years;
6451
6452 month = kMonthInYear[date];
6453 day = kDayInYear[date];
6454
6455 ASSERT(MakeDay(year, month, day) == save_date);
6456}
6457
6458
6459static inline void DateYMDFromTimeSlow(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006460 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006461#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006462 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006463#endif
6464
6465 date += kDaysOffset;
6466 year = 400 * (date / kDaysIn400Years) - kYearsOffset;
6467 date %= kDaysIn400Years;
6468
6469 ASSERT(MakeDay(year, 0, 1) + date == save_date);
6470
6471 date--;
6472 int yd1 = date / kDaysIn100Years;
6473 date %= kDaysIn100Years;
6474 year += 100 * yd1;
6475
6476 date++;
6477 int yd2 = date / kDaysIn4Years;
6478 date %= kDaysIn4Years;
6479 year += 4 * yd2;
6480
6481 date--;
6482 int yd3 = date / 365;
6483 date %= 365;
6484 year += yd3;
6485
6486 bool is_leap = (!yd1 || yd2) && !yd3;
6487
6488 ASSERT(date >= -1);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006489 ASSERT(is_leap || (date >= 0));
6490 ASSERT((date < 365) || (is_leap && (date < 366)));
6491 ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0))));
6492 ASSERT(is_leap || ((MakeDay(year, 0, 1) + date) == save_date));
6493 ASSERT(!is_leap || ((MakeDay(year, 0, 1) + date + 1) == save_date));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006494
6495 if (is_leap) {
6496 day = kDayInYear[2*365 + 1 + date];
6497 month = kMonthInYear[2*365 + 1 + date];
6498 } else {
6499 day = kDayInYear[date];
6500 month = kMonthInYear[date];
6501 }
6502
6503 ASSERT(MakeDay(year, month, day) == save_date);
6504}
6505
6506
6507static inline void DateYMDFromTime(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006508 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006509 if (date >= 0 && date < 32 * kDaysIn4Years) {
6510 DateYMDFromTimeAfter1970(date, year, month, day);
6511 } else {
6512 DateYMDFromTimeSlow(date, year, month, day);
6513 }
6514}
6515
6516
6517static Object* Runtime_DateYMDFromTime(Arguments args) {
6518 NoHandleAllocation ha;
6519 ASSERT(args.length() == 2);
6520
6521 CONVERT_DOUBLE_CHECKED(t, args[0]);
6522 CONVERT_CHECKED(JSArray, res_array, args[1]);
6523
6524 int year, month, day;
6525 DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
6526
6527 res_array->SetElement(0, Smi::FromInt(year));
6528 res_array->SetElement(1, Smi::FromInt(month));
6529 res_array->SetElement(2, Smi::FromInt(day));
6530
6531 return Heap::undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006532}
6533
6534
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006535static Object* Runtime_NewArgumentsFast(Arguments args) {
6536 NoHandleAllocation ha;
6537 ASSERT(args.length() == 3);
6538
6539 JSFunction* callee = JSFunction::cast(args[0]);
6540 Object** parameters = reinterpret_cast<Object**>(args[1]);
6541 const int length = Smi::cast(args[2])->value();
6542
6543 Object* result = Heap::AllocateArgumentsObject(callee, length);
6544 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006545 // Allocate the elements if needed.
6546 if (length > 0) {
6547 // Allocate the fixed array.
6548 Object* obj = Heap::AllocateRawFixedArray(length);
6549 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006550
6551 AssertNoAllocation no_gc;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006552 FixedArray* array = reinterpret_cast<FixedArray*>(obj);
6553 array->set_map(Heap::fixed_array_map());
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006554 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006555
6556 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006557 for (int i = 0; i < length; i++) {
6558 array->set(i, *--parameters, mode);
6559 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006560 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006561 }
6562 return result;
6563}
6564
6565
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006566static Object* Runtime_NewClosure(Arguments args) {
6567 HandleScope scope;
6568 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00006569 CONVERT_ARG_CHECKED(Context, context, 0);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006570 CONVERT_ARG_CHECKED(SharedFunctionInfo, shared, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006571
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00006572 PretenureFlag pretenure = (context->global_context() == *context)
6573 ? TENURED // Allocate global closures in old space.
6574 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006575 Handle<JSFunction> result =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006576 Factory::NewFunctionFromSharedFunctionInfo(shared, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006577 return *result;
6578}
6579
6580
ager@chromium.org5c838252010-02-19 08:53:10 +00006581static Code* ComputeConstructStub(Handle<JSFunction> function) {
6582 Handle<Object> prototype = Factory::null_value();
6583 if (function->has_instance_prototype()) {
6584 prototype = Handle<Object>(function->instance_prototype());
6585 }
6586 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006587 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00006588 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006589 if (code->IsFailure()) {
6590 return Builtins::builtin(Builtins::JSConstructStubGeneric);
6591 }
6592 return Code::cast(code);
6593 }
6594
ager@chromium.org5c838252010-02-19 08:53:10 +00006595 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006596}
6597
6598
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006599static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006600 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006601 ASSERT(args.length() == 1);
6602
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006603 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006604
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006605 // If the constructor isn't a proper function we throw a type error.
6606 if (!constructor->IsJSFunction()) {
6607 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6608 Handle<Object> type_error =
6609 Factory::NewTypeError("not_constructor", arguments);
6610 return Top::Throw(*type_error);
6611 }
6612
6613 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006614
6615 // If function should not have prototype, construction is not allowed. In this
6616 // case generated code bailouts here, since function has no initial_map.
6617 if (!function->should_have_prototype()) {
6618 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6619 Handle<Object> type_error =
6620 Factory::NewTypeError("not_constructor", arguments);
6621 return Top::Throw(*type_error);
6622 }
6623
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006624#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006625 // Handle stepping into constructors if step into is active.
6626 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006627 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006628 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006629#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006630
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006631 if (function->has_initial_map()) {
6632 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006633 // The 'Function' function ignores the receiver object when
6634 // called using 'new' and creates a new JSFunction object that
6635 // is returned. The receiver object is only used for error
6636 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006637 // JSFunction. Factory::NewJSObject() should not be used to
6638 // allocate JSFunctions since it does not properly initialize
6639 // the shared part of the function. Since the receiver is
6640 // ignored anyway, we use the global object as the receiver
6641 // instead of a new JSFunction object. This way, errors are
6642 // reported the same way whether or not 'Function' is called
6643 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006644 return Top::context()->global();
6645 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006646 }
6647
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006648 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006649 Handle<SharedFunctionInfo> shared(function->shared());
6650 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006651
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006652 bool first_allocation = !function->has_initial_map();
6653 Handle<JSObject> result = Factory::NewJSObject(function);
6654 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006655 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00006656 ComputeConstructStub(Handle<JSFunction>(function)));
6657 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006658 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006659
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006660 Counters::constructed_objects.Increment();
6661 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006662
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006663 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006664}
6665
6666
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006667static Object* Runtime_LazyCompile(Arguments args) {
6668 HandleScope scope;
6669 ASSERT(args.length() == 1);
6670
6671 Handle<JSFunction> function = args.at<JSFunction>(0);
6672#ifdef DEBUG
6673 if (FLAG_trace_lazy) {
6674 PrintF("[lazy: ");
6675 function->shared()->name()->Print();
6676 PrintF("]\n");
6677 }
6678#endif
6679
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006680 // Compile the target function. Here we compile using CompileLazyInLoop in
6681 // order to get the optimized version. This helps code like delta-blue
6682 // that calls performance-critical routines through constructors. A
6683 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
6684 // direct call. Since the in-loop tracking takes place through CallICs
6685 // this means that things called through constructors are never known to
6686 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006687 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006688 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006689 return Failure::Exception();
6690 }
6691
6692 return function->code();
6693}
6694
6695
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006696static Object* Runtime_GetFunctionDelegate(Arguments args) {
6697 HandleScope scope;
6698 ASSERT(args.length() == 1);
6699 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6700 return *Execution::GetFunctionDelegate(args.at<Object>(0));
6701}
6702
6703
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00006704static Object* Runtime_GetConstructorDelegate(Arguments args) {
6705 HandleScope scope;
6706 ASSERT(args.length() == 1);
6707 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6708 return *Execution::GetConstructorDelegate(args.at<Object>(0));
6709}
6710
6711
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006712static Object* Runtime_NewContext(Arguments args) {
6713 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00006714 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006715
kasper.lund7276f142008-07-30 08:49:36 +00006716 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006717 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
6718 Object* result = Heap::AllocateFunctionContext(length, function);
6719 if (result->IsFailure()) return result;
6720
6721 Top::set_context(Context::cast(result));
6722
kasper.lund7276f142008-07-30 08:49:36 +00006723 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006724}
6725
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006726static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006727 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006728 Object* js_object = object;
6729 if (!js_object->IsJSObject()) {
6730 js_object = js_object->ToObject();
6731 if (js_object->IsFailure()) {
6732 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006733 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006734 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006735 Handle<Object> result =
6736 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
6737 return Top::Throw(*result);
6738 }
6739 }
6740
6741 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006742 Heap::AllocateWithContext(Top::context(),
6743 JSObject::cast(js_object),
6744 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006745 if (result->IsFailure()) return result;
6746
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006747 Context* context = Context::cast(result);
6748 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006749
kasper.lund7276f142008-07-30 08:49:36 +00006750 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006751}
6752
6753
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006754static Object* Runtime_PushContext(Arguments args) {
6755 NoHandleAllocation ha;
6756 ASSERT(args.length() == 1);
6757 return PushContextHelper(args[0], false);
6758}
6759
6760
6761static Object* Runtime_PushCatchContext(Arguments args) {
6762 NoHandleAllocation ha;
6763 ASSERT(args.length() == 1);
6764 return PushContextHelper(args[0], true);
6765}
6766
6767
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006768static Object* Runtime_LookupContext(Arguments args) {
6769 HandleScope scope;
6770 ASSERT(args.length() == 2);
6771
6772 CONVERT_ARG_CHECKED(Context, context, 0);
6773 CONVERT_ARG_CHECKED(String, name, 1);
6774
6775 int index;
6776 PropertyAttributes attributes;
6777 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006778 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006779 context->Lookup(name, flags, &index, &attributes);
6780
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006781 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006782 ASSERT(holder->IsJSObject());
6783 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006784 }
6785
6786 // No intermediate context found. Use global object by default.
6787 return Top::context()->global();
6788}
6789
6790
ager@chromium.orga1645e22009-09-09 19:27:10 +00006791// A mechanism to return a pair of Object pointers in registers (if possible).
6792// How this is achieved is calling convention-dependent.
6793// All currently supported x86 compiles uses calling conventions that are cdecl
6794// variants where a 64-bit value is returned in two 32-bit registers
6795// (edx:eax on ia32, r1:r0 on ARM).
6796// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
6797// In Win64 calling convention, a struct of two pointers is returned in memory,
6798// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006799#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006800struct ObjectPair {
6801 Object* x;
6802 Object* y;
6803};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006804
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006805static inline ObjectPair MakePair(Object* x, Object* y) {
6806 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006807 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
6808 // In Win64 they are assigned to a hidden first argument.
6809 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006810}
6811#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006812typedef uint64_t ObjectPair;
6813static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006814 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006815 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006816}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006817#endif
6818
6819
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006820static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006821 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
6822 USE(attributes);
6823 return x->IsTheHole() ? Heap::undefined_value() : x;
6824}
6825
6826
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006827static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
6828 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006829 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006830 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006831 JSFunction* context_extension_function =
6832 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006833 // If the holder isn't a context extension object, we just return it
6834 // as the receiver. This allows arguments objects to be used as
6835 // receivers, but only if they are put in the context scope chain
6836 // explicitly via a with-statement.
6837 Object* constructor = holder->map()->constructor();
6838 if (constructor != context_extension_function) return holder;
6839 // Fall back to using the global object as the receiver if the
6840 // property turns out to be a local variable allocated in a context
6841 // extension object - introduced via eval.
6842 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006843}
6844
6845
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006846static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006847 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00006848 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006849
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006850 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00006851 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006852 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006853 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006854 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006855
6856 int index;
6857 PropertyAttributes attributes;
6858 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006859 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006860 context->Lookup(name, flags, &index, &attributes);
6861
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006862 // If the index is non-negative, the slot has been found in a local
6863 // variable or a parameter. Read it from the context object or the
6864 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006865 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006866 // If the "property" we were looking for is a local variable or an
6867 // argument in a context, the receiver is the global object; see
6868 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
6869 JSObject* receiver = Top::context()->global()->global_receiver();
6870 Object* value = (holder->IsContext())
6871 ? Context::cast(*holder)->get(index)
6872 : JSObject::cast(*holder)->GetElement(index);
6873 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006874 }
6875
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006876 // If the holder is found, we read the property from it.
6877 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006878 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006879 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006880 JSObject* receiver;
6881 if (object->IsGlobalObject()) {
6882 receiver = GlobalObject::cast(object)->global_receiver();
6883 } else if (context->is_exception_holder(*holder)) {
6884 receiver = Top::context()->global()->global_receiver();
6885 } else {
6886 receiver = ComputeReceiverForNonGlobal(object);
6887 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006888 // No need to unhole the value here. This is taken care of by the
6889 // GetProperty function.
6890 Object* value = object->GetProperty(*name);
6891 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006892 }
6893
6894 if (throw_error) {
6895 // The property doesn't exist - throw exception.
6896 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006897 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006898 return MakePair(Top::Throw(*reference_error), NULL);
6899 } else {
6900 // The property doesn't exist - return undefined
6901 return MakePair(Heap::undefined_value(), Heap::undefined_value());
6902 }
6903}
6904
6905
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006906static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006907 return LoadContextSlotHelper(args, true);
6908}
6909
6910
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006911static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006912 return LoadContextSlotHelper(args, false);
6913}
6914
6915
6916static Object* Runtime_StoreContextSlot(Arguments args) {
6917 HandleScope scope;
6918 ASSERT(args.length() == 3);
6919
6920 Handle<Object> value(args[0]);
6921 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006922 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006923
6924 int index;
6925 PropertyAttributes attributes;
6926 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006927 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006928 context->Lookup(name, flags, &index, &attributes);
6929
6930 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006931 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006932 // Ignore if read_only variable.
6933 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006934 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006935 }
6936 } else {
6937 ASSERT((attributes & READ_ONLY) == 0);
6938 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006939 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006940 USE(result);
6941 ASSERT(!result->IsFailure());
6942 }
6943 return *value;
6944 }
6945
6946 // Slow case: The property is not in a FixedArray context.
6947 // It is either in an JSObject extension context or it was not found.
6948 Handle<JSObject> context_ext;
6949
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006950 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006951 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006952 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006953 } else {
6954 // The property was not found. It needs to be stored in the global context.
6955 ASSERT(attributes == ABSENT);
6956 attributes = NONE;
6957 context_ext = Handle<JSObject>(Top::context()->global());
6958 }
6959
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006960 // Set the property, but ignore if read_only variable on the context
6961 // extension object itself.
6962 if ((attributes & READ_ONLY) == 0 ||
6963 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006964 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
6965 if (set.is_null()) {
6966 // Failure::Exception is converted to a null handle in the
6967 // handle-based methods such as SetProperty. We therefore need
6968 // to convert null handles back to exceptions.
6969 ASSERT(Top::has_pending_exception());
6970 return Failure::Exception();
6971 }
6972 }
6973 return *value;
6974}
6975
6976
6977static Object* Runtime_Throw(Arguments args) {
6978 HandleScope scope;
6979 ASSERT(args.length() == 1);
6980
6981 return Top::Throw(args[0]);
6982}
6983
6984
6985static Object* Runtime_ReThrow(Arguments args) {
6986 HandleScope scope;
6987 ASSERT(args.length() == 1);
6988
6989 return Top::ReThrow(args[0]);
6990}
6991
6992
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006993static Object* Runtime_PromoteScheduledException(Arguments args) {
6994 ASSERT_EQ(0, args.length());
6995 return Top::PromoteScheduledException();
6996}
6997
6998
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006999static Object* Runtime_ThrowReferenceError(Arguments args) {
7000 HandleScope scope;
7001 ASSERT(args.length() == 1);
7002
7003 Handle<Object> name(args[0]);
7004 Handle<Object> reference_error =
7005 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
7006 return Top::Throw(*reference_error);
7007}
7008
7009
7010static Object* Runtime_StackOverflow(Arguments args) {
7011 NoHandleAllocation na;
7012 return Top::StackOverflow();
7013}
7014
7015
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007016static Object* Runtime_StackGuard(Arguments args) {
7017 ASSERT(args.length() == 1);
7018
7019 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007020 if (StackGuard::IsStackOverflow()) {
7021 return Runtime_StackOverflow(args);
7022 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007023
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007024 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007025}
7026
7027
7028// NOTE: These PrintXXX functions are defined for all builds (not just
7029// DEBUG builds) because we may want to be able to trace function
7030// calls in all modes.
7031static void PrintString(String* str) {
7032 // not uncommon to have empty strings
7033 if (str->length() > 0) {
7034 SmartPointer<char> s =
7035 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
7036 PrintF("%s", *s);
7037 }
7038}
7039
7040
7041static void PrintObject(Object* obj) {
7042 if (obj->IsSmi()) {
7043 PrintF("%d", Smi::cast(obj)->value());
7044 } else if (obj->IsString() || obj->IsSymbol()) {
7045 PrintString(String::cast(obj));
7046 } else if (obj->IsNumber()) {
7047 PrintF("%g", obj->Number());
7048 } else if (obj->IsFailure()) {
7049 PrintF("<failure>");
7050 } else if (obj->IsUndefined()) {
7051 PrintF("<undefined>");
7052 } else if (obj->IsNull()) {
7053 PrintF("<null>");
7054 } else if (obj->IsTrue()) {
7055 PrintF("<true>");
7056 } else if (obj->IsFalse()) {
7057 PrintF("<false>");
7058 } else {
7059 PrintF("%p", obj);
7060 }
7061}
7062
7063
7064static int StackSize() {
7065 int n = 0;
7066 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
7067 return n;
7068}
7069
7070
7071static void PrintTransition(Object* result) {
7072 // indentation
7073 { const int nmax = 80;
7074 int n = StackSize();
7075 if (n <= nmax)
7076 PrintF("%4d:%*s", n, n, "");
7077 else
7078 PrintF("%4d:%*s", n, nmax, "...");
7079 }
7080
7081 if (result == NULL) {
7082 // constructor calls
7083 JavaScriptFrameIterator it;
7084 JavaScriptFrame* frame = it.frame();
7085 if (frame->IsConstructor()) PrintF("new ");
7086 // function name
7087 Object* fun = frame->function();
7088 if (fun->IsJSFunction()) {
7089 PrintObject(JSFunction::cast(fun)->shared()->name());
7090 } else {
7091 PrintObject(fun);
7092 }
7093 // function arguments
7094 // (we are intentionally only printing the actually
7095 // supplied parameters, not all parameters required)
7096 PrintF("(this=");
7097 PrintObject(frame->receiver());
7098 const int length = frame->GetProvidedParametersCount();
7099 for (int i = 0; i < length; i++) {
7100 PrintF(", ");
7101 PrintObject(frame->GetParameter(i));
7102 }
7103 PrintF(") {\n");
7104
7105 } else {
7106 // function result
7107 PrintF("} -> ");
7108 PrintObject(result);
7109 PrintF("\n");
7110 }
7111}
7112
7113
7114static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007115 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007116 NoHandleAllocation ha;
7117 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007118 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007119}
7120
7121
7122static Object* Runtime_TraceExit(Arguments args) {
7123 NoHandleAllocation ha;
7124 PrintTransition(args[0]);
7125 return args[0]; // return TOS
7126}
7127
7128
7129static Object* Runtime_DebugPrint(Arguments args) {
7130 NoHandleAllocation ha;
7131 ASSERT(args.length() == 1);
7132
7133#ifdef DEBUG
7134 if (args[0]->IsString()) {
7135 // If we have a string, assume it's a code "marker"
7136 // and print some interesting cpu debugging info.
7137 JavaScriptFrameIterator it;
7138 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007139 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
7140 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007141 } else {
7142 PrintF("DebugPrint: ");
7143 }
7144 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007145 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007146 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007147 HeapObject::cast(args[0])->map()->Print();
7148 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007149#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007150 // ShortPrint is available in release mode. Print is not.
7151 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007152#endif
7153 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00007154 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007155
7156 return args[0]; // return TOS
7157}
7158
7159
7160static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007161 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007162 NoHandleAllocation ha;
7163 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007164 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007165}
7166
7167
mads.s.ager31e71382008-08-13 09:32:07 +00007168static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007169 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007170 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007171
7172 // According to ECMA-262, section 15.9.1, page 117, the precision of
7173 // the number in a Date object representing a particular instant in
7174 // time is milliseconds. Therefore, we floor the result of getting
7175 // the OS time.
7176 double millis = floor(OS::TimeCurrentMillis());
7177 return Heap::NumberFromDouble(millis);
7178}
7179
7180
7181static Object* Runtime_DateParseString(Arguments args) {
7182 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007183 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007184
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007185 CONVERT_ARG_CHECKED(String, str, 0);
7186 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007187
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007188 CONVERT_ARG_CHECKED(JSArray, output, 1);
7189 RUNTIME_ASSERT(output->HasFastElements());
7190
7191 AssertNoAllocation no_allocation;
7192
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007193 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007194 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
7195 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007196 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007197 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007198 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007199 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007200 result = DateParser::Parse(str->ToUC16Vector(), output_array);
7201 }
7202
7203 if (result) {
7204 return *output;
7205 } else {
7206 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007207 }
7208}
7209
7210
7211static Object* Runtime_DateLocalTimezone(Arguments args) {
7212 NoHandleAllocation ha;
7213 ASSERT(args.length() == 1);
7214
7215 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007216 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007217 return Heap::AllocateStringFromUtf8(CStrVector(zone));
7218}
7219
7220
7221static Object* Runtime_DateLocalTimeOffset(Arguments args) {
7222 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007223 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007224
7225 return Heap::NumberFromDouble(OS::LocalTimeOffset());
7226}
7227
7228
7229static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
7230 NoHandleAllocation ha;
7231 ASSERT(args.length() == 1);
7232
7233 CONVERT_DOUBLE_CHECKED(x, args[0]);
7234 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
7235}
7236
7237
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007238static Object* Runtime_GlobalReceiver(Arguments args) {
7239 ASSERT(args.length() == 1);
7240 Object* global = args[0];
7241 if (!global->IsJSGlobalObject()) return Heap::null_value();
7242 return JSGlobalObject::cast(global)->global_receiver();
7243}
7244
7245
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007246static Object* Runtime_CompileString(Arguments args) {
7247 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007248 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007249 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007250 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007251
ager@chromium.org381abbb2009-02-25 13:23:22 +00007252 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007253 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007254 Compiler::ValidationState validate = (is_json->IsTrue())
7255 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007256 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
7257 context,
7258 true,
7259 validate);
7260 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007261 Handle<JSFunction> fun =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007262 Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007263 return *fun;
7264}
7265
7266
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007267static ObjectPair CompileGlobalEval(Handle<String> source,
7268 Handle<Object> receiver) {
7269 // Deal with a normal eval call with a string argument. Compile it
7270 // and return the compiled function bound in the local context.
7271 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
7272 source,
7273 Handle<Context>(Top::context()),
7274 Top::context()->IsGlobalContext(),
7275 Compiler::DONT_VALIDATE_JSON);
7276 if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
7277 Handle<JSFunction> compiled = Factory::NewFunctionFromSharedFunctionInfo(
7278 shared,
7279 Handle<Context>(Top::context()),
7280 NOT_TENURED);
7281 return MakePair(*compiled, *receiver);
7282}
7283
7284
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007285static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
7286 ASSERT(args.length() == 3);
7287 if (!args[0]->IsJSFunction()) {
7288 return MakePair(Top::ThrowIllegalOperation(), NULL);
7289 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007290
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007291 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007292 Handle<JSFunction> callee = args.at<JSFunction>(0);
7293 Handle<Object> receiver; // Will be overwritten.
7294
7295 // Compute the calling context.
7296 Handle<Context> context = Handle<Context>(Top::context());
7297#ifdef DEBUG
7298 // Make sure Top::context() agrees with the old code that traversed
7299 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007300 StackFrameLocator locator;
7301 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007302 ASSERT(Context::cast(frame->context()) == *context);
7303#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007304
7305 // Find where the 'eval' symbol is bound. It is unaliased only if
7306 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007307 int index = -1;
7308 PropertyAttributes attributes = ABSENT;
7309 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007310 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
7311 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007312 // Stop search when eval is found or when the global context is
7313 // reached.
7314 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007315 if (context->is_function_context()) {
7316 context = Handle<Context>(Context::cast(context->closure()->context()));
7317 } else {
7318 context = Handle<Context>(context->previous());
7319 }
7320 }
7321
iposva@chromium.org245aa852009-02-10 00:49:54 +00007322 // If eval could not be resolved, it has been deleted and we need to
7323 // throw a reference error.
7324 if (attributes == ABSENT) {
7325 Handle<Object> name = Factory::eval_symbol();
7326 Handle<Object> reference_error =
7327 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007328 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007329 }
7330
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007331 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007332 // 'eval' is not bound in the global context. Just call the function
7333 // with the given arguments. This is not necessarily the global eval.
7334 if (receiver->IsContext()) {
7335 context = Handle<Context>::cast(receiver);
7336 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007337 } else if (receiver->IsJSContextExtensionObject()) {
7338 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007339 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007340 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007341 }
7342
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007343 // 'eval' is bound in the global context, but it may have been overwritten.
7344 // Compare it to the builtin 'GlobalEval' function to make sure.
7345 if (*callee != Top::global_context()->global_eval_fun() ||
7346 !args[1]->IsString()) {
7347 return MakePair(*callee, Top::context()->global()->global_receiver());
7348 }
7349
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007350 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
7351}
7352
7353
7354static ObjectPair Runtime_ResolvePossiblyDirectEvalNoLookup(Arguments args) {
7355 ASSERT(args.length() == 3);
7356 if (!args[0]->IsJSFunction()) {
7357 return MakePair(Top::ThrowIllegalOperation(), NULL);
7358 }
7359
7360 HandleScope scope;
7361 Handle<JSFunction> callee = args.at<JSFunction>(0);
7362
7363 // 'eval' is bound in the global context, but it may have been overwritten.
7364 // Compare it to the builtin 'GlobalEval' function to make sure.
7365 if (*callee != Top::global_context()->global_eval_fun() ||
7366 !args[1]->IsString()) {
7367 return MakePair(*callee, Top::context()->global()->global_receiver());
7368 }
7369
7370 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007371}
7372
7373
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007374static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
7375 // This utility adjusts the property attributes for newly created Function
7376 // object ("new Function(...)") by changing the map.
7377 // All it does is changing the prototype property to enumerable
7378 // as specified in ECMA262, 15.3.5.2.
7379 HandleScope scope;
7380 ASSERT(args.length() == 1);
7381 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7382 ASSERT(func->map()->instance_type() ==
7383 Top::function_instance_map()->instance_type());
7384 ASSERT(func->map()->instance_size() ==
7385 Top::function_instance_map()->instance_size());
7386 func->set_map(*Top::function_instance_map());
7387 return *func;
7388}
7389
7390
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007391// Push an array unto an array of arrays if it is not already in the
7392// array. Returns true if the element was pushed on the stack and
7393// false otherwise.
7394static Object* Runtime_PushIfAbsent(Arguments args) {
7395 ASSERT(args.length() == 2);
7396 CONVERT_CHECKED(JSArray, array, args[0]);
7397 CONVERT_CHECKED(JSArray, element, args[1]);
7398 RUNTIME_ASSERT(array->HasFastElements());
7399 int length = Smi::cast(array->length())->value();
7400 FixedArray* elements = FixedArray::cast(array->elements());
7401 for (int i = 0; i < length; i++) {
7402 if (elements->get(i) == element) return Heap::false_value();
7403 }
7404 Object* obj = array->SetFastElement(length, element);
7405 if (obj->IsFailure()) return obj;
7406 return Heap::true_value();
7407}
7408
7409
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007410/**
7411 * A simple visitor visits every element of Array's.
7412 * The backend storage can be a fixed array for fast elements case,
7413 * or a dictionary for sparse array. Since Dictionary is a subtype
7414 * of FixedArray, the class can be used by both fast and slow cases.
7415 * The second parameter of the constructor, fast_elements, specifies
7416 * whether the storage is a FixedArray or Dictionary.
7417 *
7418 * An index limit is used to deal with the situation that a result array
7419 * length overflows 32-bit non-negative integer.
7420 */
7421class ArrayConcatVisitor {
7422 public:
7423 ArrayConcatVisitor(Handle<FixedArray> storage,
7424 uint32_t index_limit,
7425 bool fast_elements) :
7426 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007427 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007428
7429 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007430 if (i >= index_limit_ - index_offset_) return;
7431 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007432
7433 if (fast_elements_) {
7434 ASSERT(index < static_cast<uint32_t>(storage_->length()));
7435 storage_->set(index, *elm);
7436
7437 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007438 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
7439 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007440 Factory::DictionaryAtNumberPut(dict, index, elm);
7441 if (!result.is_identical_to(dict))
7442 storage_ = result;
7443 }
7444 }
7445
7446 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007447 if (index_limit_ - index_offset_ < delta) {
7448 index_offset_ = index_limit_;
7449 } else {
7450 index_offset_ += delta;
7451 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007452 }
7453
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007454 Handle<FixedArray> storage() { return storage_; }
7455
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007456 private:
7457 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007458 // Limit on the accepted indices. Elements with indices larger than the
7459 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007460 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007461 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007462 uint32_t index_offset_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007463 bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007464};
7465
7466
ager@chromium.org3811b432009-10-28 14:53:37 +00007467template<class ExternalArrayClass, class ElementType>
7468static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
7469 bool elements_are_ints,
7470 bool elements_are_guaranteed_smis,
7471 uint32_t range,
7472 ArrayConcatVisitor* visitor) {
7473 Handle<ExternalArrayClass> array(
7474 ExternalArrayClass::cast(receiver->elements()));
7475 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
7476
7477 if (visitor != NULL) {
7478 if (elements_are_ints) {
7479 if (elements_are_guaranteed_smis) {
7480 for (uint32_t j = 0; j < len; j++) {
7481 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
7482 visitor->visit(j, e);
7483 }
7484 } else {
7485 for (uint32_t j = 0; j < len; j++) {
7486 int64_t val = static_cast<int64_t>(array->get(j));
7487 if (Smi::IsValid(static_cast<intptr_t>(val))) {
7488 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
7489 visitor->visit(j, e);
7490 } else {
7491 Handle<Object> e(
7492 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
7493 visitor->visit(j, e);
7494 }
7495 }
7496 }
7497 } else {
7498 for (uint32_t j = 0; j < len; j++) {
7499 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
7500 visitor->visit(j, e);
7501 }
7502 }
7503 }
7504
7505 return len;
7506}
7507
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007508/**
7509 * A helper function that visits elements of a JSObject. Only elements
7510 * whose index between 0 and range (exclusive) are visited.
7511 *
7512 * If the third parameter, visitor, is not NULL, the visitor is called
7513 * with parameters, 'visitor_index_offset + element index' and the element.
7514 *
7515 * It returns the number of visisted elements.
7516 */
7517static uint32_t IterateElements(Handle<JSObject> receiver,
7518 uint32_t range,
7519 ArrayConcatVisitor* visitor) {
7520 uint32_t num_of_elements = 0;
7521
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007522 switch (receiver->GetElementsKind()) {
7523 case JSObject::FAST_ELEMENTS: {
7524 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
7525 uint32_t len = elements->length();
7526 if (range < len) {
7527 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007528 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007529
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007530 for (uint32_t j = 0; j < len; j++) {
7531 Handle<Object> e(elements->get(j));
7532 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007533 num_of_elements++;
7534 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007535 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007536 }
7537 }
7538 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007539 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007540 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007541 case JSObject::PIXEL_ELEMENTS: {
7542 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
7543 uint32_t len = pixels->length();
7544 if (range < len) {
7545 len = range;
7546 }
7547
7548 for (uint32_t j = 0; j < len; j++) {
7549 num_of_elements++;
7550 if (visitor != NULL) {
7551 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
7552 visitor->visit(j, e);
7553 }
7554 }
7555 break;
7556 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007557 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
7558 num_of_elements =
7559 IterateExternalArrayElements<ExternalByteArray, int8_t>(
7560 receiver, true, true, range, visitor);
7561 break;
7562 }
7563 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7564 num_of_elements =
7565 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
7566 receiver, true, true, range, visitor);
7567 break;
7568 }
7569 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
7570 num_of_elements =
7571 IterateExternalArrayElements<ExternalShortArray, int16_t>(
7572 receiver, true, true, range, visitor);
7573 break;
7574 }
7575 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7576 num_of_elements =
7577 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
7578 receiver, true, true, range, visitor);
7579 break;
7580 }
7581 case JSObject::EXTERNAL_INT_ELEMENTS: {
7582 num_of_elements =
7583 IterateExternalArrayElements<ExternalIntArray, int32_t>(
7584 receiver, true, false, range, visitor);
7585 break;
7586 }
7587 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7588 num_of_elements =
7589 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
7590 receiver, true, false, range, visitor);
7591 break;
7592 }
7593 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
7594 num_of_elements =
7595 IterateExternalArrayElements<ExternalFloatArray, float>(
7596 receiver, false, false, range, visitor);
7597 break;
7598 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007599 case JSObject::DICTIONARY_ELEMENTS: {
7600 Handle<NumberDictionary> dict(receiver->element_dictionary());
7601 uint32_t capacity = dict->Capacity();
7602 for (uint32_t j = 0; j < capacity; j++) {
7603 Handle<Object> k(dict->KeyAt(j));
7604 if (dict->IsKey(*k)) {
7605 ASSERT(k->IsNumber());
7606 uint32_t index = static_cast<uint32_t>(k->Number());
7607 if (index < range) {
7608 num_of_elements++;
7609 if (visitor) {
7610 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
7611 }
7612 }
7613 }
7614 }
7615 break;
7616 }
7617 default:
7618 UNREACHABLE();
7619 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007620 }
7621
7622 return num_of_elements;
7623}
7624
7625
7626/**
7627 * A helper function that visits elements of an Array object, and elements
7628 * on its prototypes.
7629 *
7630 * Elements on prototypes are visited first, and only elements whose indices
7631 * less than Array length are visited.
7632 *
7633 * If a ArrayConcatVisitor object is given, the visitor is called with
7634 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007635 *
7636 * The returned number of elements is an upper bound on the actual number
7637 * of elements added. If the same element occurs in more than one object
7638 * in the array's prototype chain, it will be counted more than once, but
7639 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007640 */
7641static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
7642 ArrayConcatVisitor* visitor) {
7643 uint32_t range = static_cast<uint32_t>(array->length()->Number());
7644 Handle<Object> obj = array;
7645
7646 static const int kEstimatedPrototypes = 3;
7647 List< Handle<JSObject> > objects(kEstimatedPrototypes);
7648
7649 // Visit prototype first. If an element on the prototype is shadowed by
7650 // the inheritor using the same index, the ArrayConcatVisitor visits
7651 // the prototype element before the shadowing element.
7652 // The visitor can simply overwrite the old value by new value using
7653 // the same index. This follows Array::concat semantics.
7654 while (!obj->IsNull()) {
7655 objects.Add(Handle<JSObject>::cast(obj));
7656 obj = Handle<Object>(obj->GetPrototype());
7657 }
7658
7659 uint32_t nof_elements = 0;
7660 for (int i = objects.length() - 1; i >= 0; i--) {
7661 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007662 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007663 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007664
7665 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
7666 nof_elements = JSObject::kMaxElementCount;
7667 } else {
7668 nof_elements += encountered_elements;
7669 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007670 }
7671
7672 return nof_elements;
7673}
7674
7675
7676/**
7677 * A helper function of Runtime_ArrayConcat.
7678 *
7679 * The first argument is an Array of arrays and objects. It is the
7680 * same as the arguments array of Array::concat JS function.
7681 *
7682 * If an argument is an Array object, the function visits array
7683 * elements. If an argument is not an Array object, the function
7684 * visits the object as if it is an one-element array.
7685 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007686 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007687 * non-negative number is used as new length. For example, if one
7688 * array length is 2^32 - 1, second array length is 1, the
7689 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007690 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
7691 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007692 */
7693static uint32_t IterateArguments(Handle<JSArray> arguments,
7694 ArrayConcatVisitor* visitor) {
7695 uint32_t visited_elements = 0;
7696 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7697
7698 for (uint32_t i = 0; i < num_of_args; i++) {
7699 Handle<Object> obj(arguments->GetElement(i));
7700 if (obj->IsJSArray()) {
7701 Handle<JSArray> array = Handle<JSArray>::cast(obj);
7702 uint32_t len = static_cast<uint32_t>(array->length()->Number());
7703 uint32_t nof_elements =
7704 IterateArrayAndPrototypeElements(array, visitor);
7705 // Total elements of array and its prototype chain can be more than
7706 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007707 // the array length number of elements. We use the length as an estimate
7708 // for the actual number of elements added.
7709 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
7710 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
7711 visited_elements = JSArray::kMaxElementCount;
7712 } else {
7713 visited_elements += added_elements;
7714 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007715 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007716 } else {
7717 if (visitor) {
7718 visitor->visit(0, obj);
7719 visitor->increase_index_offset(1);
7720 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007721 if (visited_elements < JSArray::kMaxElementCount) {
7722 visited_elements++;
7723 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007724 }
7725 }
7726 return visited_elements;
7727}
7728
7729
7730/**
7731 * Array::concat implementation.
7732 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007733 * TODO(lrn): Fix non-compliance for very large concatenations and update to
7734 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007735 */
7736static Object* Runtime_ArrayConcat(Arguments args) {
7737 ASSERT(args.length() == 1);
7738 HandleScope handle_scope;
7739
7740 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
7741 Handle<JSArray> arguments(arg_arrays);
7742
7743 // Pass 1: estimate the number of elements of the result
7744 // (it could be more than real numbers if prototype has elements).
7745 uint32_t result_length = 0;
7746 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7747
7748 { AssertNoAllocation nogc;
7749 for (uint32_t i = 0; i < num_of_args; i++) {
7750 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007751 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007752 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007753 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007754 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
7755 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007756 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007757 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007758 if (JSObject::kMaxElementCount - result_length < length_estimate) {
7759 result_length = JSObject::kMaxElementCount;
7760 break;
7761 }
7762 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007763 }
7764 }
7765
7766 // Allocate an empty array, will set length and content later.
7767 Handle<JSArray> result = Factory::NewJSArray(0);
7768
7769 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
7770 // If estimated number of elements is more than half of length, a
7771 // fixed array (fast case) is more time and space-efficient than a
7772 // dictionary.
7773 bool fast_case = (estimate_nof_elements * 2) >= result_length;
7774
7775 Handle<FixedArray> storage;
7776 if (fast_case) {
7777 // The backing storage array must have non-existing elements to
7778 // preserve holes across concat operations.
7779 storage = Factory::NewFixedArrayWithHoles(result_length);
7780
7781 } else {
7782 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
7783 uint32_t at_least_space_for = estimate_nof_elements +
7784 (estimate_nof_elements >> 2);
7785 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007786 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007787 }
7788
7789 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
7790
7791 ArrayConcatVisitor visitor(storage, result_length, fast_case);
7792
7793 IterateArguments(arguments, &visitor);
7794
7795 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007796 // Please note the storage might have changed in the visitor.
7797 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007798
7799 return *result;
7800}
7801
7802
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007803// This will not allocate (flatten the string), but it may run
7804// very slowly for very deeply nested ConsStrings. For debugging use only.
7805static Object* Runtime_GlobalPrint(Arguments args) {
7806 NoHandleAllocation ha;
7807 ASSERT(args.length() == 1);
7808
7809 CONVERT_CHECKED(String, string, args[0]);
7810 StringInputBuffer buffer(string);
7811 while (buffer.has_more()) {
7812 uint16_t character = buffer.GetNext();
7813 PrintF("%c", character);
7814 }
7815 return string;
7816}
7817
ager@chromium.org5ec48922009-05-05 07:25:34 +00007818// Moves all own elements of an object, that are below a limit, to positions
7819// starting at zero. All undefined values are placed after non-undefined values,
7820// and are followed by non-existing element. Does not change the length
7821// property.
7822// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007823static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007824 ASSERT(args.length() == 2);
7825 CONVERT_CHECKED(JSObject, object, args[0]);
7826 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
7827 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007828}
7829
7830
7831// Move contents of argument 0 (an array) to argument 1 (an array)
7832static Object* Runtime_MoveArrayContents(Arguments args) {
7833 ASSERT(args.length() == 2);
7834 CONVERT_CHECKED(JSArray, from, args[0]);
7835 CONVERT_CHECKED(JSArray, to, args[1]);
7836 to->SetContent(FixedArray::cast(from->elements()));
7837 to->set_length(from->length());
7838 from->SetContent(Heap::empty_fixed_array());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007839 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007840 return to;
7841}
7842
7843
7844// How many elements does this array have?
7845static Object* Runtime_EstimateNumberOfElements(Arguments args) {
7846 ASSERT(args.length() == 1);
7847 CONVERT_CHECKED(JSArray, array, args[0]);
7848 HeapObject* elements = array->elements();
7849 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007850 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007851 } else {
7852 return array->length();
7853 }
7854}
7855
7856
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007857static Object* Runtime_SwapElements(Arguments args) {
7858 HandleScope handle_scope;
7859
7860 ASSERT_EQ(3, args.length());
7861
ager@chromium.orgac091b72010-05-05 07:34:42 +00007862 CONVERT_ARG_CHECKED(JSObject, object, 0);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007863 Handle<Object> key1 = args.at<Object>(1);
7864 Handle<Object> key2 = args.at<Object>(2);
7865
7866 uint32_t index1, index2;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007867 if (!key1->ToArrayIndex(&index1)
7868 || !key2->ToArrayIndex(&index2)) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00007869 return Top::ThrowIllegalOperation();
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007870 }
7871
ager@chromium.orgac091b72010-05-05 07:34:42 +00007872 Handle<JSObject> jsobject = Handle<JSObject>::cast(object);
7873 Handle<Object> tmp1 = GetElement(jsobject, index1);
7874 Handle<Object> tmp2 = GetElement(jsobject, index2);
7875
7876 SetElement(jsobject, index1, tmp2);
7877 SetElement(jsobject, index2, tmp1);
7878
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007879 return Heap::undefined_value();
7880}
7881
7882
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007883// Returns an array that tells you where in the [0, length) interval an array
7884// might have elements. Can either return keys or intervals. Keys can have
7885// gaps in (undefined). Intervals can also span over some undefined keys.
7886static Object* Runtime_GetArrayKeys(Arguments args) {
7887 ASSERT(args.length() == 2);
7888 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007889 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007890 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007891 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007892 // Create an array and get all the keys into it, then remove all the
7893 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007894 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007895 int keys_length = keys->length();
7896 for (int i = 0; i < keys_length; i++) {
7897 Object* key = keys->get(i);
7898 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007899 if (!key->ToArrayIndex(&index) || index >= length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007900 // Zap invalid keys.
7901 keys->set_undefined(i);
7902 }
7903 }
7904 return *Factory::NewJSArrayWithElements(keys);
7905 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007906 ASSERT(array->HasFastElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007907 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
7908 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007909 single_interval->set(0, Smi::FromInt(-1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007910 uint32_t actual_length =
7911 static_cast<uint32_t>(FixedArray::cast(array->elements())->length());
ager@chromium.org5ec48922009-05-05 07:25:34 +00007912 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007913 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00007914 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007915 single_interval->set(1, *length_object);
7916 return *Factory::NewJSArrayWithElements(single_interval);
7917 }
7918}
7919
7920
7921// DefineAccessor takes an optional final argument which is the
7922// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
7923// to the way accessors are implemented, it is set for both the getter
7924// and setter on the first call to DefineAccessor and ignored on
7925// subsequent calls.
7926static Object* Runtime_DefineAccessor(Arguments args) {
7927 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
7928 // Compute attributes.
7929 PropertyAttributes attributes = NONE;
7930 if (args.length() == 5) {
7931 CONVERT_CHECKED(Smi, attrs, args[4]);
7932 int value = attrs->value();
7933 // Only attribute bits should be set.
7934 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
7935 attributes = static_cast<PropertyAttributes>(value);
7936 }
7937
7938 CONVERT_CHECKED(JSObject, obj, args[0]);
7939 CONVERT_CHECKED(String, name, args[1]);
7940 CONVERT_CHECKED(Smi, flag, args[2]);
7941 CONVERT_CHECKED(JSFunction, fun, args[3]);
7942 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
7943}
7944
7945
7946static Object* Runtime_LookupAccessor(Arguments args) {
7947 ASSERT(args.length() == 3);
7948 CONVERT_CHECKED(JSObject, obj, args[0]);
7949 CONVERT_CHECKED(String, name, args[1]);
7950 CONVERT_CHECKED(Smi, flag, args[2]);
7951 return obj->LookupAccessor(name, flag->value() == 0);
7952}
7953
7954
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007955#ifdef ENABLE_DEBUGGER_SUPPORT
7956static Object* Runtime_DebugBreak(Arguments args) {
7957 ASSERT(args.length() == 0);
7958 return Execution::DebugBreakHelper();
7959}
7960
7961
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007962// Helper functions for wrapping and unwrapping stack frame ids.
7963static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007964 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007965 return Smi::FromInt(id >> 2);
7966}
7967
7968
7969static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
7970 return static_cast<StackFrame::Id>(wrapped->value() << 2);
7971}
7972
7973
7974// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00007975// args[0]: debug event listener function to set or null or undefined for
7976// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007977// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00007978static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007979 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007980 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
7981 args[0]->IsUndefined() ||
7982 args[0]->IsNull());
7983 Handle<Object> callback = args.at<Object>(0);
7984 Handle<Object> data = args.at<Object>(1);
7985 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007986
7987 return Heap::undefined_value();
7988}
7989
7990
7991static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007992 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007993 StackGuard::DebugBreak();
7994 return Heap::undefined_value();
7995}
7996
7997
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007998static Object* DebugLookupResultValue(Object* receiver, String* name,
7999 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00008000 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008001 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008002 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008003 case NORMAL:
8004 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008005 if (value->IsTheHole()) {
8006 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008007 }
8008 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008009 case FIELD:
8010 value =
8011 JSObject::cast(
8012 result->holder())->FastPropertyAt(result->GetFieldIndex());
8013 if (value->IsTheHole()) {
8014 return Heap::undefined_value();
8015 }
8016 return value;
8017 case CONSTANT_FUNCTION:
8018 return result->GetConstantFunction();
8019 case CALLBACKS: {
8020 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008021 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008022 value = receiver->GetPropertyWithCallback(
8023 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00008024 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008025 value = Top::pending_exception();
8026 Top::clear_pending_exception();
8027 if (caught_exception != NULL) {
8028 *caught_exception = true;
8029 }
8030 }
8031 return value;
8032 } else {
8033 return Heap::undefined_value();
8034 }
8035 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008036 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008037 case MAP_TRANSITION:
8038 case CONSTANT_TRANSITION:
8039 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008040 return Heap::undefined_value();
8041 default:
8042 UNREACHABLE();
8043 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008044 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008045 return Heap::undefined_value();
8046}
8047
8048
ager@chromium.org32912102009-01-16 10:38:43 +00008049// Get debugger related details for an object property.
8050// args[0]: object holding property
8051// args[1]: name of the property
8052//
8053// The array returned contains the following information:
8054// 0: Property value
8055// 1: Property details
8056// 2: Property value is exception
8057// 3: Getter function if defined
8058// 4: Setter function if defined
8059// Items 2-4 are only filled if the property has either a getter or a setter
8060// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00008061static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008062 HandleScope scope;
8063
8064 ASSERT(args.length() == 2);
8065
8066 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8067 CONVERT_ARG_CHECKED(String, name, 1);
8068
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008069 // Make sure to set the current context to the context before the debugger was
8070 // entered (if the debugger is entered). The reason for switching context here
8071 // is that for some property lookups (accessors and interceptors) callbacks
8072 // into the embedding application can occour, and the embedding application
8073 // could have the assumption that its own global context is the current
8074 // context and not some internal debugger context.
8075 SaveContext save;
8076 if (Debug::InDebugger()) {
8077 Top::set_context(*Debug::debugger_entry()->GetContext());
8078 }
8079
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008080 // Skip the global proxy as it has no properties and always delegates to the
8081 // real global object.
8082 if (obj->IsJSGlobalProxy()) {
8083 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
8084 }
8085
8086
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008087 // Check if the name is trivially convertible to an index and get the element
8088 // if so.
8089 uint32_t index;
8090 if (name->AsArrayIndex(&index)) {
8091 Handle<FixedArray> details = Factory::NewFixedArray(2);
8092 details->set(0, Runtime::GetElementOrCharAt(obj, index));
8093 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
8094 return *Factory::NewJSArrayWithElements(details);
8095 }
8096
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008097 // Find the number of objects making up this.
8098 int length = LocalPrototypeChainLength(*obj);
8099
8100 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008101 Handle<JSObject> jsproto = obj;
8102 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008103 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008104 jsproto->LocalLookup(*name, &result);
8105 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008106 // LookupResult is not GC safe as it holds raw object pointers.
8107 // GC can happen later in this code so put the required fields into
8108 // local variables using handles when required for later use.
8109 PropertyType result_type = result.type();
8110 Handle<Object> result_callback_obj;
8111 if (result_type == CALLBACKS) {
8112 result_callback_obj = Handle<Object>(result.GetCallbackObject());
8113 }
8114 Smi* property_details = result.GetPropertyDetails().AsSmi();
8115 // DebugLookupResultValue can cause GC so details from LookupResult needs
8116 // to be copied to handles before this.
8117 bool caught_exception = false;
8118 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
8119 &caught_exception);
8120 if (raw_value->IsFailure()) return raw_value;
8121 Handle<Object> value(raw_value);
8122
8123 // If the callback object is a fixed array then it contains JavaScript
8124 // getter and/or setter.
8125 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
8126 result_callback_obj->IsFixedArray();
8127 Handle<FixedArray> details =
8128 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
8129 details->set(0, *value);
8130 details->set(1, property_details);
8131 if (hasJavaScriptAccessors) {
8132 details->set(2,
8133 caught_exception ? Heap::true_value()
8134 : Heap::false_value());
8135 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
8136 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
8137 }
8138
8139 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008140 }
8141 if (i < length - 1) {
8142 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
8143 }
8144 }
8145
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008146 return Heap::undefined_value();
8147}
8148
8149
8150static Object* Runtime_DebugGetProperty(Arguments args) {
8151 HandleScope scope;
8152
8153 ASSERT(args.length() == 2);
8154
8155 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8156 CONVERT_ARG_CHECKED(String, name, 1);
8157
8158 LookupResult result;
8159 obj->Lookup(*name, &result);
8160 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008161 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008162 }
8163 return Heap::undefined_value();
8164}
8165
8166
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008167// Return the property type calculated from the property details.
8168// args[0]: smi with property details.
8169static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
8170 ASSERT(args.length() == 1);
8171 CONVERT_CHECKED(Smi, details, args[0]);
8172 PropertyType type = PropertyDetails(details).type();
8173 return Smi::FromInt(static_cast<int>(type));
8174}
8175
8176
8177// Return the property attribute calculated from the property details.
8178// args[0]: smi with property details.
8179static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
8180 ASSERT(args.length() == 1);
8181 CONVERT_CHECKED(Smi, details, args[0]);
8182 PropertyAttributes attributes = PropertyDetails(details).attributes();
8183 return Smi::FromInt(static_cast<int>(attributes));
8184}
8185
8186
8187// Return the property insertion index calculated from the property details.
8188// args[0]: smi with property details.
8189static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
8190 ASSERT(args.length() == 1);
8191 CONVERT_CHECKED(Smi, details, args[0]);
8192 int index = PropertyDetails(details).index();
8193 return Smi::FromInt(index);
8194}
8195
8196
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008197// Return property value from named interceptor.
8198// args[0]: object
8199// args[1]: property name
8200static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
8201 HandleScope scope;
8202 ASSERT(args.length() == 2);
8203 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8204 RUNTIME_ASSERT(obj->HasNamedInterceptor());
8205 CONVERT_ARG_CHECKED(String, name, 1);
8206
8207 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008208 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008209}
8210
8211
8212// Return element value from indexed interceptor.
8213// args[0]: object
8214// args[1]: index
8215static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
8216 HandleScope scope;
8217 ASSERT(args.length() == 2);
8218 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8219 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
8220 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
8221
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008222 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008223}
8224
8225
8226static Object* Runtime_CheckExecutionState(Arguments args) {
8227 ASSERT(args.length() >= 1);
8228 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00008229 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008230 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008231 return Top::Throw(Heap::illegal_execution_state_symbol());
8232 }
8233
8234 return Heap::true_value();
8235}
8236
8237
8238static Object* Runtime_GetFrameCount(Arguments args) {
8239 HandleScope scope;
8240 ASSERT(args.length() == 1);
8241
8242 // Check arguments.
8243 Object* result = Runtime_CheckExecutionState(args);
8244 if (result->IsFailure()) return result;
8245
8246 // Count all frames which are relevant to debugging stack trace.
8247 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008248 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008249 if (id == StackFrame::NO_ID) {
8250 // If there is no JavaScript stack frame count is 0.
8251 return Smi::FromInt(0);
8252 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008253 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
8254 return Smi::FromInt(n);
8255}
8256
8257
8258static const int kFrameDetailsFrameIdIndex = 0;
8259static const int kFrameDetailsReceiverIndex = 1;
8260static const int kFrameDetailsFunctionIndex = 2;
8261static const int kFrameDetailsArgumentCountIndex = 3;
8262static const int kFrameDetailsLocalCountIndex = 4;
8263static const int kFrameDetailsSourcePositionIndex = 5;
8264static const int kFrameDetailsConstructCallIndex = 6;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008265static const int kFrameDetailsAtReturnIndex = 7;
8266static const int kFrameDetailsDebuggerFrameIndex = 8;
8267static const int kFrameDetailsFirstDynamicIndex = 9;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008268
8269// Return an array with frame details
8270// args[0]: number: break id
8271// args[1]: number: frame index
8272//
8273// The array returned contains the following information:
8274// 0: Frame id
8275// 1: Receiver
8276// 2: Function
8277// 3: Argument count
8278// 4: Local count
8279// 5: Source position
8280// 6: Constructor call
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008281// 7: Is at return
8282// 8: Debugger frame
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008283// Arguments name, value
8284// Locals name, value
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008285// Return value if any
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008286static Object* Runtime_GetFrameDetails(Arguments args) {
8287 HandleScope scope;
8288 ASSERT(args.length() == 2);
8289
8290 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008291 Object* check = Runtime_CheckExecutionState(args);
8292 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008293 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8294
8295 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008296 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008297 if (id == StackFrame::NO_ID) {
8298 // If there are no JavaScript stack frames return undefined.
8299 return Heap::undefined_value();
8300 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008301 int count = 0;
8302 JavaScriptFrameIterator it(id);
8303 for (; !it.done(); it.Advance()) {
8304 if (count == index) break;
8305 count++;
8306 }
8307 if (it.done()) return Heap::undefined_value();
8308
8309 // Traverse the saved contexts chain to find the active context for the
8310 // selected frame.
8311 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008312 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008313 save = save->prev();
8314 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008315 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008316
8317 // Get the frame id.
8318 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
8319
8320 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008321 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008322
8323 // Check for constructor frame.
8324 bool constructor = it.frame()->IsConstructor();
8325
8326 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008327 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008328 ScopeInfo<> info(*code);
8329
8330 // Get the context.
8331 Handle<Context> context(Context::cast(it.frame()->context()));
8332
8333 // Get the locals names and values into a temporary array.
8334 //
8335 // TODO(1240907): Hide compiler-introduced stack variables
8336 // (e.g. .result)? For users of the debugger, they will probably be
8337 // confusing.
8338 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
8339 for (int i = 0; i < info.NumberOfLocals(); i++) {
8340 // Name of the local.
8341 locals->set(i * 2, *info.LocalName(i));
8342
8343 // Fetch the value of the local - either from the stack or from a
8344 // heap-allocated context.
8345 if (i < info.number_of_stack_slots()) {
8346 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
8347 } else {
8348 Handle<String> name = info.LocalName(i);
8349 // Traverse the context chain to the function context as all local
8350 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008351 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008352 context = Handle<Context>(context->previous());
8353 }
8354 ASSERT(context->is_function_context());
8355 locals->set(i * 2 + 1,
8356 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
8357 NULL)));
8358 }
8359 }
8360
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008361 // Check whether this frame is positioned at return.
8362 int at_return = (index == 0) ? Debug::IsBreakAtReturn(it.frame()) : false;
8363
8364 // If positioned just before return find the value to be returned and add it
8365 // to the frame information.
8366 Handle<Object> return_value = Factory::undefined_value();
8367 if (at_return) {
8368 StackFrameIterator it2;
8369 Address internal_frame_sp = NULL;
8370 while (!it2.done()) {
8371 if (it2.frame()->is_internal()) {
8372 internal_frame_sp = it2.frame()->sp();
8373 } else {
8374 if (it2.frame()->is_java_script()) {
8375 if (it2.frame()->id() == it.frame()->id()) {
8376 // The internal frame just before the JavaScript frame contains the
8377 // value to return on top. A debug break at return will create an
8378 // internal frame to store the return value (eax/rax/r0) before
8379 // entering the debug break exit frame.
8380 if (internal_frame_sp != NULL) {
8381 return_value =
8382 Handle<Object>(Memory::Object_at(internal_frame_sp));
8383 break;
8384 }
8385 }
8386 }
8387
8388 // Indicate that the previous frame was not an internal frame.
8389 internal_frame_sp = NULL;
8390 }
8391 it2.Advance();
8392 }
8393 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008394
8395 // Now advance to the arguments adapter frame (if any). It contains all
8396 // the provided parameters whereas the function frame always have the number
8397 // of arguments matching the functions parameters. The rest of the
8398 // information (except for what is collected above) is the same.
8399 it.AdvanceToArgumentsFrame();
8400
8401 // Find the number of arguments to fill. At least fill the number of
8402 // parameters for the function and fill more if more parameters are provided.
8403 int argument_count = info.number_of_parameters();
8404 if (argument_count < it.frame()->GetProvidedParametersCount()) {
8405 argument_count = it.frame()->GetProvidedParametersCount();
8406 }
8407
8408 // Calculate the size of the result.
8409 int details_size = kFrameDetailsFirstDynamicIndex +
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008410 2 * (argument_count + info.NumberOfLocals()) +
8411 (at_return ? 1 : 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008412 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8413
8414 // Add the frame id.
8415 details->set(kFrameDetailsFrameIdIndex, *frame_id);
8416
8417 // Add the function (same as in function frame).
8418 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
8419
8420 // Add the arguments count.
8421 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
8422
8423 // Add the locals count
8424 details->set(kFrameDetailsLocalCountIndex,
8425 Smi::FromInt(info.NumberOfLocals()));
8426
8427 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00008428 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008429 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
8430 } else {
8431 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
8432 }
8433
8434 // Add the constructor information.
8435 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
8436
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008437 // Add the at return information.
8438 details->set(kFrameDetailsAtReturnIndex, Heap::ToBoolean(at_return));
8439
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008440 // Add information on whether this frame is invoked in the debugger context.
8441 details->set(kFrameDetailsDebuggerFrameIndex,
8442 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
8443
8444 // Fill the dynamic part.
8445 int details_index = kFrameDetailsFirstDynamicIndex;
8446
8447 // Add arguments name and value.
8448 for (int i = 0; i < argument_count; i++) {
8449 // Name of the argument.
8450 if (i < info.number_of_parameters()) {
8451 details->set(details_index++, *info.parameter_name(i));
8452 } else {
8453 details->set(details_index++, Heap::undefined_value());
8454 }
8455
8456 // Parameter value.
8457 if (i < it.frame()->GetProvidedParametersCount()) {
8458 details->set(details_index++, it.frame()->GetParameter(i));
8459 } else {
8460 details->set(details_index++, Heap::undefined_value());
8461 }
8462 }
8463
8464 // Add locals name and value from the temporary copy from the function frame.
8465 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
8466 details->set(details_index++, locals->get(i));
8467 }
8468
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008469 // Add the value being returned.
8470 if (at_return) {
8471 details->set(details_index++, *return_value);
8472 }
8473
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008474 // Add the receiver (same as in function frame).
8475 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
8476 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
8477 Handle<Object> receiver(it.frame()->receiver());
8478 if (!receiver->IsJSObject()) {
8479 // If the receiver is NOT a JSObject we have hit an optimization
8480 // where a value object is not converted into a wrapped JS objects.
8481 // To hide this optimization from the debugger, we wrap the receiver
8482 // by creating correct wrapper object based on the calling frame's
8483 // global context.
8484 it.Advance();
8485 Handle<Context> calling_frames_global_context(
8486 Context::cast(Context::cast(it.frame()->context())->global_context()));
8487 receiver = Factory::ToObject(receiver, calling_frames_global_context);
8488 }
8489 details->set(kFrameDetailsReceiverIndex, *receiver);
8490
8491 ASSERT_EQ(details_size, details_index);
8492 return *Factory::NewJSArrayWithElements(details);
8493}
8494
8495
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008496// Copy all the context locals into an object used to materialize a scope.
8497static void CopyContextLocalsToScopeObject(Handle<Code> code,
8498 ScopeInfo<>& scope_info,
8499 Handle<Context> context,
8500 Handle<JSObject> scope_object) {
8501 // Fill all context locals to the context extension.
8502 for (int i = Context::MIN_CONTEXT_SLOTS;
8503 i < scope_info.number_of_context_slots();
8504 i++) {
8505 int context_index =
8506 ScopeInfo<>::ContextSlotIndex(*code,
8507 *scope_info.context_slot_name(i),
8508 NULL);
8509
8510 // Don't include the arguments shadow (.arguments) context variable.
8511 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
8512 SetProperty(scope_object,
8513 scope_info.context_slot_name(i),
8514 Handle<Object>(context->get(context_index)), NONE);
8515 }
8516 }
8517}
8518
8519
8520// Create a plain JSObject which materializes the local scope for the specified
8521// frame.
8522static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
8523 Handle<JSFunction> function(JSFunction::cast(frame->function()));
8524 Handle<Code> code(function->code());
8525 ScopeInfo<> scope_info(*code);
8526
8527 // Allocate and initialize a JSObject with all the arguments, stack locals
8528 // heap locals and extension properties of the debugged function.
8529 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
8530
8531 // First fill all parameters.
8532 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8533 SetProperty(local_scope,
8534 scope_info.parameter_name(i),
8535 Handle<Object>(frame->GetParameter(i)), NONE);
8536 }
8537
8538 // Second fill all stack locals.
8539 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
8540 SetProperty(local_scope,
8541 scope_info.stack_slot_name(i),
8542 Handle<Object>(frame->GetExpression(i)), NONE);
8543 }
8544
8545 // Third fill all context locals.
8546 Handle<Context> frame_context(Context::cast(frame->context()));
8547 Handle<Context> function_context(frame_context->fcontext());
8548 CopyContextLocalsToScopeObject(code, scope_info,
8549 function_context, local_scope);
8550
8551 // Finally copy any properties from the function context extension. This will
8552 // be variables introduced by eval.
8553 if (function_context->closure() == *function) {
8554 if (function_context->has_extension() &&
8555 !function_context->IsGlobalContext()) {
8556 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008557 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008558 for (int i = 0; i < keys->length(); i++) {
8559 // Names of variables introduced by eval are strings.
8560 ASSERT(keys->get(i)->IsString());
8561 Handle<String> key(String::cast(keys->get(i)));
8562 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
8563 }
8564 }
8565 }
8566 return local_scope;
8567}
8568
8569
8570// Create a plain JSObject which materializes the closure content for the
8571// context.
8572static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
8573 ASSERT(context->is_function_context());
8574
8575 Handle<Code> code(context->closure()->code());
8576 ScopeInfo<> scope_info(*code);
8577
8578 // Allocate and initialize a JSObject with all the content of theis function
8579 // closure.
8580 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
8581
8582 // Check whether the arguments shadow object exists.
8583 int arguments_shadow_index =
8584 ScopeInfo<>::ContextSlotIndex(*code,
8585 Heap::arguments_shadow_symbol(),
8586 NULL);
8587 if (arguments_shadow_index >= 0) {
8588 // In this case all the arguments are available in the arguments shadow
8589 // object.
8590 Handle<JSObject> arguments_shadow(
8591 JSObject::cast(context->get(arguments_shadow_index)));
8592 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8593 SetProperty(closure_scope,
8594 scope_info.parameter_name(i),
8595 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
8596 }
8597 }
8598
8599 // Fill all context locals to the context extension.
8600 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
8601
8602 // Finally copy any properties from the function context extension. This will
8603 // be variables introduced by eval.
8604 if (context->has_extension()) {
8605 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008606 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008607 for (int i = 0; i < keys->length(); i++) {
8608 // Names of variables introduced by eval are strings.
8609 ASSERT(keys->get(i)->IsString());
8610 Handle<String> key(String::cast(keys->get(i)));
8611 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
8612 }
8613 }
8614
8615 return closure_scope;
8616}
8617
8618
8619// Iterate over the actual scopes visible from a stack frame. All scopes are
8620// backed by an actual context except the local scope, which is inserted
8621// "artifically" in the context chain.
8622class ScopeIterator {
8623 public:
8624 enum ScopeType {
8625 ScopeTypeGlobal = 0,
8626 ScopeTypeLocal,
8627 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00008628 ScopeTypeClosure,
8629 // Every catch block contains an implicit with block (its parameter is
8630 // a JSContextExtensionObject) that extends current scope with a variable
8631 // holding exception object. Such with blocks are treated as scopes of their
8632 // own type.
8633 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008634 };
8635
8636 explicit ScopeIterator(JavaScriptFrame* frame)
8637 : frame_(frame),
8638 function_(JSFunction::cast(frame->function())),
8639 context_(Context::cast(frame->context())),
8640 local_done_(false),
8641 at_local_(false) {
8642
8643 // Check whether the first scope is actually a local scope.
8644 if (context_->IsGlobalContext()) {
8645 // If there is a stack slot for .result then this local scope has been
8646 // created for evaluating top level code and it is not a real local scope.
8647 // Checking for the existence of .result seems fragile, but the scope info
8648 // saved with the code object does not otherwise have that information.
8649 Handle<Code> code(function_->code());
8650 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
8651 at_local_ = index < 0;
8652 } else if (context_->is_function_context()) {
8653 at_local_ = true;
8654 }
8655 }
8656
8657 // More scopes?
8658 bool Done() { return context_.is_null(); }
8659
8660 // Move to the next scope.
8661 void Next() {
8662 // If at a local scope mark the local scope as passed.
8663 if (at_local_) {
8664 at_local_ = false;
8665 local_done_ = true;
8666
8667 // If the current context is not associated with the local scope the
8668 // current context is the next real scope, so don't move to the next
8669 // context in this case.
8670 if (context_->closure() != *function_) {
8671 return;
8672 }
8673 }
8674
8675 // The global scope is always the last in the chain.
8676 if (context_->IsGlobalContext()) {
8677 context_ = Handle<Context>();
8678 return;
8679 }
8680
8681 // Move to the next context.
8682 if (context_->is_function_context()) {
8683 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
8684 } else {
8685 context_ = Handle<Context>(context_->previous());
8686 }
8687
8688 // If passing the local scope indicate that the current scope is now the
8689 // local scope.
8690 if (!local_done_ &&
8691 (context_->IsGlobalContext() || (context_->is_function_context()))) {
8692 at_local_ = true;
8693 }
8694 }
8695
8696 // Return the type of the current scope.
8697 int Type() {
8698 if (at_local_) {
8699 return ScopeTypeLocal;
8700 }
8701 if (context_->IsGlobalContext()) {
8702 ASSERT(context_->global()->IsGlobalObject());
8703 return ScopeTypeGlobal;
8704 }
8705 if (context_->is_function_context()) {
8706 return ScopeTypeClosure;
8707 }
8708 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00008709 // Current scope is either an explicit with statement or a with statement
8710 // implicitely generated for a catch block.
8711 // If the extension object here is a JSContextExtensionObject then
8712 // current with statement is one frome a catch block otherwise it's a
8713 // regular with statement.
8714 if (context_->extension()->IsJSContextExtensionObject()) {
8715 return ScopeTypeCatch;
8716 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008717 return ScopeTypeWith;
8718 }
8719
8720 // Return the JavaScript object with the content of the current scope.
8721 Handle<JSObject> ScopeObject() {
8722 switch (Type()) {
8723 case ScopeIterator::ScopeTypeGlobal:
8724 return Handle<JSObject>(CurrentContext()->global());
8725 break;
8726 case ScopeIterator::ScopeTypeLocal:
8727 // Materialize the content of the local scope into a JSObject.
8728 return MaterializeLocalScope(frame_);
8729 break;
8730 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00008731 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008732 // Return the with object.
8733 return Handle<JSObject>(CurrentContext()->extension());
8734 break;
8735 case ScopeIterator::ScopeTypeClosure:
8736 // Materialize the content of the closure scope into a JSObject.
8737 return MaterializeClosure(CurrentContext());
8738 break;
8739 }
8740 UNREACHABLE();
8741 return Handle<JSObject>();
8742 }
8743
8744 // Return the context for this scope. For the local context there might not
8745 // be an actual context.
8746 Handle<Context> CurrentContext() {
8747 if (at_local_ && context_->closure() != *function_) {
8748 return Handle<Context>();
8749 }
8750 return context_;
8751 }
8752
8753#ifdef DEBUG
8754 // Debug print of the content of the current scope.
8755 void DebugPrint() {
8756 switch (Type()) {
8757 case ScopeIterator::ScopeTypeGlobal:
8758 PrintF("Global:\n");
8759 CurrentContext()->Print();
8760 break;
8761
8762 case ScopeIterator::ScopeTypeLocal: {
8763 PrintF("Local:\n");
8764 Handle<Code> code(function_->code());
8765 ScopeInfo<> scope_info(*code);
8766 scope_info.Print();
8767 if (!CurrentContext().is_null()) {
8768 CurrentContext()->Print();
8769 if (CurrentContext()->has_extension()) {
8770 Handle<JSObject> extension =
8771 Handle<JSObject>(CurrentContext()->extension());
8772 if (extension->IsJSContextExtensionObject()) {
8773 extension->Print();
8774 }
8775 }
8776 }
8777 break;
8778 }
8779
8780 case ScopeIterator::ScopeTypeWith: {
8781 PrintF("With:\n");
8782 Handle<JSObject> extension =
8783 Handle<JSObject>(CurrentContext()->extension());
8784 extension->Print();
8785 break;
8786 }
8787
ager@chromium.orga1645e22009-09-09 19:27:10 +00008788 case ScopeIterator::ScopeTypeCatch: {
8789 PrintF("Catch:\n");
8790 Handle<JSObject> extension =
8791 Handle<JSObject>(CurrentContext()->extension());
8792 extension->Print();
8793 break;
8794 }
8795
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008796 case ScopeIterator::ScopeTypeClosure: {
8797 PrintF("Closure:\n");
8798 CurrentContext()->Print();
8799 if (CurrentContext()->has_extension()) {
8800 Handle<JSObject> extension =
8801 Handle<JSObject>(CurrentContext()->extension());
8802 if (extension->IsJSContextExtensionObject()) {
8803 extension->Print();
8804 }
8805 }
8806 break;
8807 }
8808
8809 default:
8810 UNREACHABLE();
8811 }
8812 PrintF("\n");
8813 }
8814#endif
8815
8816 private:
8817 JavaScriptFrame* frame_;
8818 Handle<JSFunction> function_;
8819 Handle<Context> context_;
8820 bool local_done_;
8821 bool at_local_;
8822
8823 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
8824};
8825
8826
8827static Object* Runtime_GetScopeCount(Arguments args) {
8828 HandleScope scope;
8829 ASSERT(args.length() == 2);
8830
8831 // Check arguments.
8832 Object* check = Runtime_CheckExecutionState(args);
8833 if (check->IsFailure()) return check;
8834 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8835
8836 // Get the frame where the debugging is performed.
8837 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8838 JavaScriptFrameIterator it(id);
8839 JavaScriptFrame* frame = it.frame();
8840
8841 // Count the visible scopes.
8842 int n = 0;
8843 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8844 n++;
8845 }
8846
8847 return Smi::FromInt(n);
8848}
8849
8850
8851static const int kScopeDetailsTypeIndex = 0;
8852static const int kScopeDetailsObjectIndex = 1;
8853static const int kScopeDetailsSize = 2;
8854
8855// Return an array with scope details
8856// args[0]: number: break id
8857// args[1]: number: frame index
8858// args[2]: number: scope index
8859//
8860// The array returned contains the following information:
8861// 0: Scope type
8862// 1: Scope object
8863static Object* Runtime_GetScopeDetails(Arguments args) {
8864 HandleScope scope;
8865 ASSERT(args.length() == 3);
8866
8867 // Check arguments.
8868 Object* check = Runtime_CheckExecutionState(args);
8869 if (check->IsFailure()) return check;
8870 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8871 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
8872
8873 // Get the frame where the debugging is performed.
8874 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8875 JavaScriptFrameIterator frame_it(id);
8876 JavaScriptFrame* frame = frame_it.frame();
8877
8878 // Find the requested scope.
8879 int n = 0;
8880 ScopeIterator it(frame);
8881 for (; !it.Done() && n < index; it.Next()) {
8882 n++;
8883 }
8884 if (it.Done()) {
8885 return Heap::undefined_value();
8886 }
8887
8888 // Calculate the size of the result.
8889 int details_size = kScopeDetailsSize;
8890 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8891
8892 // Fill in scope details.
8893 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
8894 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
8895
8896 return *Factory::NewJSArrayWithElements(details);
8897}
8898
8899
8900static Object* Runtime_DebugPrintScopes(Arguments args) {
8901 HandleScope scope;
8902 ASSERT(args.length() == 0);
8903
8904#ifdef DEBUG
8905 // Print the scopes for the top frame.
8906 StackFrameLocator locator;
8907 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
8908 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8909 it.DebugPrint();
8910 }
8911#endif
8912 return Heap::undefined_value();
8913}
8914
8915
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008916static Object* Runtime_GetCFrames(Arguments args) {
8917 HandleScope scope;
8918 ASSERT(args.length() == 1);
8919 Object* result = Runtime_CheckExecutionState(args);
8920 if (result->IsFailure()) return result;
8921
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008922#if V8_HOST_ARCH_64_BIT
8923 UNIMPLEMENTED();
8924 return Heap::undefined_value();
8925#else
8926
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008927 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008928 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
8929 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008930 if (frames_count == OS::kStackWalkError) {
8931 return Heap::undefined_value();
8932 }
8933
8934 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
8935 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
8936 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
8937 for (int i = 0; i < frames_count; i++) {
8938 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
8939 frame_value->SetProperty(
8940 *address_str,
8941 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
8942 NONE);
8943
8944 // Get the stack walk text for this frame.
8945 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008946 int frame_text_length = StrLength(frames[i].text);
8947 if (frame_text_length > 0) {
8948 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008949 frame_text = Factory::NewStringFromAscii(str);
8950 }
8951
8952 if (!frame_text.is_null()) {
8953 frame_value->SetProperty(*text_str, *frame_text, NONE);
8954 }
8955
8956 frames_array->set(i, *frame_value);
8957 }
8958 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008959#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008960}
8961
8962
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00008963static Object* Runtime_GetThreadCount(Arguments args) {
8964 HandleScope scope;
8965 ASSERT(args.length() == 1);
8966
8967 // Check arguments.
8968 Object* result = Runtime_CheckExecutionState(args);
8969 if (result->IsFailure()) return result;
8970
8971 // Count all archived V8 threads.
8972 int n = 0;
8973 for (ThreadState* thread = ThreadState::FirstInUse();
8974 thread != NULL;
8975 thread = thread->Next()) {
8976 n++;
8977 }
8978
8979 // Total number of threads is current thread and archived threads.
8980 return Smi::FromInt(n + 1);
8981}
8982
8983
8984static const int kThreadDetailsCurrentThreadIndex = 0;
8985static const int kThreadDetailsThreadIdIndex = 1;
8986static const int kThreadDetailsSize = 2;
8987
8988// Return an array with thread details
8989// args[0]: number: break id
8990// args[1]: number: thread index
8991//
8992// The array returned contains the following information:
8993// 0: Is current thread?
8994// 1: Thread id
8995static Object* Runtime_GetThreadDetails(Arguments args) {
8996 HandleScope scope;
8997 ASSERT(args.length() == 2);
8998
8999 // Check arguments.
9000 Object* check = Runtime_CheckExecutionState(args);
9001 if (check->IsFailure()) return check;
9002 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
9003
9004 // Allocate array for result.
9005 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
9006
9007 // Thread index 0 is current thread.
9008 if (index == 0) {
9009 // Fill the details.
9010 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
9011 details->set(kThreadDetailsThreadIdIndex,
9012 Smi::FromInt(ThreadManager::CurrentId()));
9013 } else {
9014 // Find the thread with the requested index.
9015 int n = 1;
9016 ThreadState* thread = ThreadState::FirstInUse();
9017 while (index != n && thread != NULL) {
9018 thread = thread->Next();
9019 n++;
9020 }
9021 if (thread == NULL) {
9022 return Heap::undefined_value();
9023 }
9024
9025 // Fill the details.
9026 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
9027 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
9028 }
9029
9030 // Convert to JS array and return.
9031 return *Factory::NewJSArrayWithElements(details);
9032}
9033
9034
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009035static Object* Runtime_GetBreakLocations(Arguments args) {
9036 HandleScope scope;
9037 ASSERT(args.length() == 1);
9038
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009039 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9040 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009041 // Find the number of break points
9042 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
9043 if (break_locations->IsUndefined()) return Heap::undefined_value();
9044 // Return array as JS array
9045 return *Factory::NewJSArrayWithElements(
9046 Handle<FixedArray>::cast(break_locations));
9047}
9048
9049
9050// Set a break point in a function
9051// args[0]: function
9052// args[1]: number: break source position (within the function source)
9053// args[2]: number: break point object
9054static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
9055 HandleScope scope;
9056 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009057 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9058 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009059 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9060 RUNTIME_ASSERT(source_position >= 0);
9061 Handle<Object> break_point_object_arg = args.at<Object>(2);
9062
9063 // Set break point.
9064 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
9065
9066 return Heap::undefined_value();
9067}
9068
9069
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009070Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
9071 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009072 // Iterate the heap looking for SharedFunctionInfo generated from the
9073 // script. The inner most SharedFunctionInfo containing the source position
9074 // for the requested break point is found.
9075 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
9076 // which is found is not compiled it is compiled and the heap is iterated
9077 // again as the compilation might create inner functions from the newly
9078 // compiled function and the actual requested break point might be in one of
9079 // these functions.
9080 bool done = false;
9081 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00009082 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009083 Handle<SharedFunctionInfo> target;
9084 // The current candidate for the last function in script:
9085 Handle<SharedFunctionInfo> last;
9086 while (!done) {
9087 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009088 for (HeapObject* obj = iterator.next();
9089 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009090 if (obj->IsSharedFunctionInfo()) {
9091 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
9092 if (shared->script() == *script) {
9093 // If the SharedFunctionInfo found has the requested script data and
9094 // contains the source position it is a candidate.
9095 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00009096 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009097 start_position = shared->start_position();
9098 }
9099 if (start_position <= position &&
9100 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00009101 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009102 // candidate this is the new candidate.
9103 if (target.is_null()) {
9104 target_start_position = start_position;
9105 target = shared;
9106 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00009107 if (target_start_position == start_position &&
9108 shared->end_position() == target->end_position()) {
9109 // If a top-level function contain only one function
9110 // declartion the source for the top-level and the function is
9111 // the same. In that case prefer the non top-level function.
9112 if (!shared->is_toplevel()) {
9113 target_start_position = start_position;
9114 target = shared;
9115 }
9116 } else if (target_start_position <= start_position &&
9117 shared->end_position() <= target->end_position()) {
9118 // This containment check includes equality as a function inside
9119 // a top-level function can share either start or end position
9120 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009121 target_start_position = start_position;
9122 target = shared;
9123 }
9124 }
9125 }
9126
9127 // Keep track of the last function in the script.
9128 if (last.is_null() ||
9129 shared->end_position() > last->start_position()) {
9130 last = shared;
9131 }
9132 }
9133 }
9134 }
9135
9136 // Make sure some candidate is selected.
9137 if (target.is_null()) {
9138 if (!last.is_null()) {
9139 // Position after the last function - use last.
9140 target = last;
9141 } else {
9142 // Unable to find function - possibly script without any function.
9143 return Heap::undefined_value();
9144 }
9145 }
9146
9147 // If the candidate found is compiled we are done. NOTE: when lazy
9148 // compilation of inner functions is introduced some additional checking
9149 // needs to be done here to compile inner functions.
9150 done = target->is_compiled();
9151 if (!done) {
9152 // If the candidate is not compiled compile it to reveal any inner
9153 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009154 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009155 }
9156 }
9157
9158 return *target;
9159}
9160
9161
9162// Change the state of a break point in a script. NOTE: Regarding performance
9163// see the NOTE for GetScriptFromScriptData.
9164// args[0]: script to set break point in
9165// args[1]: number: break source position (within the script source)
9166// args[2]: number: break point object
9167static Object* Runtime_SetScriptBreakPoint(Arguments args) {
9168 HandleScope scope;
9169 ASSERT(args.length() == 3);
9170 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
9171 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9172 RUNTIME_ASSERT(source_position >= 0);
9173 Handle<Object> break_point_object_arg = args.at<Object>(2);
9174
9175 // Get the script from the script wrapper.
9176 RUNTIME_ASSERT(wrapper->value()->IsScript());
9177 Handle<Script> script(Script::cast(wrapper->value()));
9178
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009179 Object* result = Runtime::FindSharedFunctionInfoInScript(
9180 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009181 if (!result->IsUndefined()) {
9182 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
9183 // Find position within function. The script position might be before the
9184 // source position of the first function.
9185 int position;
9186 if (shared->start_position() > source_position) {
9187 position = 0;
9188 } else {
9189 position = source_position - shared->start_position();
9190 }
9191 Debug::SetBreakPoint(shared, position, break_point_object_arg);
9192 }
9193 return Heap::undefined_value();
9194}
9195
9196
9197// Clear a break point
9198// args[0]: number: break point object
9199static Object* Runtime_ClearBreakPoint(Arguments args) {
9200 HandleScope scope;
9201 ASSERT(args.length() == 1);
9202 Handle<Object> break_point_object_arg = args.at<Object>(0);
9203
9204 // Clear break point.
9205 Debug::ClearBreakPoint(break_point_object_arg);
9206
9207 return Heap::undefined_value();
9208}
9209
9210
9211// Change the state of break on exceptions
9212// args[0]: boolean indicating uncaught exceptions
9213// args[1]: boolean indicating on/off
9214static Object* Runtime_ChangeBreakOnException(Arguments args) {
9215 HandleScope scope;
9216 ASSERT(args.length() == 2);
9217 ASSERT(args[0]->IsNumber());
9218 ASSERT(args[1]->IsBoolean());
9219
9220 // Update break point state
9221 ExceptionBreakType type =
9222 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
9223 bool enable = args[1]->ToBoolean()->IsTrue();
9224 Debug::ChangeBreakOnException(type, enable);
9225 return Heap::undefined_value();
9226}
9227
9228
9229// Prepare for stepping
9230// args[0]: break id for checking execution state
9231// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00009232// args[2]: number of times to perform the step, for step out it is the number
9233// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009234static Object* Runtime_PrepareStep(Arguments args) {
9235 HandleScope scope;
9236 ASSERT(args.length() == 3);
9237 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009238 Object* check = Runtime_CheckExecutionState(args);
9239 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009240 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
9241 return Top::Throw(Heap::illegal_argument_symbol());
9242 }
9243
9244 // Get the step action and check validity.
9245 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
9246 if (step_action != StepIn &&
9247 step_action != StepNext &&
9248 step_action != StepOut &&
9249 step_action != StepInMin &&
9250 step_action != StepMin) {
9251 return Top::Throw(Heap::illegal_argument_symbol());
9252 }
9253
9254 // Get the number of steps.
9255 int step_count = NumberToInt32(args[2]);
9256 if (step_count < 1) {
9257 return Top::Throw(Heap::illegal_argument_symbol());
9258 }
9259
ager@chromium.orga1645e22009-09-09 19:27:10 +00009260 // Clear all current stepping setup.
9261 Debug::ClearStepping();
9262
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009263 // Prepare step.
9264 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
9265 return Heap::undefined_value();
9266}
9267
9268
9269// Clear all stepping set by PrepareStep.
9270static Object* Runtime_ClearStepping(Arguments args) {
9271 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009272 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009273 Debug::ClearStepping();
9274 return Heap::undefined_value();
9275}
9276
9277
9278// Creates a copy of the with context chain. The copy of the context chain is
9279// is linked to the function context supplied.
9280static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
9281 Handle<Context> function_context) {
9282 // At the bottom of the chain. Return the function context to link to.
9283 if (context_chain->is_function_context()) {
9284 return function_context;
9285 }
9286
9287 // Recursively copy the with contexts.
9288 Handle<Context> previous(context_chain->previous());
9289 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
9290 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009291 CopyWithContextChain(function_context, previous),
9292 extension,
9293 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009294}
9295
9296
9297// Helper function to find or create the arguments object for
9298// Runtime_DebugEvaluate.
9299static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
9300 Handle<JSFunction> function,
9301 Handle<Code> code,
9302 const ScopeInfo<>* sinfo,
9303 Handle<Context> function_context) {
9304 // Try to find the value of 'arguments' to pass as parameter. If it is not
9305 // found (that is the debugged function does not reference 'arguments' and
9306 // does not support eval) then create an 'arguments' object.
9307 int index;
9308 if (sinfo->number_of_stack_slots() > 0) {
9309 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
9310 if (index != -1) {
9311 return Handle<Object>(frame->GetExpression(index));
9312 }
9313 }
9314
9315 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
9316 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
9317 NULL);
9318 if (index != -1) {
9319 return Handle<Object>(function_context->get(index));
9320 }
9321 }
9322
9323 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009324 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
9325 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009326
9327 AssertNoAllocation no_gc;
9328 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009329 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009330 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009331 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009332 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009333 return arguments;
9334}
9335
9336
9337// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00009338// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009339// extension part has all the parameters and locals of the function on the
9340// stack frame. A function which calls eval with the code to evaluate is then
9341// compiled in this context and called in this context. As this context
9342// replaces the context of the function on the stack frame a new (empty)
9343// function is created as well to be used as the closure for the context.
9344// This function and the context acts as replacements for the function on the
9345// stack frame presenting the same view of the values of parameters and
9346// local variables as if the piece of JavaScript was evaluated at the point
9347// where the function on the stack frame is currently stopped.
9348static Object* Runtime_DebugEvaluate(Arguments args) {
9349 HandleScope scope;
9350
9351 // Check the execution state and decode arguments frame and source to be
9352 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009353 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009354 Object* check_result = Runtime_CheckExecutionState(args);
9355 if (check_result->IsFailure()) return check_result;
9356 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9357 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009358 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
9359
9360 // Handle the processing of break.
9361 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009362
9363 // Get the frame where the debugging is performed.
9364 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9365 JavaScriptFrameIterator it(id);
9366 JavaScriptFrame* frame = it.frame();
9367 Handle<JSFunction> function(JSFunction::cast(frame->function()));
9368 Handle<Code> code(function->code());
9369 ScopeInfo<> sinfo(*code);
9370
9371 // Traverse the saved contexts chain to find the active context for the
9372 // selected frame.
9373 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009374 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009375 save = save->prev();
9376 }
9377 ASSERT(save != NULL);
9378 SaveContext savex;
9379 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009380
9381 // Create the (empty) function replacing the function on the stack frame for
9382 // the purpose of evaluating in the context created below. It is important
9383 // that this function does not describe any parameters and local variables
9384 // in the context. If it does then this will cause problems with the lookup
9385 // in Context::Lookup, where context slots for parameters and local variables
9386 // are looked at before the extension object.
9387 Handle<JSFunction> go_between =
9388 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
9389 go_between->set_context(function->context());
9390#ifdef DEBUG
9391 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
9392 ASSERT(go_between_sinfo.number_of_parameters() == 0);
9393 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
9394#endif
9395
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009396 // Materialize the content of the local scope into a JSObject.
9397 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009398
9399 // Allocate a new context for the debug evaluation and set the extension
9400 // object build.
9401 Handle<Context> context =
9402 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009403 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009404 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009405 Handle<Context> frame_context(Context::cast(frame->context()));
9406 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009407 context = CopyWithContextChain(frame_context, context);
9408
9409 // Wrap the evaluation statement in a new function compiled in the newly
9410 // created context. The function has one parameter which has to be called
9411 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00009412 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009413 // function(arguments,__source__) {return eval(__source__);}
9414 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00009415 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009416 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009417 Handle<String> function_source =
9418 Factory::NewStringFromAscii(Vector<const char>(source_str,
9419 source_str_length));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009420 Handle<SharedFunctionInfo> shared =
ager@chromium.org381abbb2009-02-25 13:23:22 +00009421 Compiler::CompileEval(function_source,
9422 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00009423 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00009424 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009425 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009426 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009427 Factory::NewFunctionFromSharedFunctionInfo(shared, context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009428
9429 // Invoke the result of the compilation to get the evaluation function.
9430 bool has_pending_exception;
9431 Handle<Object> receiver(frame->receiver());
9432 Handle<Object> evaluation_function =
9433 Execution::Call(compiled_function, receiver, 0, NULL,
9434 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009435 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009436
9437 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
9438 function_context);
9439
9440 // Invoke the evaluation function and return the result.
9441 const int argc = 2;
9442 Object** argv[argc] = { arguments.location(),
9443 Handle<Object>::cast(source).location() };
9444 Handle<Object> result =
9445 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
9446 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009447 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009448
9449 // Skip the global proxy as it has no properties and always delegates to the
9450 // real global object.
9451 if (result->IsJSGlobalProxy()) {
9452 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
9453 }
9454
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009455 return *result;
9456}
9457
9458
9459static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
9460 HandleScope scope;
9461
9462 // Check the execution state and decode arguments frame and source to be
9463 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009464 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009465 Object* check_result = Runtime_CheckExecutionState(args);
9466 if (check_result->IsFailure()) return check_result;
9467 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009468 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
9469
9470 // Handle the processing of break.
9471 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009472
9473 // Enter the top context from before the debugger was invoked.
9474 SaveContext save;
9475 SaveContext* top = &save;
9476 while (top != NULL && *top->context() == *Debug::debug_context()) {
9477 top = top->prev();
9478 }
9479 if (top != NULL) {
9480 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009481 }
9482
9483 // Get the global context now set to the top context from before the
9484 // debugger was invoked.
9485 Handle<Context> context = Top::global_context();
9486
9487 // Compile the source to be evaluated.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009488 Handle<SharedFunctionInfo> shared =
9489 Compiler::CompileEval(source,
9490 context,
9491 true,
9492 Compiler::DONT_VALIDATE_JSON);
9493 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009494 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009495 Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
9496 context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009497
9498 // Invoke the result of the compilation to get the evaluation function.
9499 bool has_pending_exception;
9500 Handle<Object> receiver = Top::global();
9501 Handle<Object> result =
9502 Execution::Call(compiled_function, receiver, 0, NULL,
9503 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009504 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009505 return *result;
9506}
9507
9508
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009509static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
9510 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009511 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009512
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009513 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009514 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009515
9516 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009517 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00009518 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
9519 // Get the script wrapper in a local handle before calling GetScriptWrapper,
9520 // because using
9521 // instances->set(i, *GetScriptWrapper(script))
9522 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
9523 // already have deferenced the instances handle.
9524 Handle<JSValue> wrapper = GetScriptWrapper(script);
9525 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009526 }
9527
9528 // Return result as a JS array.
9529 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
9530 Handle<JSArray>::cast(result)->SetContent(*instances);
9531 return *result;
9532}
9533
9534
9535// Helper function used by Runtime_DebugReferencedBy below.
9536static int DebugReferencedBy(JSObject* target,
9537 Object* instance_filter, int max_references,
9538 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009539 JSFunction* arguments_function) {
9540 NoHandleAllocation ha;
9541 AssertNoAllocation no_alloc;
9542
9543 // Iterate the heap.
9544 int count = 0;
9545 JSObject* last = NULL;
9546 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009547 HeapObject* heap_obj = NULL;
9548 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009549 (max_references == 0 || count < max_references)) {
9550 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009551 if (heap_obj->IsJSObject()) {
9552 // Skip context extension objects and argument arrays as these are
9553 // checked in the context of functions using them.
9554 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00009555 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009556 obj->map()->constructor() == arguments_function) {
9557 continue;
9558 }
9559
9560 // Check if the JS object has a reference to the object looked for.
9561 if (obj->ReferencesObject(target)) {
9562 // Check instance filter if supplied. This is normally used to avoid
9563 // references from mirror objects (see Runtime_IsInPrototypeChain).
9564 if (!instance_filter->IsUndefined()) {
9565 Object* V = obj;
9566 while (true) {
9567 Object* prototype = V->GetPrototype();
9568 if (prototype->IsNull()) {
9569 break;
9570 }
9571 if (instance_filter == prototype) {
9572 obj = NULL; // Don't add this object.
9573 break;
9574 }
9575 V = prototype;
9576 }
9577 }
9578
9579 if (obj != NULL) {
9580 // Valid reference found add to instance array if supplied an update
9581 // count.
9582 if (instances != NULL && count < instances_size) {
9583 instances->set(count, obj);
9584 }
9585 last = obj;
9586 count++;
9587 }
9588 }
9589 }
9590 }
9591
9592 // Check for circular reference only. This can happen when the object is only
9593 // referenced from mirrors and has a circular reference in which case the
9594 // object is not really alive and would have been garbage collected if not
9595 // referenced from the mirror.
9596 if (count == 1 && last == target) {
9597 count = 0;
9598 }
9599
9600 // Return the number of referencing objects found.
9601 return count;
9602}
9603
9604
9605// Scan the heap for objects with direct references to an object
9606// args[0]: the object to find references to
9607// args[1]: constructor function for instances to exclude (Mirror)
9608// args[2]: the the maximum number of objects to return
9609static Object* Runtime_DebugReferencedBy(Arguments args) {
9610 ASSERT(args.length() == 3);
9611
9612 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009613 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009614
9615 // Check parameters.
9616 CONVERT_CHECKED(JSObject, target, args[0]);
9617 Object* instance_filter = args[1];
9618 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
9619 instance_filter->IsJSObject());
9620 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
9621 RUNTIME_ASSERT(max_references >= 0);
9622
9623 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009624 JSObject* arguments_boilerplate =
9625 Top::context()->global_context()->arguments_boilerplate();
9626 JSFunction* arguments_function =
9627 JSFunction::cast(arguments_boilerplate->map()->constructor());
9628
9629 // Get the number of referencing objects.
9630 int count;
9631 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009632 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009633
9634 // Allocate an array to hold the result.
9635 Object* object = Heap::AllocateFixedArray(count);
9636 if (object->IsFailure()) return object;
9637 FixedArray* instances = FixedArray::cast(object);
9638
9639 // Fill the referencing objects.
9640 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009641 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009642
9643 // Return result as JS array.
9644 Object* result =
9645 Heap::AllocateJSObject(
9646 Top::context()->global_context()->array_function());
9647 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9648 return result;
9649}
9650
9651
9652// Helper function used by Runtime_DebugConstructedBy below.
9653static int DebugConstructedBy(JSFunction* constructor, int max_references,
9654 FixedArray* instances, int instances_size) {
9655 AssertNoAllocation no_alloc;
9656
9657 // Iterate the heap.
9658 int count = 0;
9659 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009660 HeapObject* heap_obj = NULL;
9661 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009662 (max_references == 0 || count < max_references)) {
9663 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009664 if (heap_obj->IsJSObject()) {
9665 JSObject* obj = JSObject::cast(heap_obj);
9666 if (obj->map()->constructor() == constructor) {
9667 // Valid reference found add to instance array if supplied an update
9668 // count.
9669 if (instances != NULL && count < instances_size) {
9670 instances->set(count, obj);
9671 }
9672 count++;
9673 }
9674 }
9675 }
9676
9677 // Return the number of referencing objects found.
9678 return count;
9679}
9680
9681
9682// Scan the heap for objects constructed by a specific function.
9683// args[0]: the constructor to find instances of
9684// args[1]: the the maximum number of objects to return
9685static Object* Runtime_DebugConstructedBy(Arguments args) {
9686 ASSERT(args.length() == 2);
9687
9688 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009689 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009690
9691 // Check parameters.
9692 CONVERT_CHECKED(JSFunction, constructor, args[0]);
9693 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
9694 RUNTIME_ASSERT(max_references >= 0);
9695
9696 // Get the number of referencing objects.
9697 int count;
9698 count = DebugConstructedBy(constructor, max_references, NULL, 0);
9699
9700 // Allocate an array to hold the result.
9701 Object* object = Heap::AllocateFixedArray(count);
9702 if (object->IsFailure()) return object;
9703 FixedArray* instances = FixedArray::cast(object);
9704
9705 // Fill the referencing objects.
9706 count = DebugConstructedBy(constructor, max_references, instances, count);
9707
9708 // Return result as JS array.
9709 Object* result =
9710 Heap::AllocateJSObject(
9711 Top::context()->global_context()->array_function());
9712 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9713 return result;
9714}
9715
9716
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009717// Find the effective prototype object as returned by __proto__.
9718// args[0]: the object to find the prototype for.
9719static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009720 ASSERT(args.length() == 1);
9721
9722 CONVERT_CHECKED(JSObject, obj, args[0]);
9723
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009724 // Use the __proto__ accessor.
9725 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009726}
9727
9728
9729static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009730 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009731 CPU::DebugBreak();
9732 return Heap::undefined_value();
9733}
9734
9735
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009736static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009737#ifdef DEBUG
9738 HandleScope scope;
9739 ASSERT(args.length() == 1);
9740 // Get the function and make sure it is compiled.
9741 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009742 Handle<SharedFunctionInfo> shared(func->shared());
9743 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009744 return Failure::Exception();
9745 }
9746 func->code()->PrintLn();
9747#endif // DEBUG
9748 return Heap::undefined_value();
9749}
ager@chromium.org9085a012009-05-11 19:22:57 +00009750
9751
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009752static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
9753#ifdef DEBUG
9754 HandleScope scope;
9755 ASSERT(args.length() == 1);
9756 // Get the function and make sure it is compiled.
9757 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009758 Handle<SharedFunctionInfo> shared(func->shared());
9759 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009760 return Failure::Exception();
9761 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009762 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009763#endif // DEBUG
9764 return Heap::undefined_value();
9765}
9766
9767
ager@chromium.org9085a012009-05-11 19:22:57 +00009768static Object* Runtime_FunctionGetInferredName(Arguments args) {
9769 NoHandleAllocation ha;
9770 ASSERT(args.length() == 1);
9771
9772 CONVERT_CHECKED(JSFunction, f, args[0]);
9773 return f->shared()->inferred_name();
9774}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009775
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009776
9777static int FindSharedFunctionInfosForScript(Script* script,
9778 FixedArray* buffer) {
9779 AssertNoAllocation no_allocations;
9780
9781 int counter = 0;
9782 int buffer_size = buffer->length();
9783 HeapIterator iterator;
9784 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
9785 ASSERT(obj != NULL);
9786 if (!obj->IsSharedFunctionInfo()) {
9787 continue;
9788 }
9789 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
9790 if (shared->script() != script) {
9791 continue;
9792 }
9793 if (counter < buffer_size) {
9794 buffer->set(counter, shared);
9795 }
9796 counter++;
9797 }
9798 return counter;
9799}
9800
9801// For a script finds all SharedFunctionInfo's in the heap that points
9802// to this script. Returns JSArray of SharedFunctionInfo wrapped
9803// in OpaqueReferences.
9804static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
9805 Arguments args) {
9806 ASSERT(args.length() == 1);
9807 HandleScope scope;
9808 CONVERT_CHECKED(JSValue, script_value, args[0]);
9809
9810 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
9811
9812 const int kBufferSize = 32;
9813
9814 Handle<FixedArray> array;
9815 array = Factory::NewFixedArray(kBufferSize);
9816 int number = FindSharedFunctionInfosForScript(*script, *array);
9817 if (number > kBufferSize) {
9818 array = Factory::NewFixedArray(number);
9819 FindSharedFunctionInfosForScript(*script, *array);
9820 }
9821
9822 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
9823 result->set_length(Smi::FromInt(number));
9824
9825 LiveEdit::WrapSharedFunctionInfos(result);
9826
9827 return *result;
9828}
9829
9830// For a script calculates compilation information about all its functions.
9831// The script source is explicitly specified by the second argument.
9832// The source of the actual script is not used, however it is important that
9833// all generated code keeps references to this particular instance of script.
9834// Returns a JSArray of compilation infos. The array is ordered so that
9835// each function with all its descendant is always stored in a continues range
9836// with the function itself going first. The root function is a script function.
9837static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
9838 ASSERT(args.length() == 2);
9839 HandleScope scope;
9840 CONVERT_CHECKED(JSValue, script, args[0]);
9841 CONVERT_ARG_CHECKED(String, source, 1);
9842 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
9843
9844 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
9845
9846 if (Top::has_pending_exception()) {
9847 return Failure::Exception();
9848 }
9849
9850 return result;
9851}
9852
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009853// Changes the source of the script to a new_source.
9854// If old_script_name is provided (i.e. is a String), also creates a copy of
9855// the script with its original source and sends notification to debugger.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009856static Object* Runtime_LiveEditReplaceScript(Arguments args) {
9857 ASSERT(args.length() == 3);
9858 HandleScope scope;
9859 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
9860 CONVERT_ARG_CHECKED(String, new_source, 1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009861 Handle<Object> old_script_name(args[2]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009862
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009863 CONVERT_CHECKED(Script, original_script_pointer,
9864 original_script_value->value());
9865 Handle<Script> original_script(original_script_pointer);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009866
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009867 Object* old_script = LiveEdit::ChangeScriptSource(original_script,
9868 new_source,
9869 old_script_name);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009870
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009871 if (old_script->IsScript()) {
9872 Handle<Script> script_handle(Script::cast(old_script));
9873 return *(GetScriptWrapper(script_handle));
9874 } else {
9875 return Heap::null_value();
9876 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009877}
9878
9879// Replaces code of SharedFunctionInfo with a new one.
9880static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
9881 ASSERT(args.length() == 2);
9882 HandleScope scope;
9883 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
9884 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
9885
ager@chromium.orgac091b72010-05-05 07:34:42 +00009886 return LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009887}
9888
9889// Connects SharedFunctionInfo to another script.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009890static Object* Runtime_LiveEditFunctionSetScript(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009891 ASSERT(args.length() == 2);
9892 HandleScope scope;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009893 Handle<Object> function_object(args[0]);
9894 Handle<Object> script_object(args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009895
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009896 if (function_object->IsJSValue()) {
9897 Handle<JSValue> function_wrapper = Handle<JSValue>::cast(function_object);
9898 if (script_object->IsJSValue()) {
9899 CONVERT_CHECKED(Script, script, JSValue::cast(*script_object)->value());
9900 script_object = Handle<Object>(script);
9901 }
9902
9903 LiveEdit::SetFunctionScript(function_wrapper, script_object);
9904 } else {
9905 // Just ignore this. We may not have a SharedFunctionInfo for some functions
9906 // and we check it in this function.
9907 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009908
9909 return Heap::undefined_value();
9910}
9911
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009912
9913// In a code of a parent function replaces original function as embedded object
9914// with a substitution one.
9915static Object* Runtime_LiveEditReplaceRefToNestedFunction(Arguments args) {
9916 ASSERT(args.length() == 3);
9917 HandleScope scope;
9918
9919 CONVERT_ARG_CHECKED(JSValue, parent_wrapper, 0);
9920 CONVERT_ARG_CHECKED(JSValue, orig_wrapper, 1);
9921 CONVERT_ARG_CHECKED(JSValue, subst_wrapper, 2);
9922
9923 LiveEdit::ReplaceRefToNestedFunction(parent_wrapper, orig_wrapper,
9924 subst_wrapper);
9925
9926 return Heap::undefined_value();
9927}
9928
9929
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009930// Updates positions of a shared function info (first parameter) according
9931// to script source change. Text change is described in second parameter as
9932// array of groups of 3 numbers:
9933// (change_begin, change_end, change_end_new_position).
9934// Each group describes a change in text; groups are sorted by change_begin.
9935static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
9936 ASSERT(args.length() == 2);
9937 HandleScope scope;
9938 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
9939 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
9940
ager@chromium.orgac091b72010-05-05 07:34:42 +00009941 return LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009942}
9943
9944
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009945// For array of SharedFunctionInfo's (each wrapped in JSValue)
9946// checks that none of them have activations on stacks (of any thread).
9947// Returns array of the same length with corresponding results of
9948// LiveEdit::FunctionPatchabilityStatus type.
ager@chromium.org357bf652010-04-12 11:30:10 +00009949static Object* Runtime_LiveEditCheckAndDropActivations(Arguments args) {
9950 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009951 HandleScope scope;
9952 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +00009953 CONVERT_BOOLEAN_CHECKED(do_drop, args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009954
ager@chromium.org357bf652010-04-12 11:30:10 +00009955 return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009956}
9957
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009958// Compares 2 strings line-by-line and returns diff in form of JSArray of
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00009959// triplets (pos1, pos1_end, pos2_end) describing list of diff chunks.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009960static Object* Runtime_LiveEditCompareStringsLinewise(Arguments args) {
9961 ASSERT(args.length() == 2);
9962 HandleScope scope;
9963 CONVERT_ARG_CHECKED(String, s1, 0);
9964 CONVERT_ARG_CHECKED(String, s2, 1);
9965
9966 return *LiveEdit::CompareStringsLinewise(s1, s2);
9967}
9968
9969
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009970
fschneider@chromium.org086aac62010-03-17 13:18:24 +00009971// A testing entry. Returns statement position which is the closest to
9972// source_position.
9973static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
9974 ASSERT(args.length() == 2);
9975 HandleScope scope;
9976 CONVERT_ARG_CHECKED(JSFunction, function, 0);
9977 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9978
9979 Handle<Code> code(function->code());
9980
9981 RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
9982 int closest_pc = 0;
9983 int distance = kMaxInt;
9984 while (!it.done()) {
9985 int statement_position = static_cast<int>(it.rinfo()->data());
9986 // Check if this break point is closer that what was previously found.
9987 if (source_position <= statement_position &&
9988 statement_position - source_position < distance) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00009989 closest_pc =
9990 static_cast<int>(it.rinfo()->pc() - code->instruction_start());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00009991 distance = statement_position - source_position;
9992 // Check whether we can't get any closer.
9993 if (distance == 0) break;
9994 }
9995 it.next();
9996 }
9997
9998 return Smi::FromInt(closest_pc);
9999}
10000
10001
ager@chromium.org357bf652010-04-12 11:30:10 +000010002// Calls specified function with or without entering the debugger.
10003// This is used in unit tests to run code as if debugger is entered or simply
10004// to have a stack with C++ frame in the middle.
10005static Object* Runtime_ExecuteInDebugContext(Arguments args) {
10006 ASSERT(args.length() == 2);
10007 HandleScope scope;
10008 CONVERT_ARG_CHECKED(JSFunction, function, 0);
10009 CONVERT_BOOLEAN_CHECKED(without_debugger, args[1]);
10010
10011 Handle<Object> result;
10012 bool pending_exception;
10013 {
10014 if (without_debugger) {
10015 result = Execution::Call(function, Top::global(), 0, NULL,
10016 &pending_exception);
10017 } else {
10018 EnterDebugger enter_debugger;
10019 result = Execution::Call(function, Top::global(), 0, NULL,
10020 &pending_exception);
10021 }
10022 }
10023 if (!pending_exception) {
10024 return *result;
10025 } else {
10026 return Failure::Exception();
10027 }
10028}
10029
10030
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010031#endif // ENABLE_DEBUGGER_SUPPORT
10032
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010033#ifdef ENABLE_LOGGING_AND_PROFILING
10034
10035static Object* Runtime_ProfilerResume(Arguments args) {
10036 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010037 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010038
10039 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010040 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10041 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010042 return Heap::undefined_value();
10043}
10044
10045
10046static Object* Runtime_ProfilerPause(Arguments args) {
10047 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010048 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010049
10050 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010051 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10052 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010053 return Heap::undefined_value();
10054}
10055
10056#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010057
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010058// Finds the script object from the script data. NOTE: This operation uses
10059// heap traversal to find the function generated for the source position
10060// for the requested break point. For lazily compiled functions several heap
10061// traversals might be required rendering this operation as a rather slow
10062// operation. However for setting break points which is normally done through
10063// some kind of user interaction the performance is not crucial.
10064static Handle<Object> Runtime_GetScriptFromScriptName(
10065 Handle<String> script_name) {
10066 // Scan the heap for Script objects to find the script with the requested
10067 // script data.
10068 Handle<Script> script;
10069 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010070 HeapObject* obj = NULL;
10071 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010072 // If a script is found check if it has the script data requested.
10073 if (obj->IsScript()) {
10074 if (Script::cast(obj)->name()->IsString()) {
10075 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
10076 script = Handle<Script>(Script::cast(obj));
10077 }
10078 }
10079 }
10080 }
10081
10082 // If no script with the requested script data is found return undefined.
10083 if (script.is_null()) return Factory::undefined_value();
10084
10085 // Return the script found.
10086 return GetScriptWrapper(script);
10087}
10088
10089
10090// Get the script object from script data. NOTE: Regarding performance
10091// see the NOTE for GetScriptFromScriptData.
10092// args[0]: script data for the script to find the source for
10093static Object* Runtime_GetScript(Arguments args) {
10094 HandleScope scope;
10095
10096 ASSERT(args.length() == 1);
10097
10098 CONVERT_CHECKED(String, script_name, args[0]);
10099
10100 // Find the requested script.
10101 Handle<Object> result =
10102 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
10103 return *result;
10104}
10105
10106
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010107// Determines whether the given stack frame should be displayed in
10108// a stack trace. The caller is the error constructor that asked
10109// for the stack trace to be collected. The first time a construct
10110// call to this function is encountered it is skipped. The seen_caller
10111// in/out parameter is used to remember if the caller has been seen
10112// yet.
10113static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
10114 bool* seen_caller) {
10115 // Only display JS frames.
10116 if (!raw_frame->is_java_script())
10117 return false;
10118 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
10119 Object* raw_fun = frame->function();
10120 // Not sure when this can happen but skip it just in case.
10121 if (!raw_fun->IsJSFunction())
10122 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010123 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010124 *seen_caller = true;
10125 return false;
10126 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010127 // Skip all frames until we've seen the caller. Also, skip the most
10128 // obvious builtin calls. Some builtin calls (such as Number.ADD
10129 // which is invoked using 'call') are very difficult to recognize
10130 // so we're leaving them in for now.
10131 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010132}
10133
10134
10135// Collect the raw data for a stack trace. Returns an array of three
10136// element segments each containing a receiver, function and native
10137// code offset.
10138static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010139 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010140 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010141 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
10142
10143 HandleScope scope;
10144
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000010145 limit = Max(limit, 0); // Ensure that limit is not negative.
10146 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010147 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010148
10149 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010150 // If the caller parameter is a function we skip frames until we're
10151 // under it before starting to collect.
10152 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010153 int cursor = 0;
10154 int frames_seen = 0;
10155 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010156 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010157 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010158 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010159 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010160 Object* recv = frame->receiver();
10161 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010162 Address pc = frame->pc();
10163 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010164 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010165 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010166 if (cursor + 2 < elements->length()) {
10167 elements->set(cursor++, recv);
10168 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010169 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010170 } else {
10171 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010172 Handle<Object> recv_handle(recv);
10173 Handle<Object> fun_handle(fun);
10174 SetElement(result, cursor++, recv_handle);
10175 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010176 SetElement(result, cursor++, Handle<Smi>(offset));
10177 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010178 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010179 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010180 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010181
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010182 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010183 return *result;
10184}
10185
10186
ager@chromium.org3811b432009-10-28 14:53:37 +000010187// Returns V8 version as a string.
10188static Object* Runtime_GetV8Version(Arguments args) {
10189 ASSERT_EQ(args.length(), 0);
10190
10191 NoHandleAllocation ha;
10192
10193 const char* version_string = v8::V8::GetVersion();
10194
10195 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
10196}
10197
10198
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010199static Object* Runtime_Abort(Arguments args) {
10200 ASSERT(args.length() == 2);
10201 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
10202 Smi::cast(args[1])->value());
10203 Top::PrintStack();
10204 OS::Abort();
10205 UNREACHABLE();
10206 return NULL;
10207}
10208
10209
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010210static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
10211 ASSERT(args.length() == 0);
10212 HandleScope::DeleteExtensions();
10213 return Heap::undefined_value();
10214}
10215
10216
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010217static Object* CacheMiss(FixedArray* cache_obj, int index, Object* key_obj) {
10218 ASSERT(index % 2 == 0); // index of the key
10219 ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
10220 ASSERT(index < cache_obj->length());
10221
10222 HandleScope scope;
10223
10224 Handle<FixedArray> cache(cache_obj);
10225 Handle<Object> key(key_obj);
10226 Handle<JSFunction> factory(JSFunction::cast(
10227 cache->get(JSFunctionResultCache::kFactoryIndex)));
10228 // TODO(antonm): consider passing a receiver when constructing a cache.
10229 Handle<Object> receiver(Top::global_context()->global());
10230
10231 Handle<Object> value;
10232 {
10233 // This handle is nor shared, nor used later, so it's safe.
10234 Object** argv[] = { key.location() };
10235 bool pending_exception = false;
10236 value = Execution::Call(factory,
10237 receiver,
10238 1,
10239 argv,
10240 &pending_exception);
10241 if (pending_exception) return Failure::Exception();
10242 }
10243
10244 cache->set(index, *key);
10245 cache->set(index + 1, *value);
10246 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(index));
10247
10248 return *value;
10249}
10250
10251
10252static Object* Runtime_GetFromCache(Arguments args) {
10253 // This is only called from codegen, so checks might be more lax.
10254 CONVERT_CHECKED(FixedArray, cache, args[0]);
10255 Object* key = args[1];
10256
10257 const int finger_index =
10258 Smi::cast(cache->get(JSFunctionResultCache::kFingerIndex))->value();
10259
10260 Object* o = cache->get(finger_index);
10261 if (o == key) {
10262 // The fastest case: hit the same place again.
10263 return cache->get(finger_index + 1);
10264 }
10265
10266 for (int i = finger_index - 2;
10267 i >= JSFunctionResultCache::kEntriesIndex;
10268 i -= 2) {
10269 o = cache->get(i);
10270 if (o == key) {
10271 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10272 return cache->get(i + 1);
10273 }
10274 }
10275
10276 const int size =
10277 Smi::cast(cache->get(JSFunctionResultCache::kCacheSizeIndex))->value();
10278 ASSERT(size <= cache->length());
10279
10280 for (int i = size - 2; i > finger_index; i -= 2) {
10281 o = cache->get(i);
10282 if (o == key) {
10283 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10284 return cache->get(i + 1);
10285 }
10286 }
10287
10288 // Cache miss. If we have spare room, put new data into it, otherwise
10289 // evict post finger entry which must be least recently used.
10290 if (size < cache->length()) {
10291 cache->set(JSFunctionResultCache::kCacheSizeIndex, Smi::FromInt(size + 2));
10292 return CacheMiss(cache, size, key);
10293 } else {
antonm@chromium.org397e23c2010-04-21 12:00:05 +000010294 int target_index = finger_index + JSFunctionResultCache::kEntrySize;
10295 if (target_index == cache->length()) {
10296 target_index = JSFunctionResultCache::kEntriesIndex;
10297 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010298 return CacheMiss(cache, target_index, key);
10299 }
10300}
10301
kasper.lund44510672008-07-25 07:37:58 +000010302#ifdef DEBUG
10303// ListNatives is ONLY used by the fuzz-natives.js in debug mode
10304// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010305static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +000010306 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010307 HandleScope scope;
10308 Handle<JSArray> result = Factory::NewJSArray(0);
10309 int index = 0;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010310 bool inline_runtime_functions = false;
ager@chromium.orga1645e22009-09-09 19:27:10 +000010311#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010312 { \
10313 HandleScope inner; \
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010314 Handle<String> name; \
10315 /* Inline runtime functions have an underscore in front of the name. */ \
10316 if (inline_runtime_functions) { \
10317 name = Factory::NewStringFromAscii( \
10318 Vector<const char>("_" #Name, StrLength("_" #Name))); \
10319 } else { \
10320 name = Factory::NewStringFromAscii( \
10321 Vector<const char>(#Name, StrLength(#Name))); \
10322 } \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010323 Handle<JSArray> pair = Factory::NewJSArray(0); \
10324 SetElement(pair, 0, name); \
10325 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
10326 SetElement(result, index++, pair); \
10327 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010328 inline_runtime_functions = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010329 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010330 inline_runtime_functions = true;
10331 INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010332#undef ADD_ENTRY
10333 return *result;
10334}
kasper.lund44510672008-07-25 07:37:58 +000010335#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010336
10337
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010338static Object* Runtime_Log(Arguments args) {
10339 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +000010340 CONVERT_CHECKED(String, format, args[0]);
10341 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010342 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010343 Logger::LogRuntime(chars, elms);
10344 return Heap::undefined_value();
10345}
10346
10347
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010348static Object* Runtime_IS_VAR(Arguments args) {
10349 UNREACHABLE(); // implemented as macro in the parser
10350 return NULL;
10351}
10352
10353
10354// ----------------------------------------------------------------------------
10355// Implementation of Runtime
10356
ager@chromium.orga1645e22009-09-09 19:27:10 +000010357#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010358 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +000010359 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010360
10361static Runtime::Function Runtime_functions[] = {
10362 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010363 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010364};
10365
10366#undef F
10367
10368
10369Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
10370 ASSERT(0 <= fid && fid < kNofFunctions);
10371 return &Runtime_functions[fid];
10372}
10373
10374
10375Runtime::Function* Runtime::FunctionForName(const char* name) {
10376 for (Function* f = Runtime_functions; f->name != NULL; f++) {
10377 if (strcmp(f->name, name) == 0) {
10378 return f;
10379 }
10380 }
10381 return NULL;
10382}
10383
10384
10385void Runtime::PerformGC(Object* result) {
10386 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010387 if (failure->IsRetryAfterGC()) {
10388 // Try to do a garbage collection; ignore it if it fails. The C
10389 // entry stub will throw an out-of-memory exception in that case.
10390 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
10391 } else {
10392 // Handle last resort GC and make sure to allow future allocations
10393 // to grow the heap without causing GCs (if possible).
10394 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +000010395 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010396 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010397}
10398
10399
10400} } // namespace v8::internal