blob: d53b13d07fca9363e9913429ded8cb084576a4c4 [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));
629 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!details.IsDontDelete()));
630 elms->set(ENUMERABLE_INDEX, Heap::ToBoolean(!details.IsDontEnum()));
631 elms->set(CONFIGURABLE_INDEX, Heap::ToBoolean(!details.IsReadOnly()));
632 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
1641static Object* CharCodeAt(String* subject, Object* index) {
1642 uint32_t i = 0;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001643 if (!index->ToArrayIndex(&i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001644 // Flatten the string. If someone wants to get a char at an index
1645 // in a cons string, it is likely that more indices will be
1646 // accessed.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001647 Object* flat = subject->TryFlatten();
1648 if (flat->IsFailure()) return flat;
1649 subject = String::cast(flat);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001650 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001651 return Heap::nan_value();
1652 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001653 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001654}
1655
1656
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001657static Object* CharFromCode(Object* char_code) {
1658 uint32_t code;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001659 if (char_code->ToArrayIndex(&code)) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001660 if (code <= 0xffff) {
1661 return Heap::LookupSingleCharacterStringFromCode(code);
1662 }
1663 }
1664 return Heap::empty_string();
1665}
1666
1667
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001668static Object* Runtime_StringCharCodeAt(Arguments args) {
1669 NoHandleAllocation ha;
1670 ASSERT(args.length() == 2);
1671
1672 CONVERT_CHECKED(String, subject, args[0]);
1673 Object* index = args[1];
1674 return CharCodeAt(subject, index);
1675}
1676
1677
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001678static Object* Runtime_StringCharAt(Arguments args) {
1679 NoHandleAllocation ha;
1680 ASSERT(args.length() == 2);
1681
1682 CONVERT_CHECKED(String, subject, args[0]);
1683 Object* index = args[1];
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001684 Object* code = CharCodeAt(subject, index);
1685 if (code == Heap::nan_value()) {
1686 return Heap::undefined_value();
1687 }
1688 return CharFromCode(code);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001689}
1690
1691
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001692static Object* Runtime_CharFromCode(Arguments args) {
1693 NoHandleAllocation ha;
1694 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001695 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001696}
1697
lrn@chromium.org25156de2010-04-06 13:10:27 +00001698
1699class FixedArrayBuilder {
1700 public:
1701 explicit FixedArrayBuilder(int initial_capacity)
1702 : array_(Factory::NewFixedArrayWithHoles(initial_capacity)),
1703 length_(0) {
1704 // Require a non-zero initial size. Ensures that doubling the size to
1705 // extend the array will work.
1706 ASSERT(initial_capacity > 0);
1707 }
1708
1709 explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
1710 : array_(backing_store),
1711 length_(0) {
1712 // Require a non-zero initial size. Ensures that doubling the size to
1713 // extend the array will work.
1714 ASSERT(backing_store->length() > 0);
1715 }
1716
1717 bool HasCapacity(int elements) {
1718 int length = array_->length();
1719 int required_length = length_ + elements;
1720 return (length >= required_length);
1721 }
1722
1723 void EnsureCapacity(int elements) {
1724 int length = array_->length();
1725 int required_length = length_ + elements;
1726 if (length < required_length) {
1727 int new_length = length;
1728 do {
1729 new_length *= 2;
1730 } while (new_length < required_length);
1731 Handle<FixedArray> extended_array =
1732 Factory::NewFixedArrayWithHoles(new_length);
1733 array_->CopyTo(0, *extended_array, 0, length_);
1734 array_ = extended_array;
1735 }
1736 }
1737
1738 void Add(Object* value) {
1739 ASSERT(length_ < capacity());
1740 array_->set(length_, value);
1741 length_++;
1742 }
1743
1744 void Add(Smi* value) {
1745 ASSERT(length_ < capacity());
1746 array_->set(length_, value);
1747 length_++;
1748 }
1749
1750 Handle<FixedArray> array() {
1751 return array_;
1752 }
1753
1754 int length() {
1755 return length_;
1756 }
1757
1758 int capacity() {
1759 return array_->length();
1760 }
1761
1762 Handle<JSArray> ToJSArray() {
1763 Handle<JSArray> result_array = Factory::NewJSArrayWithElements(array_);
1764 result_array->set_length(Smi::FromInt(length_));
1765 return result_array;
1766 }
1767
1768 Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
1769 target_array->set_elements(*array_);
1770 target_array->set_length(Smi::FromInt(length_));
1771 return target_array;
1772 }
1773
1774 private:
1775 Handle<FixedArray> array_;
1776 int length_;
1777};
1778
1779
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001780// Forward declarations.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001781const int kStringBuilderConcatHelperLengthBits = 11;
1782const int kStringBuilderConcatHelperPositionBits = 19;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001783
1784template <typename schar>
1785static inline void StringBuilderConcatHelper(String*,
1786 schar*,
1787 FixedArray*,
1788 int);
1789
lrn@chromium.org25156de2010-04-06 13:10:27 +00001790typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
1791 StringBuilderSubstringLength;
1792typedef BitField<int,
1793 kStringBuilderConcatHelperLengthBits,
1794 kStringBuilderConcatHelperPositionBits>
1795 StringBuilderSubstringPosition;
1796
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001797
1798class ReplacementStringBuilder {
1799 public:
1800 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
lrn@chromium.org25156de2010-04-06 13:10:27 +00001801 : array_builder_(estimated_part_count),
1802 subject_(subject),
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001803 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001804 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001805 // Require a non-zero initial size. Ensures that doubling the size to
1806 // extend the array will work.
1807 ASSERT(estimated_part_count > 0);
1808 }
1809
lrn@chromium.org25156de2010-04-06 13:10:27 +00001810 static inline void AddSubjectSlice(FixedArrayBuilder* builder,
1811 int from,
1812 int to) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001813 ASSERT(from >= 0);
1814 int length = to - from;
1815 ASSERT(length > 0);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001816 if (StringBuilderSubstringLength::is_valid(length) &&
1817 StringBuilderSubstringPosition::is_valid(from)) {
1818 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1819 StringBuilderSubstringPosition::encode(from);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001820 builder->Add(Smi::FromInt(encoded_slice));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001821 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001822 // Otherwise encode as two smis.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001823 builder->Add(Smi::FromInt(-length));
1824 builder->Add(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001825 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00001826 }
1827
1828
1829 void EnsureCapacity(int elements) {
1830 array_builder_.EnsureCapacity(elements);
1831 }
1832
1833
1834 void AddSubjectSlice(int from, int to) {
1835 AddSubjectSlice(&array_builder_, from, to);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001836 IncrementCharacterCount(to - from);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001837 }
1838
1839
1840 void AddString(Handle<String> string) {
1841 int length = string->length();
1842 ASSERT(length > 0);
1843 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001844 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001845 is_ascii_ = false;
1846 }
1847 IncrementCharacterCount(length);
1848 }
1849
1850
1851 Handle<String> ToString() {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001852 if (array_builder_.length() == 0) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001853 return Factory::empty_string();
1854 }
1855
1856 Handle<String> joined_string;
1857 if (is_ascii_) {
1858 joined_string = NewRawAsciiString(character_count_);
1859 AssertNoAllocation no_alloc;
1860 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1861 char* char_buffer = seq->GetChars();
1862 StringBuilderConcatHelper(*subject_,
1863 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001864 *array_builder_.array(),
1865 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001866 } else {
1867 // Non-ASCII.
1868 joined_string = NewRawTwoByteString(character_count_);
1869 AssertNoAllocation no_alloc;
1870 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1871 uc16* char_buffer = seq->GetChars();
1872 StringBuilderConcatHelper(*subject_,
1873 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001874 *array_builder_.array(),
1875 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001876 }
1877 return joined_string;
1878 }
1879
1880
1881 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001882 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001883 V8::FatalProcessOutOfMemory("String.replace result too large.");
1884 }
1885 character_count_ += by;
1886 }
1887
lrn@chromium.org25156de2010-04-06 13:10:27 +00001888 Handle<JSArray> GetParts() {
1889 Handle<JSArray> result =
1890 Factory::NewJSArrayWithElements(array_builder_.array());
1891 result->set_length(Smi::FromInt(array_builder_.length()));
1892 return result;
1893 }
kmillikin@chromium.orgd9825192010-03-30 08:36:16 +00001894
lrn@chromium.org25156de2010-04-06 13:10:27 +00001895 private:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001896 Handle<String> NewRawAsciiString(int size) {
1897 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1898 }
1899
1900
1901 Handle<String> NewRawTwoByteString(int size) {
1902 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1903 }
1904
1905
1906 void AddElement(Object* element) {
1907 ASSERT(element->IsSmi() || element->IsString());
lrn@chromium.org25156de2010-04-06 13:10:27 +00001908 ASSERT(array_builder_.capacity() > array_builder_.length());
1909 array_builder_.Add(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001910 }
1911
lrn@chromium.org25156de2010-04-06 13:10:27 +00001912 FixedArrayBuilder array_builder_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001913 Handle<String> subject_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001914 int character_count_;
1915 bool is_ascii_;
1916};
1917
1918
1919class CompiledReplacement {
1920 public:
1921 CompiledReplacement()
1922 : parts_(1), replacement_substrings_(0) {}
1923
1924 void Compile(Handle<String> replacement,
1925 int capture_count,
1926 int subject_length);
1927
1928 void Apply(ReplacementStringBuilder* builder,
1929 int match_from,
1930 int match_to,
1931 Handle<JSArray> last_match_info);
1932
1933 // Number of distinct parts of the replacement pattern.
1934 int parts() {
1935 return parts_.length();
1936 }
1937 private:
1938 enum PartType {
1939 SUBJECT_PREFIX = 1,
1940 SUBJECT_SUFFIX,
1941 SUBJECT_CAPTURE,
1942 REPLACEMENT_SUBSTRING,
1943 REPLACEMENT_STRING,
1944
1945 NUMBER_OF_PART_TYPES
1946 };
1947
1948 struct ReplacementPart {
1949 static inline ReplacementPart SubjectMatch() {
1950 return ReplacementPart(SUBJECT_CAPTURE, 0);
1951 }
1952 static inline ReplacementPart SubjectCapture(int capture_index) {
1953 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1954 }
1955 static inline ReplacementPart SubjectPrefix() {
1956 return ReplacementPart(SUBJECT_PREFIX, 0);
1957 }
1958 static inline ReplacementPart SubjectSuffix(int subject_length) {
1959 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1960 }
1961 static inline ReplacementPart ReplacementString() {
1962 return ReplacementPart(REPLACEMENT_STRING, 0);
1963 }
1964 static inline ReplacementPart ReplacementSubString(int from, int to) {
1965 ASSERT(from >= 0);
1966 ASSERT(to > from);
1967 return ReplacementPart(-from, to);
1968 }
1969
1970 // If tag <= 0 then it is the negation of a start index of a substring of
1971 // the replacement pattern, otherwise it's a value from PartType.
1972 ReplacementPart(int tag, int data)
1973 : tag(tag), data(data) {
1974 // Must be non-positive or a PartType value.
1975 ASSERT(tag < NUMBER_OF_PART_TYPES);
1976 }
1977 // Either a value of PartType or a non-positive number that is
1978 // the negation of an index into the replacement string.
1979 int tag;
1980 // The data value's interpretation depends on the value of tag:
1981 // tag == SUBJECT_PREFIX ||
1982 // tag == SUBJECT_SUFFIX: data is unused.
1983 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1984 // tag == REPLACEMENT_SUBSTRING ||
1985 // tag == REPLACEMENT_STRING: data is index into array of substrings
1986 // of the replacement string.
1987 // tag <= 0: Temporary representation of the substring of the replacement
1988 // string ranging over -tag .. data.
1989 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1990 // substring objects.
1991 int data;
1992 };
1993
1994 template<typename Char>
1995 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1996 Vector<Char> characters,
1997 int capture_count,
1998 int subject_length) {
1999 int length = characters.length();
2000 int last = 0;
2001 for (int i = 0; i < length; i++) {
2002 Char c = characters[i];
2003 if (c == '$') {
2004 int next_index = i + 1;
2005 if (next_index == length) { // No next character!
2006 break;
2007 }
2008 Char c2 = characters[next_index];
2009 switch (c2) {
2010 case '$':
2011 if (i > last) {
2012 // There is a substring before. Include the first "$".
2013 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
2014 last = next_index + 1; // Continue after the second "$".
2015 } else {
2016 // Let the next substring start with the second "$".
2017 last = next_index;
2018 }
2019 i = next_index;
2020 break;
2021 case '`':
2022 if (i > last) {
2023 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2024 }
2025 parts->Add(ReplacementPart::SubjectPrefix());
2026 i = next_index;
2027 last = i + 1;
2028 break;
2029 case '\'':
2030 if (i > last) {
2031 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2032 }
2033 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
2034 i = next_index;
2035 last = i + 1;
2036 break;
2037 case '&':
2038 if (i > last) {
2039 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2040 }
2041 parts->Add(ReplacementPart::SubjectMatch());
2042 i = next_index;
2043 last = i + 1;
2044 break;
2045 case '0':
2046 case '1':
2047 case '2':
2048 case '3':
2049 case '4':
2050 case '5':
2051 case '6':
2052 case '7':
2053 case '8':
2054 case '9': {
2055 int capture_ref = c2 - '0';
2056 if (capture_ref > capture_count) {
2057 i = next_index;
2058 continue;
2059 }
2060 int second_digit_index = next_index + 1;
2061 if (second_digit_index < length) {
2062 // Peek ahead to see if we have two digits.
2063 Char c3 = characters[second_digit_index];
2064 if ('0' <= c3 && c3 <= '9') { // Double digits.
2065 int double_digit_ref = capture_ref * 10 + c3 - '0';
2066 if (double_digit_ref <= capture_count) {
2067 next_index = second_digit_index;
2068 capture_ref = double_digit_ref;
2069 }
2070 }
2071 }
2072 if (capture_ref > 0) {
2073 if (i > last) {
2074 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2075 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002076 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002077 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
2078 last = next_index + 1;
2079 }
2080 i = next_index;
2081 break;
2082 }
2083 default:
2084 i = next_index;
2085 break;
2086 }
2087 }
2088 }
2089 if (length > last) {
2090 if (last == 0) {
2091 parts->Add(ReplacementPart::ReplacementString());
2092 } else {
2093 parts->Add(ReplacementPart::ReplacementSubString(last, length));
2094 }
2095 }
2096 }
2097
2098 ZoneList<ReplacementPart> parts_;
2099 ZoneList<Handle<String> > replacement_substrings_;
2100};
2101
2102
2103void CompiledReplacement::Compile(Handle<String> replacement,
2104 int capture_count,
2105 int subject_length) {
2106 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00002107 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002108 AssertNoAllocation no_alloc;
2109 ParseReplacementPattern(&parts_,
2110 replacement->ToAsciiVector(),
2111 capture_count,
2112 subject_length);
2113 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00002114 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002115 AssertNoAllocation no_alloc;
2116
2117 ParseReplacementPattern(&parts_,
2118 replacement->ToUC16Vector(),
2119 capture_count,
2120 subject_length);
2121 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002122 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002123 int substring_index = 0;
2124 for (int i = 0, n = parts_.length(); i < n; i++) {
2125 int tag = parts_[i].tag;
2126 if (tag <= 0) { // A replacement string slice.
2127 int from = -tag;
2128 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002129 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002130 parts_[i].tag = REPLACEMENT_SUBSTRING;
2131 parts_[i].data = substring_index;
2132 substring_index++;
2133 } else if (tag == REPLACEMENT_STRING) {
2134 replacement_substrings_.Add(replacement);
2135 parts_[i].data = substring_index;
2136 substring_index++;
2137 }
2138 }
2139}
2140
2141
2142void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
2143 int match_from,
2144 int match_to,
2145 Handle<JSArray> last_match_info) {
2146 for (int i = 0, n = parts_.length(); i < n; i++) {
2147 ReplacementPart part = parts_[i];
2148 switch (part.tag) {
2149 case SUBJECT_PREFIX:
2150 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
2151 break;
2152 case SUBJECT_SUFFIX: {
2153 int subject_length = part.data;
2154 if (match_to < subject_length) {
2155 builder->AddSubjectSlice(match_to, subject_length);
2156 }
2157 break;
2158 }
2159 case SUBJECT_CAPTURE: {
2160 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002161 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002162 int from = RegExpImpl::GetCapture(match_info, capture * 2);
2163 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
2164 if (from >= 0 && to > from) {
2165 builder->AddSubjectSlice(from, to);
2166 }
2167 break;
2168 }
2169 case REPLACEMENT_SUBSTRING:
2170 case REPLACEMENT_STRING:
2171 builder->AddString(replacement_substrings_[part.data]);
2172 break;
2173 default:
2174 UNREACHABLE();
2175 }
2176 }
2177}
2178
2179
2180
2181static Object* StringReplaceRegExpWithString(String* subject,
2182 JSRegExp* regexp,
2183 String* replacement,
2184 JSArray* last_match_info) {
2185 ASSERT(subject->IsFlat());
2186 ASSERT(replacement->IsFlat());
2187
2188 HandleScope handles;
2189
2190 int length = subject->length();
2191 Handle<String> subject_handle(subject);
2192 Handle<JSRegExp> regexp_handle(regexp);
2193 Handle<String> replacement_handle(replacement);
2194 Handle<JSArray> last_match_info_handle(last_match_info);
2195 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2196 subject_handle,
2197 0,
2198 last_match_info_handle);
2199 if (match.is_null()) {
2200 return Failure::Exception();
2201 }
2202 if (match->IsNull()) {
2203 return *subject_handle;
2204 }
2205
2206 int capture_count = regexp_handle->CaptureCount();
2207
2208 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002209 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002210 CompiledReplacement compiled_replacement;
2211 compiled_replacement.Compile(replacement_handle,
2212 capture_count,
2213 length);
2214
2215 bool is_global = regexp_handle->GetFlags().is_global();
2216
2217 // Guessing the number of parts that the final result string is built
2218 // from. Global regexps can match any number of times, so we guess
2219 // conservatively.
2220 int expected_parts =
2221 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
2222 ReplacementStringBuilder builder(subject_handle, expected_parts);
2223
2224 // Index of end of last match.
2225 int prev = 0;
2226
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002227 // Number of parts added by compiled replacement plus preceeding
2228 // string and possibly suffix after last match. It is possible for
2229 // all components to use two elements when encoded as two smis.
2230 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002231 bool matched = true;
2232 do {
2233 ASSERT(last_match_info_handle->HasFastElements());
2234 // Increase the capacity of the builder before entering local handle-scope,
2235 // so its internal buffer can safely allocate a new handle if it grows.
2236 builder.EnsureCapacity(parts_added_per_loop);
2237
2238 HandleScope loop_scope;
2239 int start, end;
2240 {
2241 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002242 FixedArray* match_info_array =
2243 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002244
2245 ASSERT_EQ(capture_count * 2 + 2,
2246 RegExpImpl::GetLastCaptureCount(match_info_array));
2247 start = RegExpImpl::GetCapture(match_info_array, 0);
2248 end = RegExpImpl::GetCapture(match_info_array, 1);
2249 }
2250
2251 if (prev < start) {
2252 builder.AddSubjectSlice(prev, start);
2253 }
2254 compiled_replacement.Apply(&builder,
2255 start,
2256 end,
2257 last_match_info_handle);
2258 prev = end;
2259
2260 // Only continue checking for global regexps.
2261 if (!is_global) break;
2262
2263 // Continue from where the match ended, unless it was an empty match.
2264 int next = end;
2265 if (start == end) {
2266 next = end + 1;
2267 if (next > length) break;
2268 }
2269
2270 match = RegExpImpl::Exec(regexp_handle,
2271 subject_handle,
2272 next,
2273 last_match_info_handle);
2274 if (match.is_null()) {
2275 return Failure::Exception();
2276 }
2277 matched = !match->IsNull();
2278 } while (matched);
2279
2280 if (prev < length) {
2281 builder.AddSubjectSlice(prev, length);
2282 }
2283
2284 return *(builder.ToString());
2285}
2286
2287
2288static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2289 ASSERT(args.length() == 4);
2290
2291 CONVERT_CHECKED(String, subject, args[0]);
2292 if (!subject->IsFlat()) {
2293 Object* flat_subject = subject->TryFlatten();
2294 if (flat_subject->IsFailure()) {
2295 return flat_subject;
2296 }
2297 subject = String::cast(flat_subject);
2298 }
2299
2300 CONVERT_CHECKED(String, replacement, args[2]);
2301 if (!replacement->IsFlat()) {
2302 Object* flat_replacement = replacement->TryFlatten();
2303 if (flat_replacement->IsFailure()) {
2304 return flat_replacement;
2305 }
2306 replacement = String::cast(flat_replacement);
2307 }
2308
2309 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2310 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2311
2312 ASSERT(last_match_info->HasFastElements());
2313
2314 return StringReplaceRegExpWithString(subject,
2315 regexp,
2316 replacement,
2317 last_match_info);
2318}
2319
2320
ager@chromium.org7c537e22008-10-16 08:43:32 +00002321// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2322// limit, we can fix the size of tables.
2323static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002324// Reduce alphabet to this size.
2325static const int kBMAlphabetSize = 0x100;
2326// For patterns below this length, the skip length of Boyer-Moore is too short
2327// to compensate for the algorithmic overhead compared to simple brute force.
2328static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002329
ager@chromium.org7c537e22008-10-16 08:43:32 +00002330// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2331// shift. Only allows the last kBMMaxShift characters of the needle
2332// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002333class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002334 public:
2335 BMGoodSuffixBuffers() {}
2336 inline void init(int needle_length) {
2337 ASSERT(needle_length > 1);
2338 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2339 int len = needle_length - start;
2340 biased_suffixes_ = suffixes_ - start;
2341 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2342 for (int i = 0; i <= len; i++) {
2343 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002344 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002345 }
2346 inline int& suffix(int index) {
2347 ASSERT(biased_suffixes_ + index >= suffixes_);
2348 return biased_suffixes_[index];
2349 }
2350 inline int& shift(int index) {
2351 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2352 return biased_good_suffix_shift_[index];
2353 }
2354 private:
2355 int suffixes_[kBMMaxShift + 1];
2356 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002357 int* biased_suffixes_;
2358 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002359 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2360};
2361
2362// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002363static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002364static BMGoodSuffixBuffers bmgs_buffers;
2365
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002366// State of the string match tables.
2367// SIMPLE: No usable content in the buffers.
2368// BOYER_MOORE_HORSPOOL: The bad_char_occurences table has been populated.
2369// BOYER_MOORE: The bmgs_buffers tables have also been populated.
2370// Whenever starting with a new needle, one should call InitializeStringSearch
2371// to determine which search strategy to use, and in the case of a long-needle
2372// strategy, the call also initializes the algorithm to SIMPLE.
2373enum StringSearchAlgorithm { SIMPLE_SEARCH, BOYER_MOORE_HORSPOOL, BOYER_MOORE };
2374static StringSearchAlgorithm algorithm;
2375
2376
ager@chromium.org7c537e22008-10-16 08:43:32 +00002377// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002378template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002379static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern) {
2380 // Only preprocess at most kBMMaxShift last characters of pattern.
2381 int start = pattern.length() < kBMMaxShift ? 0
2382 : pattern.length() - kBMMaxShift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002383 // Run forwards to populate bad_char_table, so that *last* instance
2384 // of character equivalence class is the one registered.
2385 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002386 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2387 : kBMAlphabetSize;
2388 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002389 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002390 } else {
2391 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002392 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002393 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002394 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002395 for (int i = start; i < pattern.length() - 1; i++) {
2396 pchar c = pattern[i];
2397 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002398 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002399 }
2400}
2401
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002402
ager@chromium.org7c537e22008-10-16 08:43:32 +00002403template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002404static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002405 int m = pattern.length();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002406 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002407 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002408 // Compute Good Suffix tables.
2409 bmgs_buffers.init(m);
2410
2411 bmgs_buffers.shift(m-1) = 1;
2412 bmgs_buffers.suffix(m) = m + 1;
2413 pchar last_char = pattern[m - 1];
2414 int suffix = m + 1;
2415 for (int i = m; i > start;) {
2416 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2417 if (bmgs_buffers.shift(suffix) == len) {
2418 bmgs_buffers.shift(suffix) = suffix - i;
2419 }
2420 suffix = bmgs_buffers.suffix(suffix);
2421 }
2422 i--;
2423 suffix--;
2424 bmgs_buffers.suffix(i) = suffix;
2425 if (suffix == m) {
2426 // No suffix to extend, so we check against last_char only.
2427 while (i > start && pattern[i - 1] != last_char) {
2428 if (bmgs_buffers.shift(m) == len) {
2429 bmgs_buffers.shift(m) = m - i;
2430 }
2431 i--;
2432 bmgs_buffers.suffix(i) = m;
2433 }
2434 if (i > start) {
2435 i--;
2436 suffix--;
2437 bmgs_buffers.suffix(i) = suffix;
2438 }
2439 }
2440 }
2441 if (suffix < m) {
2442 for (int i = start; i <= m; i++) {
2443 if (bmgs_buffers.shift(i) == len) {
2444 bmgs_buffers.shift(i) = suffix - start;
2445 }
2446 if (i == suffix) {
2447 suffix = bmgs_buffers.suffix(suffix);
2448 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002449 }
2450 }
2451}
2452
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002453
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002454template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002455static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002456 if (sizeof(schar) == 1) {
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 }
2459 if (sizeof(pchar) == 1) {
2460 if (char_code > String::kMaxAsciiCharCode) {
2461 return -1;
2462 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002463 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002464 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002465 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002466}
2467
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002468
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002469// Restricted simplified Boyer-Moore string matching.
2470// Uses only the bad-shift table of Boyer-Moore and only uses it
2471// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002472template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002473static int BoyerMooreHorspool(Vector<const schar> subject,
2474 Vector<const pchar> pattern,
2475 int start_index,
2476 bool* complete) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002477 ASSERT(algorithm <= BOYER_MOORE_HORSPOOL);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002478 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002479 int m = pattern.length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002480
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002481 int badness = -m;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002482
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002483 // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002484 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002485 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002486 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002487 // Perform search
2488 for (idx = start_index; idx <= n - m;) {
2489 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002490 int c;
2491 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002492 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002493 int shift = j - bc_occ;
2494 idx += shift;
2495 badness += 1 - shift; // at most zero, so badness cannot increase.
2496 if (idx > n - m) {
2497 *complete = true;
2498 return -1;
2499 }
2500 }
2501 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002502 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002503 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002504 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002505 return idx;
2506 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002507 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002508 // Badness increases by the number of characters we have
2509 // checked, and decreases by the number of characters we
2510 // can skip by shifting. It's a measure of how we are doing
2511 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002512 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002513 if (badness > 0) {
2514 *complete = false;
2515 return idx;
2516 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002517 }
2518 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002519 *complete = true;
2520 return -1;
2521}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002522
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002523
2524template <typename schar, typename pchar>
2525static int BoyerMooreIndexOf(Vector<const schar> subject,
2526 Vector<const pchar> pattern,
2527 int idx) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002528 ASSERT(algorithm <= BOYER_MOORE);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002529 int n = subject.length();
2530 int m = pattern.length();
2531 // Only preprocess at most kBMMaxShift last characters of pattern.
2532 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2533
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002534 pchar last_char = pattern[m - 1];
2535 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002536 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002537 int j = m - 1;
2538 schar c;
2539 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002540 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002541 idx += shift;
2542 if (idx > n - m) {
2543 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002544 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002545 }
2546 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2547 if (j < 0) {
2548 return idx;
2549 } else if (j < start) {
2550 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002551 // Fall back on BMH shift.
2552 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002553 } else {
2554 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002555 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002556 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002557 if (gs_shift > shift) {
2558 shift = gs_shift;
2559 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002560 idx += shift;
2561 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002562 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002563
2564 return -1;
2565}
2566
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002567
2568template <typename schar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002569static inline int SingleCharIndexOf(Vector<const schar> string,
2570 schar pattern_char,
2571 int start_index) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002572 if (sizeof(schar) == 1) {
2573 const schar* pos = reinterpret_cast<const schar*>(
2574 memchr(string.start() + start_index,
2575 pattern_char,
2576 string.length() - start_index));
2577 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002578 return static_cast<int>(pos - string.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002579 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002580 for (int i = start_index, n = string.length(); i < n; i++) {
2581 if (pattern_char == string[i]) {
2582 return i;
2583 }
2584 }
2585 return -1;
2586}
2587
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002588
2589template <typename schar>
2590static int SingleCharLastIndexOf(Vector<const schar> string,
2591 schar pattern_char,
2592 int start_index) {
2593 for (int i = start_index; i >= 0; i--) {
2594 if (pattern_char == string[i]) {
2595 return i;
2596 }
2597 }
2598 return -1;
2599}
2600
2601
ager@chromium.org7c537e22008-10-16 08:43:32 +00002602// Trivial string search for shorter strings.
2603// On return, if "complete" is set to true, the return value is the
2604// final result of searching for the patter in the subject.
2605// If "complete" is set to false, the return value is the index where
2606// further checking should start, i.e., it's guaranteed that the pattern
2607// does not occur at a position prior to the returned index.
2608template <typename pchar, typename schar>
2609static int SimpleIndexOf(Vector<const schar> subject,
2610 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002611 int idx,
2612 bool* complete) {
2613 // Badness is a count of how much work we have done. When we have
2614 // done enough work we decide it's probably worth switching to a better
2615 // algorithm.
2616 int badness = -10 - (pattern.length() << 2);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002617
ager@chromium.org7c537e22008-10-16 08:43:32 +00002618 // We know our pattern is at least 2 characters, we cache the first so
2619 // the common case of the first character not matching is faster.
2620 pchar pattern_first_char = pattern[0];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002621 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2622 badness++;
2623 if (badness > 0) {
2624 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002625 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002626 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002627 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2628 const schar* pos = reinterpret_cast<const schar*>(
2629 memchr(subject.start() + i,
2630 pattern_first_char,
2631 n - i + 1));
2632 if (pos == NULL) {
2633 *complete = true;
2634 return -1;
2635 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002636 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002637 } else {
2638 if (subject[i] != pattern_first_char) continue;
2639 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002640 int j = 1;
2641 do {
2642 if (pattern[j] != subject[i+j]) {
2643 break;
2644 }
2645 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002646 } while (j < pattern.length());
2647 if (j == pattern.length()) {
2648 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002649 return i;
2650 }
2651 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002652 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002653 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002654 return -1;
2655}
2656
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002657// Simple indexOf that never bails out. For short patterns only.
2658template <typename pchar, typename schar>
2659static int SimpleIndexOf(Vector<const schar> subject,
2660 Vector<const pchar> pattern,
2661 int idx) {
2662 pchar pattern_first_char = pattern[0];
2663 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002664 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2665 const schar* pos = reinterpret_cast<const schar*>(
2666 memchr(subject.start() + i,
2667 pattern_first_char,
2668 n - i + 1));
2669 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002670 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002671 } else {
2672 if (subject[i] != pattern_first_char) continue;
2673 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002674 int j = 1;
2675 do {
2676 if (pattern[j] != subject[i+j]) {
2677 break;
2678 }
2679 j++;
2680 } while (j < pattern.length());
2681 if (j == pattern.length()) {
2682 return i;
2683 }
2684 }
2685 return -1;
2686}
2687
2688
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002689// Strategy for searching for a string in another string.
2690enum StringSearchStrategy { SEARCH_FAIL, SEARCH_SHORT, SEARCH_LONG };
ager@chromium.org7c537e22008-10-16 08:43:32 +00002691
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002692
2693template <typename pchar>
2694static inline StringSearchStrategy InitializeStringSearch(
2695 Vector<const pchar> pat, bool ascii_subject) {
2696 ASSERT(pat.length() > 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002697 // We have an ASCII haystack and a non-ASCII needle. Check if there
2698 // really is a non-ASCII character in the needle and bail out if there
2699 // is.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002700 if (ascii_subject && sizeof(pchar) > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002701 for (int i = 0; i < pat.length(); i++) {
2702 uc16 c = pat[i];
2703 if (c > String::kMaxAsciiCharCode) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002704 return SEARCH_FAIL;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002705 }
2706 }
2707 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002708 if (pat.length() < kBMMinPatternLength) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002709 return SEARCH_SHORT;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002710 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002711 algorithm = SIMPLE_SEARCH;
2712 return SEARCH_LONG;
2713}
2714
2715
2716// Dispatch long needle searches to different algorithms.
2717template <typename schar, typename pchar>
2718static int ComplexIndexOf(Vector<const schar> sub,
2719 Vector<const pchar> pat,
2720 int start_index) {
2721 ASSERT(pat.length() >= kBMMinPatternLength);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002722 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002723 bool complete;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002724 int idx = start_index;
2725 switch (algorithm) {
2726 case SIMPLE_SEARCH:
2727 idx = SimpleIndexOf(sub, pat, idx, &complete);
2728 if (complete) return idx;
2729 BoyerMoorePopulateBadCharTable(pat);
2730 algorithm = BOYER_MOORE_HORSPOOL;
2731 // FALLTHROUGH.
2732 case BOYER_MOORE_HORSPOOL:
2733 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
2734 if (complete) return idx;
2735 // Build the Good Suffix table and continue searching.
2736 BoyerMoorePopulateGoodSuffixTable(pat);
2737 algorithm = BOYER_MOORE;
2738 // FALLTHROUGH.
2739 case BOYER_MOORE:
2740 return BoyerMooreIndexOf(sub, pat, idx);
2741 }
2742 UNREACHABLE();
2743 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002744}
2745
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002746
2747// Dispatch to different search strategies for a single search.
2748// If searching multiple times on the same needle, the search
2749// strategy should only be computed once and then dispatch to different
2750// loops.
2751template <typename schar, typename pchar>
2752static int StringSearch(Vector<const schar> sub,
2753 Vector<const pchar> pat,
2754 int start_index) {
2755 bool ascii_subject = (sizeof(schar) == 1);
2756 StringSearchStrategy strategy = InitializeStringSearch(pat, ascii_subject);
2757 switch (strategy) {
2758 case SEARCH_FAIL: return -1;
2759 case SEARCH_SHORT: return SimpleIndexOf(sub, pat, start_index);
2760 case SEARCH_LONG: return ComplexIndexOf(sub, pat, start_index);
2761 }
2762 UNREACHABLE();
2763 return -1;
2764}
2765
2766
ager@chromium.org7c537e22008-10-16 08:43:32 +00002767// Perform string match of pattern on subject, starting at start index.
2768// Caller must ensure that 0 <= start_index <= sub->length(),
2769// and should check that pat->length() + start_index <= sub->length()
2770int Runtime::StringMatch(Handle<String> sub,
2771 Handle<String> pat,
2772 int start_index) {
2773 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002774 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002775
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002776 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002777 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002778
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002779 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002780 if (start_index + pattern_length > subject_length) return -1;
2781
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002782 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002783 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002784 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002785
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002786 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002787 // character patterns linear search is necessary, so any smart
2788 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002789 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002790 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002791 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002792 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002793 if (pchar > String::kMaxAsciiCharCode) {
2794 return -1;
2795 }
2796 Vector<const char> ascii_vector =
2797 sub->ToAsciiVector().SubVector(start_index, subject_length);
2798 const void* pos = memchr(ascii_vector.start(),
2799 static_cast<const char>(pchar),
2800 static_cast<size_t>(ascii_vector.length()));
2801 if (pos == NULL) {
2802 return -1;
2803 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002804 return static_cast<int>(reinterpret_cast<const char*>(pos)
2805 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002806 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002807 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002808 }
2809
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002810 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002811 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002812 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002813
ager@chromium.org7c537e22008-10-16 08:43:32 +00002814 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2815 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002816 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002817 Vector<const char> pat_vector = pat->ToAsciiVector();
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);
ager@chromium.org236ad962008-09-25 09:45:57 +00002820 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002821 return StringSearch(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002822 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002823 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002824 if (sub->IsAsciiRepresentation()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002825 return StringSearch(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002826 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002827 return StringSearch(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002828}
2829
2830
2831static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002832 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002833 ASSERT(args.length() == 3);
2834
ager@chromium.org7c537e22008-10-16 08:43:32 +00002835 CONVERT_ARG_CHECKED(String, sub, 0);
2836 CONVERT_ARG_CHECKED(String, pat, 1);
2837
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002838 Object* index = args[2];
2839 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002840 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002841
ager@chromium.org870a0b62008-11-04 11:43:05 +00002842 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002843 int position = Runtime::StringMatch(sub, pat, start_index);
2844 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002845}
2846
2847
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002848template <typename schar, typename pchar>
2849static int StringMatchBackwards(Vector<const schar> sub,
2850 Vector<const pchar> pat,
2851 int idx) {
2852 ASSERT(pat.length() >= 1);
2853 ASSERT(idx + pat.length() <= sub.length());
2854
2855 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
2856 for (int i = 0; i < pat.length(); i++) {
2857 uc16 c = pat[i];
2858 if (c > String::kMaxAsciiCharCode) {
2859 return -1;
2860 }
2861 }
2862 }
2863
2864 pchar pattern_first_char = pat[0];
2865 for (int i = idx; i >= 0; i--) {
2866 if (sub[i] != pattern_first_char) continue;
2867 int j = 1;
2868 while (j < pat.length()) {
2869 if (pat[j] != sub[i+j]) {
2870 break;
2871 }
2872 j++;
2873 }
2874 if (j == pat.length()) {
2875 return i;
2876 }
2877 }
2878 return -1;
2879}
2880
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002881static Object* Runtime_StringLastIndexOf(Arguments args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002882 HandleScope scope; // create a new handle scope
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002883 ASSERT(args.length() == 3);
2884
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002885 CONVERT_ARG_CHECKED(String, sub, 0);
2886 CONVERT_ARG_CHECKED(String, pat, 1);
2887
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002888 Object* index = args[2];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002889 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002890 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002891
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002892 uint32_t pat_length = pat->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002893 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002894
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002895 if (start_index + pat_length > sub_length) {
2896 start_index = sub_length - pat_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002897 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002898
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002899 if (pat_length == 0) {
2900 return Smi::FromInt(start_index);
2901 }
2902
2903 if (!sub->IsFlat()) {
2904 FlattenString(sub);
2905 }
2906
2907 if (pat_length == 1) {
2908 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2909 if (sub->IsAsciiRepresentation()) {
2910 uc16 pchar = pat->Get(0);
2911 if (pchar > String::kMaxAsciiCharCode) {
2912 return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002913 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002914 return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
2915 static_cast<char>(pat->Get(0)),
2916 start_index));
2917 } else {
2918 return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
2919 pat->Get(0),
2920 start_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002921 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002922 }
2923
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002924 if (!pat->IsFlat()) {
2925 FlattenString(pat);
2926 }
2927
2928 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2929
2930 int position = -1;
2931
2932 if (pat->IsAsciiRepresentation()) {
2933 Vector<const char> pat_vector = pat->ToAsciiVector();
2934 if (sub->IsAsciiRepresentation()) {
2935 position = StringMatchBackwards(sub->ToAsciiVector(),
2936 pat_vector,
2937 start_index);
2938 } else {
2939 position = StringMatchBackwards(sub->ToUC16Vector(),
2940 pat_vector,
2941 start_index);
2942 }
2943 } else {
2944 Vector<const uc16> pat_vector = pat->ToUC16Vector();
2945 if (sub->IsAsciiRepresentation()) {
2946 position = StringMatchBackwards(sub->ToAsciiVector(),
2947 pat_vector,
2948 start_index);
2949 } else {
2950 position = StringMatchBackwards(sub->ToUC16Vector(),
2951 pat_vector,
2952 start_index);
2953 }
2954 }
2955
2956 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002957}
2958
2959
2960static Object* Runtime_StringLocaleCompare(Arguments args) {
2961 NoHandleAllocation ha;
2962 ASSERT(args.length() == 2);
2963
2964 CONVERT_CHECKED(String, str1, args[0]);
2965 CONVERT_CHECKED(String, str2, args[1]);
2966
2967 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002968 int str1_length = str1->length();
2969 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002970
2971 // Decide trivial cases without flattening.
2972 if (str1_length == 0) {
2973 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2974 return Smi::FromInt(-str2_length);
2975 } else {
2976 if (str2_length == 0) return Smi::FromInt(str1_length);
2977 }
2978
2979 int end = str1_length < str2_length ? str1_length : str2_length;
2980
2981 // No need to flatten if we are going to find the answer on the first
2982 // character. At this point we know there is at least one character
2983 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002984 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002985 if (d != 0) return Smi::FromInt(d);
2986
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002987 str1->TryFlatten();
2988 str2->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002989
2990 static StringInputBuffer buf1;
2991 static StringInputBuffer buf2;
2992
2993 buf1.Reset(str1);
2994 buf2.Reset(str2);
2995
2996 for (int i = 0; i < end; i++) {
2997 uint16_t char1 = buf1.GetNext();
2998 uint16_t char2 = buf2.GetNext();
2999 if (char1 != char2) return Smi::FromInt(char1 - char2);
3000 }
3001
3002 return Smi::FromInt(str1_length - str2_length);
3003}
3004
3005
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003006static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003007 NoHandleAllocation ha;
3008 ASSERT(args.length() == 3);
3009
3010 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00003011 Object* from = args[1];
3012 Object* to = args[2];
3013 int start, end;
3014 // We have a fast integer-only case here to avoid a conversion to double in
3015 // the common case where from and to are Smis.
3016 if (from->IsSmi() && to->IsSmi()) {
3017 start = Smi::cast(from)->value();
3018 end = Smi::cast(to)->value();
3019 } else {
3020 CONVERT_DOUBLE_CHECKED(from_number, from);
3021 CONVERT_DOUBLE_CHECKED(to_number, to);
3022 start = FastD2I(from_number);
3023 end = FastD2I(to_number);
3024 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003025 RUNTIME_ASSERT(end >= start);
3026 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003027 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003028 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003029 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003030}
3031
3032
ager@chromium.org41826e72009-03-30 13:30:57 +00003033static Object* Runtime_StringMatch(Arguments args) {
3034 ASSERT_EQ(3, args.length());
3035
3036 CONVERT_ARG_CHECKED(String, subject, 0);
3037 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
3038 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
3039 HandleScope handles;
3040
3041 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
3042
3043 if (match.is_null()) {
3044 return Failure::Exception();
3045 }
3046 if (match->IsNull()) {
3047 return Heap::null_value();
3048 }
3049 int length = subject->length();
3050
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00003051 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00003052 ZoneList<int> offsets(8);
3053 do {
3054 int start;
3055 int end;
3056 {
3057 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003058 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00003059 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
3060 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
3061 }
3062 offsets.Add(start);
3063 offsets.Add(end);
3064 int index = start < end ? end : end + 1;
3065 if (index > length) break;
3066 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
3067 if (match.is_null()) {
3068 return Failure::Exception();
3069 }
3070 } while (!match->IsNull());
3071 int matches = offsets.length() / 2;
3072 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
3073 for (int i = 0; i < matches ; i++) {
3074 int from = offsets.at(i * 2);
3075 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003076 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00003077 }
3078 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
3079 result->set_length(Smi::FromInt(matches));
3080 return *result;
3081}
3082
3083
lrn@chromium.org25156de2010-04-06 13:10:27 +00003084// Two smis before and after the match, for very long strings.
3085const int kMaxBuilderEntriesPerRegExpMatch = 5;
3086
3087
3088static void SetLastMatchInfoNoCaptures(Handle<String> subject,
3089 Handle<JSArray> last_match_info,
3090 int match_start,
3091 int match_end) {
3092 // Fill last_match_info with a single capture.
3093 last_match_info->EnsureSize(2 + RegExpImpl::kLastMatchOverhead);
3094 AssertNoAllocation no_gc;
3095 FixedArray* elements = FixedArray::cast(last_match_info->elements());
3096 RegExpImpl::SetLastCaptureCount(elements, 2);
3097 RegExpImpl::SetLastInput(elements, *subject);
3098 RegExpImpl::SetLastSubject(elements, *subject);
3099 RegExpImpl::SetCapture(elements, 0, match_start);
3100 RegExpImpl::SetCapture(elements, 1, match_end);
3101}
3102
3103
3104template <typename schar>
3105static bool SearchCharMultiple(Vector<schar> subject,
3106 String* pattern,
3107 schar pattern_char,
3108 FixedArrayBuilder* builder,
3109 int* match_pos) {
3110 // Position of last match.
3111 int pos = *match_pos;
3112 int subject_length = subject.length();
3113 while (pos < subject_length) {
3114 int match_end = pos + 1;
3115 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3116 *match_pos = pos;
3117 return false;
3118 }
3119 int new_pos = SingleCharIndexOf(subject, pattern_char, match_end);
3120 if (new_pos >= 0) {
3121 // Match has been found.
3122 if (new_pos > match_end) {
3123 ReplacementStringBuilder::AddSubjectSlice(builder, match_end, new_pos);
3124 }
3125 pos = new_pos;
3126 builder->Add(pattern);
3127 } else {
3128 break;
3129 }
3130 }
3131 if (pos + 1 < subject_length) {
3132 ReplacementStringBuilder::AddSubjectSlice(builder, pos + 1, subject_length);
3133 }
3134 *match_pos = pos;
3135 return true;
3136}
3137
3138
3139static bool SearchCharMultiple(Handle<String> subject,
3140 Handle<String> pattern,
3141 Handle<JSArray> last_match_info,
3142 FixedArrayBuilder* builder) {
3143 ASSERT(subject->IsFlat());
3144 ASSERT_EQ(1, pattern->length());
3145 uc16 pattern_char = pattern->Get(0);
3146 // Treating position before first as initial "previous match position".
3147 int match_pos = -1;
3148
3149 for (;;) { // Break when search complete.
3150 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3151 AssertNoAllocation no_gc;
3152 if (subject->IsAsciiRepresentation()) {
3153 if (pattern_char > String::kMaxAsciiCharCode) {
3154 break;
3155 }
3156 Vector<const char> subject_vector = subject->ToAsciiVector();
3157 char pattern_ascii_char = static_cast<char>(pattern_char);
3158 bool complete = SearchCharMultiple<const char>(subject_vector,
3159 *pattern,
3160 pattern_ascii_char,
3161 builder,
3162 &match_pos);
3163 if (complete) break;
3164 } else {
3165 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3166 bool complete = SearchCharMultiple<const uc16>(subject_vector,
3167 *pattern,
3168 pattern_char,
3169 builder,
3170 &match_pos);
3171 if (complete) break;
3172 }
3173 }
3174
3175 if (match_pos >= 0) {
3176 SetLastMatchInfoNoCaptures(subject,
3177 last_match_info,
3178 match_pos,
3179 match_pos + 1);
3180 return true;
3181 }
3182 return false; // No matches at all.
3183}
3184
3185
3186template <typename schar, typename pchar>
3187static bool SearchStringMultiple(Vector<schar> subject,
3188 String* pattern,
3189 Vector<pchar> pattern_string,
3190 FixedArrayBuilder* builder,
3191 int* match_pos) {
3192 int pos = *match_pos;
3193 int subject_length = subject.length();
3194 int pattern_length = pattern_string.length();
3195 int max_search_start = subject_length - pattern_length;
3196 bool is_ascii = (sizeof(schar) == 1);
3197 StringSearchStrategy strategy =
3198 InitializeStringSearch(pattern_string, is_ascii);
3199 switch (strategy) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003200 case SEARCH_FAIL: break;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003201 case SEARCH_SHORT:
3202 while (pos <= max_search_start) {
3203 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3204 *match_pos = pos;
3205 return false;
3206 }
3207 // Position of end of previous match.
3208 int match_end = pos + pattern_length;
3209 int new_pos = SimpleIndexOf(subject, pattern_string, match_end);
3210 if (new_pos >= 0) {
3211 // A match.
3212 if (new_pos > match_end) {
3213 ReplacementStringBuilder::AddSubjectSlice(builder,
3214 match_end,
3215 new_pos);
3216 }
3217 pos = new_pos;
3218 builder->Add(pattern);
3219 } else {
3220 break;
3221 }
3222 }
3223 break;
3224 case SEARCH_LONG:
3225 while (pos <= max_search_start) {
3226 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003227 *match_pos = pos;
3228 return false;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003229 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003230 int match_end = pos + pattern_length;
3231 int new_pos = ComplexIndexOf(subject, pattern_string, match_end);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003232 if (new_pos >= 0) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003233 // A match has been found.
3234 if (new_pos > match_end) {
3235 ReplacementStringBuilder::AddSubjectSlice(builder,
3236 match_end,
3237 new_pos);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003238 }
3239 pos = new_pos;
3240 builder->Add(pattern);
3241 } else {
3242 break;
3243 }
3244 }
3245 break;
3246 }
3247 if (pos < max_search_start) {
3248 ReplacementStringBuilder::AddSubjectSlice(builder,
3249 pos + pattern_length,
3250 subject_length);
3251 }
3252 *match_pos = pos;
3253 return true;
3254}
3255
3256
3257static bool SearchStringMultiple(Handle<String> subject,
3258 Handle<String> pattern,
3259 Handle<JSArray> last_match_info,
3260 FixedArrayBuilder* builder) {
3261 ASSERT(subject->IsFlat());
3262 ASSERT(pattern->IsFlat());
3263 ASSERT(pattern->length() > 1);
3264
3265 // Treating as if a previous match was before first character.
3266 int match_pos = -pattern->length();
3267
3268 for (;;) { // Break when search complete.
3269 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3270 AssertNoAllocation no_gc;
3271 if (subject->IsAsciiRepresentation()) {
3272 Vector<const char> subject_vector = subject->ToAsciiVector();
3273 if (pattern->IsAsciiRepresentation()) {
3274 if (SearchStringMultiple(subject_vector,
3275 *pattern,
3276 pattern->ToAsciiVector(),
3277 builder,
3278 &match_pos)) break;
3279 } else {
3280 if (SearchStringMultiple(subject_vector,
3281 *pattern,
3282 pattern->ToUC16Vector(),
3283 builder,
3284 &match_pos)) break;
3285 }
3286 } else {
3287 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3288 if (pattern->IsAsciiRepresentation()) {
3289 if (SearchStringMultiple(subject_vector,
3290 *pattern,
3291 pattern->ToAsciiVector(),
3292 builder,
3293 &match_pos)) break;
3294 } else {
3295 if (SearchStringMultiple(subject_vector,
3296 *pattern,
3297 pattern->ToUC16Vector(),
3298 builder,
3299 &match_pos)) break;
3300 }
3301 }
3302 }
3303
3304 if (match_pos >= 0) {
3305 SetLastMatchInfoNoCaptures(subject,
3306 last_match_info,
3307 match_pos,
3308 match_pos + pattern->length());
3309 return true;
3310 }
3311 return false; // No matches at all.
3312}
3313
3314
3315static RegExpImpl::IrregexpResult SearchRegExpNoCaptureMultiple(
3316 Handle<String> subject,
3317 Handle<JSRegExp> regexp,
3318 Handle<JSArray> last_match_array,
3319 FixedArrayBuilder* builder) {
3320 ASSERT(subject->IsFlat());
3321 int match_start = -1;
3322 int match_end = 0;
3323 int pos = 0;
3324 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3325 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3326
3327 OffsetsVector registers(required_registers);
3328 Vector<int> register_vector(registers.vector(), registers.length());
3329 int subject_length = subject->length();
3330
3331 for (;;) { // Break on failure, return on exception.
3332 RegExpImpl::IrregexpResult result =
3333 RegExpImpl::IrregexpExecOnce(regexp,
3334 subject,
3335 pos,
3336 register_vector);
3337 if (result == RegExpImpl::RE_SUCCESS) {
3338 match_start = register_vector[0];
3339 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3340 if (match_end < match_start) {
3341 ReplacementStringBuilder::AddSubjectSlice(builder,
3342 match_end,
3343 match_start);
3344 }
3345 match_end = register_vector[1];
3346 HandleScope loop_scope;
3347 builder->Add(*Factory::NewSubString(subject, match_start, match_end));
3348 if (match_start != match_end) {
3349 pos = match_end;
3350 } else {
3351 pos = match_end + 1;
3352 if (pos > subject_length) break;
3353 }
3354 } else if (result == RegExpImpl::RE_FAILURE) {
3355 break;
3356 } else {
3357 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3358 return result;
3359 }
3360 }
3361
3362 if (match_start >= 0) {
3363 if (match_end < subject_length) {
3364 ReplacementStringBuilder::AddSubjectSlice(builder,
3365 match_end,
3366 subject_length);
3367 }
3368 SetLastMatchInfoNoCaptures(subject,
3369 last_match_array,
3370 match_start,
3371 match_end);
3372 return RegExpImpl::RE_SUCCESS;
3373 } else {
3374 return RegExpImpl::RE_FAILURE; // No matches at all.
3375 }
3376}
3377
3378
3379static RegExpImpl::IrregexpResult SearchRegExpMultiple(
3380 Handle<String> subject,
3381 Handle<JSRegExp> regexp,
3382 Handle<JSArray> last_match_array,
3383 FixedArrayBuilder* builder) {
3384
3385 ASSERT(subject->IsFlat());
3386 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3387 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3388
3389 OffsetsVector registers(required_registers);
3390 Vector<int> register_vector(registers.vector(), registers.length());
3391
3392 RegExpImpl::IrregexpResult result =
3393 RegExpImpl::IrregexpExecOnce(regexp,
3394 subject,
3395 0,
3396 register_vector);
3397
3398 int capture_count = regexp->CaptureCount();
3399 int subject_length = subject->length();
3400
3401 // Position to search from.
3402 int pos = 0;
3403 // End of previous match. Differs from pos if match was empty.
3404 int match_end = 0;
3405 if (result == RegExpImpl::RE_SUCCESS) {
3406 // Need to keep a copy of the previous match for creating last_match_info
3407 // at the end, so we have two vectors that we swap between.
3408 OffsetsVector registers2(required_registers);
3409 Vector<int> prev_register_vector(registers2.vector(), registers2.length());
3410
3411 do {
3412 int match_start = register_vector[0];
3413 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3414 if (match_end < match_start) {
3415 ReplacementStringBuilder::AddSubjectSlice(builder,
3416 match_end,
3417 match_start);
3418 }
3419 match_end = register_vector[1];
3420
3421 {
3422 // Avoid accumulating new handles inside loop.
3423 HandleScope temp_scope;
3424 // Arguments array to replace function is match, captures, index and
3425 // subject, i.e., 3 + capture count in total.
3426 Handle<FixedArray> elements = Factory::NewFixedArray(3 + capture_count);
3427 elements->set(0, *Factory::NewSubString(subject,
3428 match_start,
3429 match_end));
3430 for (int i = 1; i <= capture_count; i++) {
3431 int start = register_vector[i * 2];
3432 if (start >= 0) {
3433 int end = register_vector[i * 2 + 1];
3434 ASSERT(start <= end);
3435 Handle<String> substring = Factory::NewSubString(subject,
3436 start,
3437 end);
3438 elements->set(i, *substring);
3439 } else {
3440 ASSERT(register_vector[i * 2 + 1] < 0);
3441 elements->set(i, Heap::undefined_value());
3442 }
3443 }
3444 elements->set(capture_count + 1, Smi::FromInt(match_start));
3445 elements->set(capture_count + 2, *subject);
3446 builder->Add(*Factory::NewJSArrayWithElements(elements));
3447 }
3448 // Swap register vectors, so the last successful match is in
3449 // prev_register_vector.
3450 Vector<int> tmp = prev_register_vector;
3451 prev_register_vector = register_vector;
3452 register_vector = tmp;
3453
3454 if (match_end > match_start) {
3455 pos = match_end;
3456 } else {
3457 pos = match_end + 1;
3458 if (pos > subject_length) {
3459 break;
3460 }
3461 }
3462
3463 result = RegExpImpl::IrregexpExecOnce(regexp,
3464 subject,
3465 pos,
3466 register_vector);
3467 } while (result == RegExpImpl::RE_SUCCESS);
3468
3469 if (result != RegExpImpl::RE_EXCEPTION) {
3470 // Finished matching, with at least one match.
3471 if (match_end < subject_length) {
3472 ReplacementStringBuilder::AddSubjectSlice(builder,
3473 match_end,
3474 subject_length);
3475 }
3476
3477 int last_match_capture_count = (capture_count + 1) * 2;
3478 int last_match_array_size =
3479 last_match_capture_count + RegExpImpl::kLastMatchOverhead;
3480 last_match_array->EnsureSize(last_match_array_size);
3481 AssertNoAllocation no_gc;
3482 FixedArray* elements = FixedArray::cast(last_match_array->elements());
3483 RegExpImpl::SetLastCaptureCount(elements, last_match_capture_count);
3484 RegExpImpl::SetLastSubject(elements, *subject);
3485 RegExpImpl::SetLastInput(elements, *subject);
3486 for (int i = 0; i < last_match_capture_count; i++) {
3487 RegExpImpl::SetCapture(elements, i, prev_register_vector[i]);
3488 }
3489 return RegExpImpl::RE_SUCCESS;
3490 }
3491 }
3492 // No matches at all, return failure or exception result directly.
3493 return result;
3494}
3495
3496
3497static Object* Runtime_RegExpExecMultiple(Arguments args) {
3498 ASSERT(args.length() == 4);
3499 HandleScope handles;
3500
3501 CONVERT_ARG_CHECKED(String, subject, 1);
3502 if (!subject->IsFlat()) { FlattenString(subject); }
3503 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
3504 CONVERT_ARG_CHECKED(JSArray, last_match_info, 2);
3505 CONVERT_ARG_CHECKED(JSArray, result_array, 3);
3506
3507 ASSERT(last_match_info->HasFastElements());
3508 ASSERT(regexp->GetFlags().is_global());
3509 Handle<FixedArray> result_elements;
3510 if (result_array->HasFastElements()) {
3511 result_elements =
3512 Handle<FixedArray>(FixedArray::cast(result_array->elements()));
3513 } else {
3514 result_elements = Factory::NewFixedArrayWithHoles(16);
3515 }
3516 FixedArrayBuilder builder(result_elements);
3517
3518 if (regexp->TypeTag() == JSRegExp::ATOM) {
3519 Handle<String> pattern(
3520 String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex)));
3521 int pattern_length = pattern->length();
3522 if (pattern_length == 1) {
3523 if (SearchCharMultiple(subject, pattern, last_match_info, &builder)) {
3524 return *builder.ToJSArray(result_array);
3525 }
3526 return Heap::null_value();
3527 }
3528
3529 if (!pattern->IsFlat()) FlattenString(pattern);
3530 if (SearchStringMultiple(subject, pattern, last_match_info, &builder)) {
3531 return *builder.ToJSArray(result_array);
3532 }
3533 return Heap::null_value();
3534 }
3535
3536 ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
3537
3538 RegExpImpl::IrregexpResult result;
3539 if (regexp->CaptureCount() == 0) {
3540 result = SearchRegExpNoCaptureMultiple(subject,
3541 regexp,
3542 last_match_info,
3543 &builder);
3544 } else {
3545 result = SearchRegExpMultiple(subject, regexp, last_match_info, &builder);
3546 }
3547 if (result == RegExpImpl::RE_SUCCESS) return *builder.ToJSArray(result_array);
3548 if (result == RegExpImpl::RE_FAILURE) return Heap::null_value();
3549 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3550 return Failure::Exception();
3551}
3552
3553
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003554static Object* Runtime_NumberToRadixString(Arguments args) {
3555 NoHandleAllocation ha;
3556 ASSERT(args.length() == 2);
3557
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003558 // Fast case where the result is a one character string.
3559 if (args[0]->IsSmi() && args[1]->IsSmi()) {
3560 int value = Smi::cast(args[0])->value();
3561 int radix = Smi::cast(args[1])->value();
3562 if (value >= 0 && value < radix) {
3563 RUNTIME_ASSERT(radix <= 36);
3564 // Character array used for conversion.
3565 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
3566 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
3567 }
3568 }
3569
3570 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003571 CONVERT_DOUBLE_CHECKED(value, args[0]);
3572 if (isnan(value)) {
3573 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3574 }
3575 if (isinf(value)) {
3576 if (value < 0) {
3577 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3578 }
3579 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3580 }
3581 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
3582 int radix = FastD2I(radix_number);
3583 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3584 char* str = DoubleToRadixCString(value, radix);
3585 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3586 DeleteArray(str);
3587 return result;
3588}
3589
3590
3591static Object* Runtime_NumberToFixed(Arguments args) {
3592 NoHandleAllocation ha;
3593 ASSERT(args.length() == 2);
3594
3595 CONVERT_DOUBLE_CHECKED(value, args[0]);
3596 if (isnan(value)) {
3597 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3598 }
3599 if (isinf(value)) {
3600 if (value < 0) {
3601 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3602 }
3603 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3604 }
3605 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3606 int f = FastD2I(f_number);
3607 RUNTIME_ASSERT(f >= 0);
3608 char* str = DoubleToFixedCString(value, f);
3609 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3610 DeleteArray(str);
3611 return res;
3612}
3613
3614
3615static Object* Runtime_NumberToExponential(Arguments args) {
3616 NoHandleAllocation ha;
3617 ASSERT(args.length() == 2);
3618
3619 CONVERT_DOUBLE_CHECKED(value, args[0]);
3620 if (isnan(value)) {
3621 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3622 }
3623 if (isinf(value)) {
3624 if (value < 0) {
3625 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3626 }
3627 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3628 }
3629 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3630 int f = FastD2I(f_number);
3631 RUNTIME_ASSERT(f >= -1 && f <= 20);
3632 char* str = DoubleToExponentialCString(value, f);
3633 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3634 DeleteArray(str);
3635 return res;
3636}
3637
3638
3639static Object* Runtime_NumberToPrecision(Arguments args) {
3640 NoHandleAllocation ha;
3641 ASSERT(args.length() == 2);
3642
3643 CONVERT_DOUBLE_CHECKED(value, args[0]);
3644 if (isnan(value)) {
3645 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3646 }
3647 if (isinf(value)) {
3648 if (value < 0) {
3649 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3650 }
3651 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3652 }
3653 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3654 int f = FastD2I(f_number);
3655 RUNTIME_ASSERT(f >= 1 && f <= 21);
3656 char* str = DoubleToPrecisionCString(value, f);
3657 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3658 DeleteArray(str);
3659 return res;
3660}
3661
3662
3663// Returns a single character string where first character equals
3664// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003665static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003666 if (index < static_cast<uint32_t>(string->length())) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003667 string->TryFlatten();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003668 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003669 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003670 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003671 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003672}
3673
3674
3675Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
3676 // Handle [] indexing on Strings
3677 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003678 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
3679 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003680 }
3681
3682 // Handle [] indexing on String objects
3683 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003684 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
3685 Handle<Object> result =
3686 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
3687 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003688 }
3689
3690 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003691 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003692 return prototype->GetElement(index);
3693 }
3694
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003695 return GetElement(object, index);
3696}
3697
3698
3699Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003700 return object->GetElement(index);
3701}
3702
3703
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003704Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
3705 HandleScope scope;
3706
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003707 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003708 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003709 Handle<Object> error =
3710 Factory::NewTypeError("non_object_property_load",
3711 HandleVector(args, 2));
3712 return Top::Throw(*error);
3713 }
3714
3715 // Check if the given key is an array index.
3716 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003717 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003718 return GetElementOrCharAt(object, index);
3719 }
3720
3721 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003722 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003723 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003724 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003725 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003726 bool has_pending_exception = false;
3727 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003728 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003729 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003730 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003731 }
3732
ager@chromium.org32912102009-01-16 10:38:43 +00003733 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003734 // the element if so.
3735 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003736 return GetElementOrCharAt(object, index);
3737 } else {
3738 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003739 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003740 }
3741}
3742
3743
3744static Object* Runtime_GetProperty(Arguments args) {
3745 NoHandleAllocation ha;
3746 ASSERT(args.length() == 2);
3747
3748 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003749 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003750
3751 return Runtime::GetObjectProperty(object, key);
3752}
3753
3754
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003755// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003756static Object* Runtime_KeyedGetProperty(Arguments args) {
3757 NoHandleAllocation ha;
3758 ASSERT(args.length() == 2);
3759
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003760 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00003761 // itself.
3762 //
3763 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00003764 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00003765 // global proxy object never has properties. This is the case
3766 // because the global proxy object forwards everything to its hidden
3767 // prototype including local lookups.
3768 //
3769 // Additionally, we need to make sure that we do not cache results
3770 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003771 if (args[0]->IsJSObject() &&
3772 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00003773 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003774 args[1]->IsString()) {
3775 JSObject* receiver = JSObject::cast(args[0]);
3776 String* key = String::cast(args[1]);
3777 if (receiver->HasFastProperties()) {
3778 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003779 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003780 int offset = KeyedLookupCache::Lookup(receiver_map, key);
3781 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003782 Object* value = receiver->FastPropertyAt(offset);
3783 return value->IsTheHole() ? Heap::undefined_value() : value;
3784 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003785 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003786 LookupResult result;
3787 receiver->LocalLookup(key, &result);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00003788 if (result.IsProperty() && result.type() == FIELD) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003789 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003790 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003791 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003792 }
3793 } else {
3794 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003795 StringDictionary* dictionary = receiver->property_dictionary();
3796 int entry = dictionary->FindEntry(key);
3797 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003798 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003799 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003800 if (!receiver->IsGlobalObject()) return value;
3801 value = JSGlobalPropertyCell::cast(value)->value();
3802 if (!value->IsTheHole()) return value;
3803 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003804 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003805 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003806 } else if (args[0]->IsString() && args[1]->IsSmi()) {
3807 // Fast case for string indexing using [] with a smi index.
3808 HandleScope scope;
3809 Handle<String> str = args.at<String>(0);
3810 int index = Smi::cast(args[1])->value();
3811 Handle<Object> result = GetCharAt(str, index);
3812 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003813 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003814
3815 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003816 return Runtime::GetObjectProperty(args.at<Object>(0),
3817 args.at<Object>(1));
3818}
3819
3820
ager@chromium.org5c838252010-02-19 08:53:10 +00003821static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
3822 ASSERT(args.length() == 5);
3823 HandleScope scope;
3824 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3825 CONVERT_CHECKED(String, name, args[1]);
3826 CONVERT_CHECKED(Smi, flag_setter, args[2]);
3827 CONVERT_CHECKED(JSFunction, fun, args[3]);
3828 CONVERT_CHECKED(Smi, flag_attr, args[4]);
3829 int unchecked = flag_attr->value();
3830 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3831 RUNTIME_ASSERT(!obj->IsNull());
3832 LookupResult result;
3833 obj->LocalLookupRealNamedProperty(name, &result);
3834
3835 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3836 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
3837 // delete it to avoid running into trouble in DefineAccessor, which
3838 // handles this incorrectly if the property is readonly (does nothing)
3839 if (result.IsProperty() &&
3840 (result.type() == FIELD || result.type() == NORMAL
3841 || result.type() == CONSTANT_FUNCTION)) {
3842 obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
3843 }
3844 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
3845}
3846
3847static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
3848 ASSERT(args.length() == 4);
3849 HandleScope scope;
3850 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
3851 CONVERT_ARG_CHECKED(String, name, 1);
3852 Handle<Object> obj_value = args.at<Object>(2);
3853
3854 CONVERT_CHECKED(Smi, flag, args[3]);
3855 int unchecked = flag->value();
3856 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3857
3858 LookupResult result;
3859 js_object->LocalLookupRealNamedProperty(*name, &result);
3860
3861 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3862
3863 // Take special care when attributes are different and there is already
3864 // a property. For simplicity we normalize the property which enables us
3865 // to not worry about changing the instance_descriptor and creating a new
3866 // map. The current version of SetObjectProperty does not handle attributes
3867 // correctly in the case where a property is a field and is reset with
3868 // new attributes.
3869 if (result.IsProperty() && attr != result.GetAttributes()) {
3870 // New attributes - normalize to avoid writing to instance descriptor
3871 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3872 // Use IgnoreAttributes version since a readonly property may be
3873 // overridden and SetProperty does not allow this.
3874 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
3875 *obj_value,
3876 attr);
3877 }
3878 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
3879}
3880
3881
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003882Object* Runtime::SetObjectProperty(Handle<Object> object,
3883 Handle<Object> key,
3884 Handle<Object> value,
3885 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003886 HandleScope scope;
3887
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003888 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003889 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003890 Handle<Object> error =
3891 Factory::NewTypeError("non_object_property_store",
3892 HandleVector(args, 2));
3893 return Top::Throw(*error);
3894 }
3895
3896 // If the object isn't a JavaScript object, we ignore the store.
3897 if (!object->IsJSObject()) return *value;
3898
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003899 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3900
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003901 // Check if the given key is an array index.
3902 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003903 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003904 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3905 // of a string using [] notation. We need to support this too in
3906 // JavaScript.
3907 // In the case of a String object we just need to redirect the assignment to
3908 // the underlying string if the index is in range. Since the underlying
3909 // string does nothing with the assignment then we can ignore such
3910 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003911 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003912 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003913 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003914
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003915 Handle<Object> result = SetElement(js_object, index, value);
3916 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003917 return *value;
3918 }
3919
3920 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003921 Handle<Object> result;
3922 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003923 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003924 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003925 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003926 key_string->TryFlatten();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003927 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003928 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003929 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003930 return *value;
3931 }
3932
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003933 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003934 bool has_pending_exception = false;
3935 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3936 if (has_pending_exception) return Failure::Exception();
3937 Handle<String> name = Handle<String>::cast(converted);
3938
3939 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003940 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003941 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003942 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003943 }
3944}
3945
3946
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003947Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
3948 Handle<Object> key,
3949 Handle<Object> value,
3950 PropertyAttributes attr) {
3951 HandleScope scope;
3952
3953 // Check if the given key is an array index.
3954 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003955 if (key->ToArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003956 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3957 // of a string using [] notation. We need to support this too in
3958 // JavaScript.
3959 // In the case of a String object we just need to redirect the assignment to
3960 // the underlying string if the index is in range. Since the underlying
3961 // string does nothing with the assignment then we can ignore such
3962 // assignments.
3963 if (js_object->IsStringObjectWithCharacterAt(index)) {
3964 return *value;
3965 }
3966
3967 return js_object->SetElement(index, *value);
3968 }
3969
3970 if (key->IsString()) {
3971 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003972 return js_object->SetElement(index, *value);
3973 } else {
3974 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003975 key_string->TryFlatten();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003976 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
3977 *value,
3978 attr);
3979 }
3980 }
3981
3982 // Call-back into JavaScript to convert the key to a string.
3983 bool has_pending_exception = false;
3984 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3985 if (has_pending_exception) return Failure::Exception();
3986 Handle<String> name = Handle<String>::cast(converted);
3987
3988 if (name->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003989 return js_object->SetElement(index, *value);
3990 } else {
3991 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
3992 }
3993}
3994
3995
ager@chromium.orge2902be2009-06-08 12:21:35 +00003996Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
3997 Handle<Object> key) {
3998 HandleScope scope;
3999
4000 // Check if the given key is an array index.
4001 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004002 if (key->ToArrayIndex(&index)) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00004003 // In Firefox/SpiderMonkey, Safari and Opera you can access the
4004 // characters of a string using [] notation. In the case of a
4005 // String object we just need to redirect the deletion to the
4006 // underlying string if the index is in range. Since the
4007 // underlying string does nothing with the deletion, we can ignore
4008 // such deletions.
4009 if (js_object->IsStringObjectWithCharacterAt(index)) {
4010 return Heap::true_value();
4011 }
4012
4013 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
4014 }
4015
4016 Handle<String> key_string;
4017 if (key->IsString()) {
4018 key_string = Handle<String>::cast(key);
4019 } else {
4020 // Call-back into JavaScript to convert the key to a string.
4021 bool has_pending_exception = false;
4022 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4023 if (has_pending_exception) return Failure::Exception();
4024 key_string = Handle<String>::cast(converted);
4025 }
4026
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004027 key_string->TryFlatten();
ager@chromium.orge2902be2009-06-08 12:21:35 +00004028 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
4029}
4030
4031
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004032static Object* Runtime_SetProperty(Arguments args) {
4033 NoHandleAllocation ha;
4034 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
4035
4036 Handle<Object> object = args.at<Object>(0);
4037 Handle<Object> key = args.at<Object>(1);
4038 Handle<Object> value = args.at<Object>(2);
4039
4040 // Compute attributes.
4041 PropertyAttributes attributes = NONE;
4042 if (args.length() == 4) {
4043 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004044 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004045 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004046 RUNTIME_ASSERT(
4047 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4048 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004049 }
4050 return Runtime::SetObjectProperty(object, key, value, attributes);
4051}
4052
4053
4054// Set a local property, even if it is READ_ONLY. If the property does not
4055// exist, it will be added with attributes NONE.
4056static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
4057 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004058 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004059 CONVERT_CHECKED(JSObject, object, args[0]);
4060 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004061 // Compute attributes.
4062 PropertyAttributes attributes = NONE;
4063 if (args.length() == 4) {
4064 CONVERT_CHECKED(Smi, value_obj, args[3]);
4065 int unchecked_value = value_obj->value();
4066 // Only attribute bits should be set.
4067 RUNTIME_ASSERT(
4068 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4069 attributes = static_cast<PropertyAttributes>(unchecked_value);
4070 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004071
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004072 return object->
4073 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004074}
4075
4076
4077static Object* Runtime_DeleteProperty(Arguments args) {
4078 NoHandleAllocation ha;
4079 ASSERT(args.length() == 2);
4080
4081 CONVERT_CHECKED(JSObject, object, args[0]);
4082 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00004083 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004084}
4085
4086
ager@chromium.org9085a012009-05-11 19:22:57 +00004087static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
4088 Handle<String> key) {
4089 if (object->HasLocalProperty(*key)) return Heap::true_value();
4090 // Handle hidden prototypes. If there's a hidden prototype above this thing
4091 // then we have to check it for properties, because they are supposed to
4092 // look like they are on this object.
4093 Handle<Object> proto(object->GetPrototype());
4094 if (proto->IsJSObject() &&
4095 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
4096 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
4097 }
4098 return Heap::false_value();
4099}
4100
4101
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004102static Object* Runtime_HasLocalProperty(Arguments args) {
4103 NoHandleAllocation ha;
4104 ASSERT(args.length() == 2);
4105 CONVERT_CHECKED(String, key, args[1]);
4106
ager@chromium.org9085a012009-05-11 19:22:57 +00004107 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004108 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00004109 if (obj->IsJSObject()) {
4110 JSObject* object = JSObject::cast(obj);
4111 // Fast case - no interceptors.
4112 if (object->HasRealNamedProperty(key)) return Heap::true_value();
4113 // Slow case. Either it's not there or we have an interceptor. We should
4114 // have handles for this kind of deal.
4115 HandleScope scope;
4116 return HasLocalPropertyImplementation(Handle<JSObject>(object),
4117 Handle<String>(key));
4118 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004119 // Well, there is one exception: Handle [] on strings.
4120 uint32_t index;
4121 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00004122 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004123 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004124 return Heap::true_value();
4125 }
4126 }
4127 return Heap::false_value();
4128}
4129
4130
4131static Object* Runtime_HasProperty(Arguments args) {
4132 NoHandleAllocation na;
4133 ASSERT(args.length() == 2);
4134
4135 // Only JS objects can have properties.
4136 if (args[0]->IsJSObject()) {
4137 JSObject* object = JSObject::cast(args[0]);
4138 CONVERT_CHECKED(String, key, args[1]);
4139 if (object->HasProperty(key)) return Heap::true_value();
4140 }
4141 return Heap::false_value();
4142}
4143
4144
4145static Object* Runtime_HasElement(Arguments args) {
4146 NoHandleAllocation na;
4147 ASSERT(args.length() == 2);
4148
4149 // Only JS objects can have elements.
4150 if (args[0]->IsJSObject()) {
4151 JSObject* object = JSObject::cast(args[0]);
4152 CONVERT_CHECKED(Smi, index_obj, args[1]);
4153 uint32_t index = index_obj->value();
4154 if (object->HasElement(index)) return Heap::true_value();
4155 }
4156 return Heap::false_value();
4157}
4158
4159
4160static Object* Runtime_IsPropertyEnumerable(Arguments args) {
4161 NoHandleAllocation ha;
4162 ASSERT(args.length() == 2);
4163
4164 CONVERT_CHECKED(JSObject, object, args[0]);
4165 CONVERT_CHECKED(String, key, args[1]);
4166
4167 uint32_t index;
4168 if (key->AsArrayIndex(&index)) {
4169 return Heap::ToBoolean(object->HasElement(index));
4170 }
4171
ager@chromium.org870a0b62008-11-04 11:43:05 +00004172 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
4173 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004174}
4175
4176
4177static Object* Runtime_GetPropertyNames(Arguments args) {
4178 HandleScope scope;
4179 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004180 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004181 return *GetKeysFor(object);
4182}
4183
4184
4185// Returns either a FixedArray as Runtime_GetPropertyNames,
4186// or, if the given object has an enum cache that contains
4187// all enumerable properties of the object and its prototypes
4188// have none, the map of the object. This is used to speed up
4189// the check for deletions during a for-in.
4190static Object* Runtime_GetPropertyNamesFast(Arguments args) {
4191 ASSERT(args.length() == 1);
4192
4193 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4194
4195 if (raw_object->IsSimpleEnum()) return raw_object->map();
4196
4197 HandleScope scope;
4198 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004199 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
4200 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004201
4202 // Test again, since cache may have been built by preceding call.
4203 if (object->IsSimpleEnum()) return object->map();
4204
4205 return *content;
4206}
4207
4208
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004209// Find the length of the prototype chain that is to to handled as one. If a
4210// prototype object is hidden it is to be viewed as part of the the object it
4211// is prototype for.
4212static int LocalPrototypeChainLength(JSObject* obj) {
4213 int count = 1;
4214 Object* proto = obj->GetPrototype();
4215 while (proto->IsJSObject() &&
4216 JSObject::cast(proto)->map()->is_hidden_prototype()) {
4217 count++;
4218 proto = JSObject::cast(proto)->GetPrototype();
4219 }
4220 return count;
4221}
4222
4223
4224// Return the names of the local named properties.
4225// args[0]: object
4226static Object* Runtime_GetLocalPropertyNames(Arguments args) {
4227 HandleScope scope;
4228 ASSERT(args.length() == 1);
4229 if (!args[0]->IsJSObject()) {
4230 return Heap::undefined_value();
4231 }
4232 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4233
4234 // Skip the global proxy as it has no properties and always delegates to the
4235 // real global object.
4236 if (obj->IsJSGlobalProxy()) {
4237 // Only collect names if access is permitted.
4238 if (obj->IsAccessCheckNeeded() &&
4239 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
4240 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
4241 return *Factory::NewJSArray(0);
4242 }
4243 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
4244 }
4245
4246 // Find the number of objects making up this.
4247 int length = LocalPrototypeChainLength(*obj);
4248
4249 // Find the number of local properties for each of the objects.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004250 ScopedVector<int> local_property_count(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004251 int total_property_count = 0;
4252 Handle<JSObject> jsproto = obj;
4253 for (int i = 0; i < length; i++) {
4254 // Only collect names if access is permitted.
4255 if (jsproto->IsAccessCheckNeeded() &&
4256 !Top::MayNamedAccess(*jsproto,
4257 Heap::undefined_value(),
4258 v8::ACCESS_KEYS)) {
4259 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
4260 return *Factory::NewJSArray(0);
4261 }
4262 int n;
4263 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4264 local_property_count[i] = n;
4265 total_property_count += n;
4266 if (i < length - 1) {
4267 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4268 }
4269 }
4270
4271 // Allocate an array with storage for all the property names.
4272 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
4273
4274 // Get the property names.
4275 jsproto = obj;
4276 int proto_with_hidden_properties = 0;
4277 for (int i = 0; i < length; i++) {
4278 jsproto->GetLocalPropertyNames(*names,
4279 i == 0 ? 0 : local_property_count[i - 1]);
4280 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
4281 proto_with_hidden_properties++;
4282 }
4283 if (i < length - 1) {
4284 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4285 }
4286 }
4287
4288 // Filter out name of hidden propeties object.
4289 if (proto_with_hidden_properties > 0) {
4290 Handle<FixedArray> old_names = names;
4291 names = Factory::NewFixedArray(
4292 names->length() - proto_with_hidden_properties);
4293 int dest_pos = 0;
4294 for (int i = 0; i < total_property_count; i++) {
4295 Object* name = old_names->get(i);
4296 if (name == Heap::hidden_symbol()) {
4297 continue;
4298 }
4299 names->set(dest_pos++, name);
4300 }
4301 }
4302
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004303 return *Factory::NewJSArrayWithElements(names);
4304}
4305
4306
4307// Return the names of the local indexed properties.
4308// args[0]: object
4309static Object* Runtime_GetLocalElementNames(Arguments args) {
4310 HandleScope scope;
4311 ASSERT(args.length() == 1);
4312 if (!args[0]->IsJSObject()) {
4313 return Heap::undefined_value();
4314 }
4315 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4316
4317 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4318 Handle<FixedArray> names = Factory::NewFixedArray(n);
4319 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4320 return *Factory::NewJSArrayWithElements(names);
4321}
4322
4323
4324// Return information on whether an object has a named or indexed interceptor.
4325// args[0]: object
4326static Object* Runtime_GetInterceptorInfo(Arguments args) {
4327 HandleScope scope;
4328 ASSERT(args.length() == 1);
4329 if (!args[0]->IsJSObject()) {
4330 return Smi::FromInt(0);
4331 }
4332 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4333
4334 int result = 0;
4335 if (obj->HasNamedInterceptor()) result |= 2;
4336 if (obj->HasIndexedInterceptor()) result |= 1;
4337
4338 return Smi::FromInt(result);
4339}
4340
4341
4342// Return property names from named interceptor.
4343// args[0]: object
4344static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
4345 HandleScope scope;
4346 ASSERT(args.length() == 1);
4347 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4348
4349 if (obj->HasNamedInterceptor()) {
4350 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4351 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4352 }
4353 return Heap::undefined_value();
4354}
4355
4356
4357// Return element names from indexed interceptor.
4358// args[0]: object
4359static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
4360 HandleScope scope;
4361 ASSERT(args.length() == 1);
4362 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4363
4364 if (obj->HasIndexedInterceptor()) {
4365 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4366 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4367 }
4368 return Heap::undefined_value();
4369}
4370
4371
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004372static Object* Runtime_LocalKeys(Arguments args) {
4373 ASSERT_EQ(args.length(), 1);
4374 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4375 HandleScope scope;
4376 Handle<JSObject> object(raw_object);
4377 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
4378 LOCAL_ONLY);
4379 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
4380 // property array and since the result is mutable we have to create
4381 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004382 int length = contents->length();
4383 Handle<FixedArray> copy = Factory::NewFixedArray(length);
4384 for (int i = 0; i < length; i++) {
4385 Object* entry = contents->get(i);
4386 if (entry->IsString()) {
4387 copy->set(i, entry);
4388 } else {
4389 ASSERT(entry->IsNumber());
4390 HandleScope scope;
4391 Handle<Object> entry_handle(entry);
4392 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
4393 copy->set(i, *entry_str);
4394 }
4395 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004396 return *Factory::NewJSArrayWithElements(copy);
4397}
4398
4399
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004400static Object* Runtime_GetArgumentsProperty(Arguments args) {
4401 NoHandleAllocation ha;
4402 ASSERT(args.length() == 1);
4403
4404 // Compute the frame holding the arguments.
4405 JavaScriptFrameIterator it;
4406 it.AdvanceToArgumentsFrame();
4407 JavaScriptFrame* frame = it.frame();
4408
4409 // Get the actual number of provided arguments.
4410 const uint32_t n = frame->GetProvidedParametersCount();
4411
4412 // Try to convert the key to an index. If successful and within
4413 // index return the the argument from the frame.
4414 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004415 if (args[0]->ToArrayIndex(&index) && index < n) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004416 return frame->GetParameter(index);
4417 }
4418
4419 // Convert the key to a string.
4420 HandleScope scope;
4421 bool exception = false;
4422 Handle<Object> converted =
4423 Execution::ToString(args.at<Object>(0), &exception);
4424 if (exception) return Failure::Exception();
4425 Handle<String> key = Handle<String>::cast(converted);
4426
4427 // Try to convert the string key into an array index.
4428 if (key->AsArrayIndex(&index)) {
4429 if (index < n) {
4430 return frame->GetParameter(index);
4431 } else {
4432 return Top::initial_object_prototype()->GetElement(index);
4433 }
4434 }
4435
4436 // Handle special arguments properties.
4437 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
4438 if (key->Equals(Heap::callee_symbol())) return frame->function();
4439
4440 // Lookup in the initial Object.prototype object.
4441 return Top::initial_object_prototype()->GetProperty(*key);
4442}
4443
4444
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004445static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004446 HandleScope scope;
4447
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004448 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004449 Handle<Object> object = args.at<Object>(0);
4450 if (object->IsJSObject()) {
4451 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00004452 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
4453 js_object->TransformToFastProperties(0);
4454 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004455 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004456 return *object;
4457}
4458
4459
4460static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004461 HandleScope scope;
4462
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004463 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004464 Handle<Object> object = args.at<Object>(0);
4465 if (object->IsJSObject()) {
4466 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004467 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004468 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004469 return *object;
4470}
4471
4472
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004473static Object* Runtime_ToBool(Arguments args) {
4474 NoHandleAllocation ha;
4475 ASSERT(args.length() == 1);
4476
4477 return args[0]->ToBoolean();
4478}
4479
4480
4481// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
4482// Possible optimizations: put the type string into the oddballs.
4483static Object* Runtime_Typeof(Arguments args) {
4484 NoHandleAllocation ha;
4485
4486 Object* obj = args[0];
4487 if (obj->IsNumber()) return Heap::number_symbol();
4488 HeapObject* heap_obj = HeapObject::cast(obj);
4489
4490 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004491 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004492
4493 InstanceType instance_type = heap_obj->map()->instance_type();
4494 if (instance_type < FIRST_NONSTRING_TYPE) {
4495 return Heap::string_symbol();
4496 }
4497
4498 switch (instance_type) {
4499 case ODDBALL_TYPE:
4500 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
4501 return Heap::boolean_symbol();
4502 }
4503 if (heap_obj->IsNull()) {
4504 return Heap::object_symbol();
4505 }
4506 ASSERT(heap_obj->IsUndefined());
4507 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004508 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004509 return Heap::function_symbol();
4510 default:
4511 // For any kind of object not handled above, the spec rule for
4512 // host objects gives that it is okay to return "object"
4513 return Heap::object_symbol();
4514 }
4515}
4516
4517
lrn@chromium.org25156de2010-04-06 13:10:27 +00004518static bool AreDigits(const char*s, int from, int to) {
4519 for (int i = from; i < to; i++) {
4520 if (s[i] < '0' || s[i] > '9') return false;
4521 }
4522
4523 return true;
4524}
4525
4526
4527static int ParseDecimalInteger(const char*s, int from, int to) {
4528 ASSERT(to - from < 10); // Overflow is not possible.
4529 ASSERT(from < to);
4530 int d = s[from] - '0';
4531
4532 for (int i = from + 1; i < to; i++) {
4533 d = 10 * d + (s[i] - '0');
4534 }
4535
4536 return d;
4537}
4538
4539
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004540static Object* Runtime_StringToNumber(Arguments args) {
4541 NoHandleAllocation ha;
4542 ASSERT(args.length() == 1);
4543 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004544 subject->TryFlatten();
lrn@chromium.org25156de2010-04-06 13:10:27 +00004545
4546 // Fast case: short integer or some sorts of junk values.
4547 int len = subject->length();
4548 if (subject->IsSeqAsciiString()) {
4549 if (len == 0) return Smi::FromInt(0);
4550
4551 char const* data = SeqAsciiString::cast(subject)->GetChars();
4552 bool minus = (data[0] == '-');
4553 int start_pos = (minus ? 1 : 0);
4554
4555 if (start_pos == len) {
4556 return Heap::nan_value();
4557 } else if (data[start_pos] > '9') {
4558 // Fast check for a junk value. A valid string may start from a
4559 // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit or
4560 // the 'I' character ('Infinity'). All of that have codes not greater than
4561 // '9' except 'I'.
4562 if (data[start_pos] != 'I') {
4563 return Heap::nan_value();
4564 }
4565 } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
4566 // The maximal/minimal smi has 10 digits. If the string has less digits we
4567 // know it will fit into the smi-data type.
4568 int d = ParseDecimalInteger(data, start_pos, len);
4569 if (minus) {
4570 if (d == 0) return Heap::minus_zero_value();
4571 d = -d;
4572 }
4573 return Smi::FromInt(d);
4574 }
4575 }
4576
4577 // Slower case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004578 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
4579}
4580
4581
4582static Object* Runtime_StringFromCharCodeArray(Arguments args) {
4583 NoHandleAllocation ha;
4584 ASSERT(args.length() == 1);
4585
4586 CONVERT_CHECKED(JSArray, codes, args[0]);
4587 int length = Smi::cast(codes->length())->value();
4588
4589 // Check if the string can be ASCII.
4590 int i;
4591 for (i = 0; i < length; i++) {
4592 Object* element = codes->GetElement(i);
4593 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
4594 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
4595 break;
4596 }
4597
4598 Object* object = NULL;
4599 if (i == length) { // The string is ASCII.
4600 object = Heap::AllocateRawAsciiString(length);
4601 } else { // The string is not ASCII.
4602 object = Heap::AllocateRawTwoByteString(length);
4603 }
4604
4605 if (object->IsFailure()) return object;
4606 String* result = String::cast(object);
4607 for (int i = 0; i < length; i++) {
4608 Object* element = codes->GetElement(i);
4609 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004610 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004611 }
4612 return result;
4613}
4614
4615
4616// kNotEscaped is generated by the following:
4617//
4618// #!/bin/perl
4619// for (my $i = 0; $i < 256; $i++) {
4620// print "\n" if $i % 16 == 0;
4621// my $c = chr($i);
4622// my $escaped = 1;
4623// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
4624// print $escaped ? "0, " : "1, ";
4625// }
4626
4627
4628static bool IsNotEscaped(uint16_t character) {
4629 // Only for 8 bit characters, the rest are always escaped (in a different way)
4630 ASSERT(character < 256);
4631 static const char kNotEscaped[256] = {
4632 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4633 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4634 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
4635 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
4636 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4637 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
4638 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4639 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
4640 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4641 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4642 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4643 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4644 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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, 0, 0, 0, 0, 0, 0,
4648 };
4649 return kNotEscaped[character] != 0;
4650}
4651
4652
4653static Object* Runtime_URIEscape(Arguments args) {
4654 const char hex_chars[] = "0123456789ABCDEF";
4655 NoHandleAllocation ha;
4656 ASSERT(args.length() == 1);
4657 CONVERT_CHECKED(String, source, args[0]);
4658
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004659 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004660
4661 int escaped_length = 0;
4662 int length = source->length();
4663 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004664 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004665 buffer->Reset(source);
4666 while (buffer->has_more()) {
4667 uint16_t character = buffer->GetNext();
4668 if (character >= 256) {
4669 escaped_length += 6;
4670 } else if (IsNotEscaped(character)) {
4671 escaped_length++;
4672 } else {
4673 escaped_length += 3;
4674 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004675 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004676 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004677 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004678 Top::context()->mark_out_of_memory();
4679 return Failure::OutOfMemoryException();
4680 }
4681 }
4682 }
4683 // No length change implies no change. Return original string if no change.
4684 if (escaped_length == length) {
4685 return source;
4686 }
4687 Object* o = Heap::AllocateRawAsciiString(escaped_length);
4688 if (o->IsFailure()) return o;
4689 String* destination = String::cast(o);
4690 int dest_position = 0;
4691
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004692 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004693 buffer->Rewind();
4694 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00004695 uint16_t chr = buffer->GetNext();
4696 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004697 destination->Set(dest_position, '%');
4698 destination->Set(dest_position+1, 'u');
4699 destination->Set(dest_position+2, hex_chars[chr >> 12]);
4700 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
4701 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
4702 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004703 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00004704 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004705 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004706 dest_position++;
4707 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004708 destination->Set(dest_position, '%');
4709 destination->Set(dest_position+1, hex_chars[chr >> 4]);
4710 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004711 dest_position += 3;
4712 }
4713 }
4714 return destination;
4715}
4716
4717
4718static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
4719 static const signed char kHexValue['g'] = {
4720 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4721 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4722 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4723 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
4724 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4725 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4726 -1, 10, 11, 12, 13, 14, 15 };
4727
4728 if (character1 > 'f') return -1;
4729 int hi = kHexValue[character1];
4730 if (hi == -1) return -1;
4731 if (character2 > 'f') return -1;
4732 int lo = kHexValue[character2];
4733 if (lo == -1) return -1;
4734 return (hi << 4) + lo;
4735}
4736
4737
ager@chromium.org870a0b62008-11-04 11:43:05 +00004738static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004739 int i,
4740 int length,
4741 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004742 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00004743 int32_t hi = 0;
4744 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004745 if (character == '%' &&
4746 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004747 source->Get(i + 1) == 'u' &&
4748 (hi = TwoDigitHex(source->Get(i + 2),
4749 source->Get(i + 3))) != -1 &&
4750 (lo = TwoDigitHex(source->Get(i + 4),
4751 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004752 *step = 6;
4753 return (hi << 8) + lo;
4754 } else if (character == '%' &&
4755 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004756 (lo = TwoDigitHex(source->Get(i + 1),
4757 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004758 *step = 3;
4759 return lo;
4760 } else {
4761 *step = 1;
4762 return character;
4763 }
4764}
4765
4766
4767static Object* Runtime_URIUnescape(Arguments args) {
4768 NoHandleAllocation ha;
4769 ASSERT(args.length() == 1);
4770 CONVERT_CHECKED(String, source, args[0]);
4771
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004772 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004773
4774 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004775 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004776
4777 int unescaped_length = 0;
4778 for (int i = 0; i < length; unescaped_length++) {
4779 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004780 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004781 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004782 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004783 i += step;
4784 }
4785
4786 // No length change implies no change. Return original string if no change.
4787 if (unescaped_length == length)
4788 return source;
4789
4790 Object* o = ascii ?
4791 Heap::AllocateRawAsciiString(unescaped_length) :
4792 Heap::AllocateRawTwoByteString(unescaped_length);
4793 if (o->IsFailure()) return o;
4794 String* destination = String::cast(o);
4795
4796 int dest_position = 0;
4797 for (int i = 0; i < length; dest_position++) {
4798 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004799 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004800 i += step;
4801 }
4802 return destination;
4803}
4804
4805
4806static Object* Runtime_StringParseInt(Arguments args) {
4807 NoHandleAllocation ha;
4808
4809 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004810 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004811
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004812 s->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004813
lrn@chromium.org25156de2010-04-06 13:10:27 +00004814 RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
4815 double value = StringToInt(s, radix);
4816 return Heap::NumberFromDouble(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004817 return Heap::nan_value();
4818}
4819
4820
4821static Object* Runtime_StringParseFloat(Arguments args) {
4822 NoHandleAllocation ha;
4823 CONVERT_CHECKED(String, str, args[0]);
4824
4825 // ECMA-262 section 15.1.2.3, empty string is NaN
4826 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
4827
4828 // Create a number object from the value.
4829 return Heap::NumberFromDouble(value);
4830}
4831
4832
4833static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
4834static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
4835
4836
4837template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004838static Object* ConvertCaseHelper(String* s,
4839 int length,
4840 int input_string_length,
4841 unibrow::Mapping<Converter, 128>* mapping) {
4842 // We try this twice, once with the assumption that the result is no longer
4843 // than the input and, if that assumption breaks, again with the exact
4844 // length. This may not be pretty, but it is nicer than what was here before
4845 // and I hereby claim my vaffel-is.
4846 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004847 // Allocate the resulting string.
4848 //
4849 // NOTE: This assumes that the upper/lower case of an ascii
4850 // character is also ascii. This is currently the case, but it
4851 // might break in the future if we implement more context and locale
4852 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00004853 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004854 ? Heap::AllocateRawAsciiString(length)
4855 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004856 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004857 String* result = String::cast(o);
4858 bool has_changed_character = false;
4859
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004860 // Convert all characters to upper case, assuming that they will fit
4861 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004862 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004863 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004864 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004865 // We can assume that the string is not empty
4866 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004867 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00004868 bool has_next = buffer->has_more();
4869 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004870 int char_length = mapping->get(current, next, chars);
4871 if (char_length == 0) {
4872 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004873 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004874 i++;
4875 } else if (char_length == 1) {
4876 // Common case: converting the letter resulted in one character.
4877 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004878 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004879 has_changed_character = true;
4880 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004881 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004882 // We've assumed that the result would be as long as the
4883 // input but here is a character that converts to several
4884 // characters. No matter, we calculate the exact length
4885 // of the result and try the whole thing again.
4886 //
4887 // Note that this leaves room for optimization. We could just
4888 // memcpy what we already have to the result string. Also,
4889 // the result string is the last object allocated we could
4890 // "realloc" it and probably, in the vast majority of cases,
4891 // extend the existing string to be able to hold the full
4892 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00004893 int next_length = 0;
4894 if (has_next) {
4895 next_length = mapping->get(next, 0, chars);
4896 if (next_length == 0) next_length = 1;
4897 }
4898 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004899 while (buffer->has_more()) {
4900 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00004901 // NOTE: we use 0 as the next character here because, while
4902 // the next character may affect what a character converts to,
4903 // it does not in any case affect the length of what it convert
4904 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004905 int char_length = mapping->get(current, 0, chars);
4906 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00004907 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004908 if (current_length > Smi::kMaxValue) {
4909 Top::context()->mark_out_of_memory();
4910 return Failure::OutOfMemoryException();
4911 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004912 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004913 // Try again with the real length.
4914 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004915 } else {
4916 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004917 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004918 i++;
4919 }
4920 has_changed_character = true;
4921 }
4922 current = next;
4923 }
4924 if (has_changed_character) {
4925 return result;
4926 } else {
4927 // If we didn't actually change anything in doing the conversion
4928 // we simple return the result and let the converted string
4929 // become garbage; there is no reason to keep two identical strings
4930 // alive.
4931 return s;
4932 }
4933}
4934
4935
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004936static inline SeqAsciiString* TryGetSeqAsciiString(String* s) {
4937 if (!s->IsFlat() || !s->IsAsciiRepresentation()) return NULL;
4938 if (s->IsConsString()) {
4939 ASSERT(ConsString::cast(s)->second()->length() == 0);
4940 return SeqAsciiString::cast(ConsString::cast(s)->first());
4941 }
4942 return SeqAsciiString::cast(s);
4943}
4944
4945
4946namespace {
4947
4948struct ToLowerTraits {
4949 typedef unibrow::ToLowercase UnibrowConverter;
4950
4951 static bool ConvertAscii(char* dst, char* src, int length) {
4952 bool changed = false;
4953 for (int i = 0; i < length; ++i) {
4954 char c = src[i];
4955 if ('A' <= c && c <= 'Z') {
4956 c += ('a' - 'A');
4957 changed = true;
4958 }
4959 dst[i] = c;
4960 }
4961 return changed;
4962 }
4963};
4964
4965
4966struct ToUpperTraits {
4967 typedef unibrow::ToUppercase UnibrowConverter;
4968
4969 static bool ConvertAscii(char* dst, char* src, int length) {
4970 bool changed = false;
4971 for (int i = 0; i < length; ++i) {
4972 char c = src[i];
4973 if ('a' <= c && c <= 'z') {
4974 c -= ('a' - 'A');
4975 changed = true;
4976 }
4977 dst[i] = c;
4978 }
4979 return changed;
4980 }
4981};
4982
4983} // namespace
4984
4985
4986template <typename ConvertTraits>
4987static Object* ConvertCase(
4988 Arguments args,
4989 unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004990 NoHandleAllocation ha;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004991 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004992 s->TryFlatten();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004993
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004994 const int length = s->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004995 // Assume that the string is not empty; we need this assumption later
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004996 if (length == 0) return s;
4997
4998 // Simpler handling of ascii strings.
4999 //
5000 // NOTE: This assumes that the upper/lower case of an ascii
5001 // character is also ascii. This is currently the case, but it
5002 // might break in the future if we implement more context and locale
5003 // dependent upper/lower conversions.
5004 SeqAsciiString* seq_ascii = TryGetSeqAsciiString(s);
5005 if (seq_ascii != NULL) {
5006 Object* o = Heap::AllocateRawAsciiString(length);
5007 if (o->IsFailure()) return o;
5008 SeqAsciiString* result = SeqAsciiString::cast(o);
5009 bool has_changed_character = ConvertTraits::ConvertAscii(
5010 result->GetChars(), seq_ascii->GetChars(), length);
5011 return has_changed_character ? result : s;
5012 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005013
5014 Object* answer = ConvertCaseHelper(s, length, length, mapping);
5015 if (answer->IsSmi()) {
5016 // Retry with correct length.
5017 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
5018 }
5019 return answer; // This may be a failure.
5020}
5021
5022
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005023static Object* Runtime_StringToLowerCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005024 return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005025}
5026
5027
5028static Object* Runtime_StringToUpperCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005029 return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005030}
5031
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005032
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005033static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
5034 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
5035}
5036
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005037
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005038static Object* Runtime_StringTrim(Arguments args) {
5039 NoHandleAllocation ha;
5040 ASSERT(args.length() == 3);
5041
5042 CONVERT_CHECKED(String, s, args[0]);
5043 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
5044 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
5045
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005046 s->TryFlatten();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005047 int length = s->length();
5048
5049 int left = 0;
5050 if (trimLeft) {
5051 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
5052 left++;
5053 }
5054 }
5055
5056 int right = length;
5057 if (trimRight) {
5058 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
5059 right--;
5060 }
5061 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005062 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005063}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005064
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005065
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005066template <typename schar, typename pchar>
5067void FindStringIndices(Vector<const schar> subject,
5068 Vector<const pchar> pattern,
5069 ZoneList<int>* indices,
5070 unsigned int limit) {
5071 ASSERT(limit > 0);
5072 // Collect indices of pattern in subject, and the end-of-string index.
5073 // Stop after finding at most limit values.
5074 StringSearchStrategy strategy =
5075 InitializeStringSearch(pattern, sizeof(schar) == 1);
5076 switch (strategy) {
5077 case SEARCH_FAIL: return;
5078 case SEARCH_SHORT: {
5079 int pattern_length = pattern.length();
5080 int index = 0;
5081 while (limit > 0) {
5082 index = SimpleIndexOf(subject, pattern, index);
5083 if (index < 0) return;
5084 indices->Add(index);
5085 index += pattern_length;
5086 limit--;
5087 }
5088 return;
5089 }
5090 case SEARCH_LONG: {
5091 int pattern_length = pattern.length();
5092 int index = 0;
5093 while (limit > 0) {
5094 index = ComplexIndexOf(subject, pattern, index);
5095 if (index < 0) return;
5096 indices->Add(index);
5097 index += pattern_length;
5098 limit--;
5099 }
5100 return;
5101 }
5102 default:
5103 UNREACHABLE();
5104 return;
5105 }
5106}
5107
5108template <typename schar>
5109inline void FindCharIndices(Vector<const schar> subject,
5110 const schar pattern_char,
5111 ZoneList<int>* indices,
5112 unsigned int limit) {
5113 // Collect indices of pattern_char in subject, and the end-of-string index.
5114 // Stop after finding at most limit values.
5115 int index = 0;
5116 while (limit > 0) {
5117 index = SingleCharIndexOf(subject, pattern_char, index);
5118 if (index < 0) return;
5119 indices->Add(index);
5120 index++;
5121 limit--;
5122 }
5123}
5124
5125
5126static Object* Runtime_StringSplit(Arguments args) {
5127 ASSERT(args.length() == 3);
5128 HandleScope handle_scope;
5129 CONVERT_ARG_CHECKED(String, subject, 0);
5130 CONVERT_ARG_CHECKED(String, pattern, 1);
5131 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
5132
5133 int subject_length = subject->length();
5134 int pattern_length = pattern->length();
5135 RUNTIME_ASSERT(pattern_length > 0);
5136
5137 // The limit can be very large (0xffffffffu), but since the pattern
5138 // isn't empty, we can never create more parts than ~half the length
5139 // of the subject.
5140
5141 if (!subject->IsFlat()) FlattenString(subject);
5142
5143 static const int kMaxInitialListCapacity = 16;
5144
5145 ZoneScope scope(DELETE_ON_EXIT);
5146
5147 // Find (up to limit) indices of separator and end-of-string in subject
5148 int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
5149 ZoneList<int> indices(initial_capacity);
5150 if (pattern_length == 1) {
5151 // Special case, go directly to fast single-character split.
5152 AssertNoAllocation nogc;
5153 uc16 pattern_char = pattern->Get(0);
5154 if (subject->IsTwoByteRepresentation()) {
5155 FindCharIndices(subject->ToUC16Vector(), pattern_char,
5156 &indices,
5157 limit);
5158 } else if (pattern_char <= String::kMaxAsciiCharCode) {
5159 FindCharIndices(subject->ToAsciiVector(),
5160 static_cast<char>(pattern_char),
5161 &indices,
5162 limit);
5163 }
5164 } else {
5165 if (!pattern->IsFlat()) FlattenString(pattern);
5166 AssertNoAllocation nogc;
5167 if (subject->IsAsciiRepresentation()) {
5168 Vector<const char> subject_vector = subject->ToAsciiVector();
5169 if (pattern->IsAsciiRepresentation()) {
5170 FindStringIndices(subject_vector,
5171 pattern->ToAsciiVector(),
5172 &indices,
5173 limit);
5174 } else {
5175 FindStringIndices(subject_vector,
5176 pattern->ToUC16Vector(),
5177 &indices,
5178 limit);
5179 }
5180 } else {
5181 Vector<const uc16> subject_vector = subject->ToUC16Vector();
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 }
5194 }
5195 if (static_cast<uint32_t>(indices.length()) < limit) {
5196 indices.Add(subject_length);
5197 }
5198 // The list indices now contains the end of each part to create.
5199
5200
5201 // Create JSArray of substrings separated by separator.
5202 int part_count = indices.length();
5203
5204 Handle<JSArray> result = Factory::NewJSArray(part_count);
5205 result->set_length(Smi::FromInt(part_count));
5206
5207 ASSERT(result->HasFastElements());
5208
5209 if (part_count == 1 && indices.at(0) == subject_length) {
5210 FixedArray::cast(result->elements())->set(0, *subject);
5211 return *result;
5212 }
5213
5214 Handle<FixedArray> elements(FixedArray::cast(result->elements()));
5215 int part_start = 0;
5216 for (int i = 0; i < part_count; i++) {
5217 HandleScope local_loop_handle;
5218 int part_end = indices.at(i);
5219 Handle<String> substring =
5220 Factory::NewSubString(subject, part_start, part_end);
5221 elements->set(i, *substring);
5222 part_start = part_end + pattern_length;
5223 }
5224
5225 return *result;
5226}
5227
5228
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005229// Copies ascii characters to the given fixed array looking up
5230// one-char strings in the cache. Gives up on the first char that is
5231// not in the cache and fills the remainder with smi zeros. Returns
5232// the length of the successfully copied prefix.
5233static int CopyCachedAsciiCharsToArray(const char* chars,
5234 FixedArray* elements,
5235 int length) {
5236 AssertNoAllocation nogc;
5237 FixedArray* ascii_cache = Heap::single_character_string_cache();
5238 Object* undefined = Heap::undefined_value();
5239 int i;
5240 for (i = 0; i < length; ++i) {
5241 Object* value = ascii_cache->get(chars[i]);
5242 if (value == undefined) break;
5243 ASSERT(!Heap::InNewSpace(value));
5244 elements->set(i, value, SKIP_WRITE_BARRIER);
5245 }
5246 if (i < length) {
5247 ASSERT(Smi::FromInt(0) == 0);
5248 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
5249 }
5250#ifdef DEBUG
5251 for (int j = 0; j < length; ++j) {
5252 Object* element = elements->get(j);
5253 ASSERT(element == Smi::FromInt(0) ||
5254 (element->IsString() && String::cast(element)->LooksValid()));
5255 }
5256#endif
5257 return i;
5258}
5259
5260
5261// Converts a String to JSArray.
5262// For example, "foo" => ["f", "o", "o"].
5263static Object* Runtime_StringToArray(Arguments args) {
5264 HandleScope scope;
5265 ASSERT(args.length() == 1);
5266 CONVERT_ARG_CHECKED(String, s, 0);
5267
5268 s->TryFlatten();
5269 const int length = s->length();
5270
5271 Handle<FixedArray> elements;
5272 if (s->IsFlat() && s->IsAsciiRepresentation()) {
5273 Object* obj = Heap::AllocateUninitializedFixedArray(length);
5274 if (obj->IsFailure()) return obj;
5275 elements = Handle<FixedArray>(FixedArray::cast(obj));
5276
5277 Vector<const char> chars = s->ToAsciiVector();
5278 // Note, this will initialize all elements (not only the prefix)
5279 // to prevent GC from seeing partially initialized array.
5280 int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
5281 *elements,
5282 length);
5283
5284 for (int i = num_copied_from_cache; i < length; ++i) {
5285 elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
5286 }
5287 } else {
5288 elements = Factory::NewFixedArray(length);
5289 for (int i = 0; i < length; ++i) {
5290 elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
5291 }
5292 }
5293
5294#ifdef DEBUG
5295 for (int i = 0; i < length; ++i) {
5296 ASSERT(String::cast(elements->get(i))->length() == 1);
5297 }
5298#endif
5299
5300 return *Factory::NewJSArrayWithElements(elements);
5301}
5302
5303
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00005304bool Runtime::IsUpperCaseChar(uint16_t ch) {
5305 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
5306 int char_length = to_upper_mapping.get(ch, 0, chars);
5307 return char_length == 0;
5308}
5309
5310
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005311static Object* Runtime_NumberToString(Arguments args) {
5312 NoHandleAllocation ha;
5313 ASSERT(args.length() == 1);
5314
5315 Object* number = args[0];
5316 RUNTIME_ASSERT(number->IsNumber());
5317
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005318 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005319}
5320
5321
ager@chromium.org357bf652010-04-12 11:30:10 +00005322static Object* Runtime_NumberToStringSkipCache(Arguments args) {
5323 NoHandleAllocation ha;
5324 ASSERT(args.length() == 1);
5325
5326 Object* number = args[0];
5327 RUNTIME_ASSERT(number->IsNumber());
5328
5329 return Heap::NumberToString(number, false);
5330}
5331
5332
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005333static Object* Runtime_NumberToInteger(Arguments args) {
5334 NoHandleAllocation ha;
5335 ASSERT(args.length() == 1);
5336
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005337 CONVERT_DOUBLE_CHECKED(number, args[0]);
5338
5339 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5340 if (number > 0 && number <= Smi::kMaxValue) {
5341 return Smi::FromInt(static_cast<int>(number));
5342 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005343 return Heap::NumberFromDouble(DoubleToInteger(number));
5344}
5345
5346
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005347static Object* Runtime_NumberToIntegerMapMinusZero(Arguments args) {
5348 NoHandleAllocation ha;
5349 ASSERT(args.length() == 1);
5350
5351 CONVERT_DOUBLE_CHECKED(number, args[0]);
5352
5353 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5354 if (number > 0 && number <= Smi::kMaxValue) {
5355 return Smi::FromInt(static_cast<int>(number));
5356 }
5357
5358 double double_value = DoubleToInteger(number);
5359 // Map both -0 and +0 to +0.
5360 if (double_value == 0) double_value = 0;
5361
5362 return Heap::NumberFromDouble(double_value);
5363}
5364
5365
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005366static Object* Runtime_NumberToJSUint32(Arguments args) {
5367 NoHandleAllocation ha;
5368 ASSERT(args.length() == 1);
5369
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005370 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005371 return Heap::NumberFromUint32(number);
5372}
5373
5374
5375static Object* Runtime_NumberToJSInt32(Arguments args) {
5376 NoHandleAllocation ha;
5377 ASSERT(args.length() == 1);
5378
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005379 CONVERT_DOUBLE_CHECKED(number, args[0]);
5380
5381 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5382 if (number > 0 && number <= Smi::kMaxValue) {
5383 return Smi::FromInt(static_cast<int>(number));
5384 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005385 return Heap::NumberFromInt32(DoubleToInt32(number));
5386}
5387
5388
ager@chromium.org870a0b62008-11-04 11:43:05 +00005389// Converts a Number to a Smi, if possible. Returns NaN if the number is not
5390// a small integer.
5391static Object* Runtime_NumberToSmi(Arguments args) {
5392 NoHandleAllocation ha;
5393 ASSERT(args.length() == 1);
5394
5395 Object* obj = args[0];
5396 if (obj->IsSmi()) {
5397 return obj;
5398 }
5399 if (obj->IsHeapNumber()) {
5400 double value = HeapNumber::cast(obj)->value();
5401 int int_value = FastD2I(value);
5402 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
5403 return Smi::FromInt(int_value);
5404 }
5405 }
5406 return Heap::nan_value();
5407}
5408
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005409
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005410static Object* Runtime_NumberAdd(Arguments args) {
5411 NoHandleAllocation ha;
5412 ASSERT(args.length() == 2);
5413
5414 CONVERT_DOUBLE_CHECKED(x, args[0]);
5415 CONVERT_DOUBLE_CHECKED(y, args[1]);
5416 return Heap::AllocateHeapNumber(x + y);
5417}
5418
5419
5420static Object* Runtime_NumberSub(Arguments args) {
5421 NoHandleAllocation ha;
5422 ASSERT(args.length() == 2);
5423
5424 CONVERT_DOUBLE_CHECKED(x, args[0]);
5425 CONVERT_DOUBLE_CHECKED(y, args[1]);
5426 return Heap::AllocateHeapNumber(x - y);
5427}
5428
5429
5430static Object* Runtime_NumberMul(Arguments args) {
5431 NoHandleAllocation ha;
5432 ASSERT(args.length() == 2);
5433
5434 CONVERT_DOUBLE_CHECKED(x, args[0]);
5435 CONVERT_DOUBLE_CHECKED(y, args[1]);
5436 return Heap::AllocateHeapNumber(x * y);
5437}
5438
5439
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005440static Object* Runtime_NumberUnaryMinus(Arguments args) {
5441 NoHandleAllocation ha;
5442 ASSERT(args.length() == 1);
5443
5444 CONVERT_DOUBLE_CHECKED(x, args[0]);
5445 return Heap::AllocateHeapNumber(-x);
5446}
5447
5448
5449static Object* Runtime_NumberDiv(Arguments args) {
5450 NoHandleAllocation ha;
5451 ASSERT(args.length() == 2);
5452
5453 CONVERT_DOUBLE_CHECKED(x, args[0]);
5454 CONVERT_DOUBLE_CHECKED(y, args[1]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005455 return Heap::NumberFromDouble(x / y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005456}
5457
5458
5459static Object* Runtime_NumberMod(Arguments args) {
5460 NoHandleAllocation ha;
5461 ASSERT(args.length() == 2);
5462
5463 CONVERT_DOUBLE_CHECKED(x, args[0]);
5464 CONVERT_DOUBLE_CHECKED(y, args[1]);
5465
ager@chromium.org3811b432009-10-28 14:53:37 +00005466 x = modulo(x, y);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005467 // NumberFromDouble may return a Smi instead of a Number object
5468 return Heap::NumberFromDouble(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005469}
5470
5471
5472static Object* Runtime_StringAdd(Arguments args) {
5473 NoHandleAllocation ha;
5474 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005475 CONVERT_CHECKED(String, str1, args[0]);
5476 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005477 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00005478 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005479}
5480
5481
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005482template <typename sinkchar>
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005483static inline void StringBuilderConcatHelper(String* special,
5484 sinkchar* sink,
5485 FixedArray* fixed_array,
5486 int array_length) {
5487 int position = 0;
5488 for (int i = 0; i < array_length; i++) {
5489 Object* element = fixed_array->get(i);
5490 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005491 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005492 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005493 int pos;
5494 int len;
5495 if (encoded_slice > 0) {
5496 // Position and length encoded in one smi.
5497 pos = StringBuilderSubstringPosition::decode(encoded_slice);
5498 len = StringBuilderSubstringLength::decode(encoded_slice);
5499 } else {
5500 // Position and length encoded in two smis.
5501 Object* obj = fixed_array->get(++i);
5502 ASSERT(obj->IsSmi());
5503 pos = Smi::cast(obj)->value();
5504 len = -encoded_slice;
5505 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00005506 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00005507 sink + position,
5508 pos,
5509 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005510 position += len;
5511 } else {
5512 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005513 int element_length = string->length();
5514 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005515 position += element_length;
5516 }
5517 }
5518}
5519
5520
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005521static Object* Runtime_StringBuilderConcat(Arguments args) {
5522 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005523 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005524 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005525 if (!args[1]->IsSmi()) {
5526 Top::context()->mark_out_of_memory();
5527 return Failure::OutOfMemoryException();
5528 }
5529 int array_length = Smi::cast(args[1])->value();
5530 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005531
5532 // This assumption is used by the slice encoding in one or two smis.
5533 ASSERT(Smi::kMaxValue >= String::kMaxLength);
5534
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005535 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005536 if (!array->HasFastElements()) {
5537 return Top::Throw(Heap::illegal_argument_symbol());
5538 }
5539 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005540 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005541 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005542 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005543
5544 if (array_length == 0) {
5545 return Heap::empty_string();
5546 } else if (array_length == 1) {
5547 Object* first = fixed_array->get(0);
5548 if (first->IsString()) return first;
5549 }
5550
ager@chromium.org5ec48922009-05-05 07:25:34 +00005551 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005552 int position = 0;
5553 for (int i = 0; i < array_length; i++) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005554 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005555 Object* elt = fixed_array->get(i);
5556 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005557 // Smi encoding of position and length.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005558 int smi_value = Smi::cast(elt)->value();
5559 int pos;
5560 int len;
5561 if (smi_value > 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005562 // Position and length encoded in one smi.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005563 pos = StringBuilderSubstringPosition::decode(smi_value);
5564 len = StringBuilderSubstringLength::decode(smi_value);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005565 } else {
5566 // Position and length encoded in two smis.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005567 len = -smi_value;
5568 // Get the position and check that it is a positive smi.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005569 i++;
5570 if (i >= array_length) {
5571 return Top::Throw(Heap::illegal_argument_symbol());
5572 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005573 Object* next_smi = fixed_array->get(i);
5574 if (!next_smi->IsSmi()) {
5575 return Top::Throw(Heap::illegal_argument_symbol());
5576 }
5577 pos = Smi::cast(next_smi)->value();
5578 if (pos < 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005579 return Top::Throw(Heap::illegal_argument_symbol());
5580 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005581 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005582 ASSERT(pos >= 0);
5583 ASSERT(len >= 0);
5584 if (pos > special_length || len > special_length - pos) {
5585 return Top::Throw(Heap::illegal_argument_symbol());
5586 }
5587 increment = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005588 } else if (elt->IsString()) {
5589 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005590 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005591 increment = element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005592 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005593 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005594 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005595 } else {
5596 return Top::Throw(Heap::illegal_argument_symbol());
5597 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005598 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005599 Top::context()->mark_out_of_memory();
5600 return Failure::OutOfMemoryException();
5601 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005602 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005603 }
5604
5605 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005606 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005607
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005608 if (ascii) {
5609 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005610 if (object->IsFailure()) return object;
5611 SeqAsciiString* answer = SeqAsciiString::cast(object);
5612 StringBuilderConcatHelper(special,
5613 answer->GetChars(),
5614 fixed_array,
5615 array_length);
5616 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005617 } else {
5618 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005619 if (object->IsFailure()) return object;
5620 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
5621 StringBuilderConcatHelper(special,
5622 answer->GetChars(),
5623 fixed_array,
5624 array_length);
5625 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005626 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005627}
5628
5629
5630static Object* Runtime_NumberOr(Arguments args) {
5631 NoHandleAllocation ha;
5632 ASSERT(args.length() == 2);
5633
5634 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5635 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5636 return Heap::NumberFromInt32(x | y);
5637}
5638
5639
5640static Object* Runtime_NumberAnd(Arguments args) {
5641 NoHandleAllocation ha;
5642 ASSERT(args.length() == 2);
5643
5644 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5645 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5646 return Heap::NumberFromInt32(x & y);
5647}
5648
5649
5650static Object* Runtime_NumberXor(Arguments args) {
5651 NoHandleAllocation ha;
5652 ASSERT(args.length() == 2);
5653
5654 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5655 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5656 return Heap::NumberFromInt32(x ^ y);
5657}
5658
5659
5660static Object* Runtime_NumberNot(Arguments args) {
5661 NoHandleAllocation ha;
5662 ASSERT(args.length() == 1);
5663
5664 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5665 return Heap::NumberFromInt32(~x);
5666}
5667
5668
5669static Object* Runtime_NumberShl(Arguments args) {
5670 NoHandleAllocation ha;
5671 ASSERT(args.length() == 2);
5672
5673 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5674 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5675 return Heap::NumberFromInt32(x << (y & 0x1f));
5676}
5677
5678
5679static Object* Runtime_NumberShr(Arguments args) {
5680 NoHandleAllocation ha;
5681 ASSERT(args.length() == 2);
5682
5683 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
5684 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5685 return Heap::NumberFromUint32(x >> (y & 0x1f));
5686}
5687
5688
5689static Object* Runtime_NumberSar(Arguments args) {
5690 NoHandleAllocation ha;
5691 ASSERT(args.length() == 2);
5692
5693 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5694 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5695 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
5696}
5697
5698
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005699static Object* Runtime_NumberEquals(Arguments args) {
5700 NoHandleAllocation ha;
5701 ASSERT(args.length() == 2);
5702
5703 CONVERT_DOUBLE_CHECKED(x, args[0]);
5704 CONVERT_DOUBLE_CHECKED(y, args[1]);
5705 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
5706 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
5707 if (x == y) return Smi::FromInt(EQUAL);
5708 Object* result;
5709 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
5710 result = Smi::FromInt(EQUAL);
5711 } else {
5712 result = Smi::FromInt(NOT_EQUAL);
5713 }
5714 return result;
5715}
5716
5717
5718static Object* Runtime_StringEquals(Arguments args) {
5719 NoHandleAllocation ha;
5720 ASSERT(args.length() == 2);
5721
5722 CONVERT_CHECKED(String, x, args[0]);
5723 CONVERT_CHECKED(String, y, args[1]);
5724
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005725 bool not_equal = !x->Equals(y);
5726 // This is slightly convoluted because the value that signifies
5727 // equality is 0 and inequality is 1 so we have to negate the result
5728 // from String::Equals.
5729 ASSERT(not_equal == 0 || not_equal == 1);
5730 STATIC_CHECK(EQUAL == 0);
5731 STATIC_CHECK(NOT_EQUAL == 1);
5732 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005733}
5734
5735
5736static Object* Runtime_NumberCompare(Arguments args) {
5737 NoHandleAllocation ha;
5738 ASSERT(args.length() == 3);
5739
5740 CONVERT_DOUBLE_CHECKED(x, args[0]);
5741 CONVERT_DOUBLE_CHECKED(y, args[1]);
5742 if (isnan(x) || isnan(y)) return args[2];
5743 if (x == y) return Smi::FromInt(EQUAL);
5744 if (isless(x, y)) return Smi::FromInt(LESS);
5745 return Smi::FromInt(GREATER);
5746}
5747
5748
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005749// Compare two Smis as if they were converted to strings and then
5750// compared lexicographically.
5751static Object* Runtime_SmiLexicographicCompare(Arguments args) {
5752 NoHandleAllocation ha;
5753 ASSERT(args.length() == 2);
5754
5755 // Arrays for the individual characters of the two Smis. Smis are
5756 // 31 bit integers and 10 decimal digits are therefore enough.
5757 static int x_elms[10];
5758 static int y_elms[10];
5759
5760 // Extract the integer values from the Smis.
5761 CONVERT_CHECKED(Smi, x, args[0]);
5762 CONVERT_CHECKED(Smi, y, args[1]);
5763 int x_value = x->value();
5764 int y_value = y->value();
5765
5766 // If the integers are equal so are the string representations.
5767 if (x_value == y_value) return Smi::FromInt(EQUAL);
5768
5769 // If one of the integers are zero the normal integer order is the
5770 // same as the lexicographic order of the string representations.
5771 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
5772
ager@chromium.org32912102009-01-16 10:38:43 +00005773 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005774 // smallest because the char code of '-' is less than the char code
5775 // of any digit. Otherwise, we make both values positive.
5776 if (x_value < 0 || y_value < 0) {
5777 if (y_value >= 0) return Smi::FromInt(LESS);
5778 if (x_value >= 0) return Smi::FromInt(GREATER);
5779 x_value = -x_value;
5780 y_value = -y_value;
5781 }
5782
5783 // Convert the integers to arrays of their decimal digits.
5784 int x_index = 0;
5785 int y_index = 0;
5786 while (x_value > 0) {
5787 x_elms[x_index++] = x_value % 10;
5788 x_value /= 10;
5789 }
5790 while (y_value > 0) {
5791 y_elms[y_index++] = y_value % 10;
5792 y_value /= 10;
5793 }
5794
5795 // Loop through the arrays of decimal digits finding the first place
5796 // where they differ.
5797 while (--x_index >= 0 && --y_index >= 0) {
5798 int diff = x_elms[x_index] - y_elms[y_index];
5799 if (diff != 0) return Smi::FromInt(diff);
5800 }
5801
5802 // If one array is a suffix of the other array, the longest array is
5803 // the representation of the largest of the Smis in the
5804 // lexicographic ordering.
5805 return Smi::FromInt(x_index - y_index);
5806}
5807
5808
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005809static Object* StringInputBufferCompare(String* x, String* y) {
5810 static StringInputBuffer bufx;
5811 static StringInputBuffer bufy;
5812 bufx.Reset(x);
5813 bufy.Reset(y);
5814 while (bufx.has_more() && bufy.has_more()) {
5815 int d = bufx.GetNext() - bufy.GetNext();
5816 if (d < 0) return Smi::FromInt(LESS);
5817 else if (d > 0) return Smi::FromInt(GREATER);
5818 }
5819
5820 // x is (non-trivial) prefix of y:
5821 if (bufy.has_more()) return Smi::FromInt(LESS);
5822 // y is prefix of x:
5823 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
5824}
5825
5826
5827static Object* FlatStringCompare(String* x, String* y) {
5828 ASSERT(x->IsFlat());
5829 ASSERT(y->IsFlat());
5830 Object* equal_prefix_result = Smi::FromInt(EQUAL);
5831 int prefix_length = x->length();
5832 if (y->length() < prefix_length) {
5833 prefix_length = y->length();
5834 equal_prefix_result = Smi::FromInt(GREATER);
5835 } else if (y->length() > prefix_length) {
5836 equal_prefix_result = Smi::FromInt(LESS);
5837 }
5838 int r;
5839 if (x->IsAsciiRepresentation()) {
5840 Vector<const char> x_chars = x->ToAsciiVector();
5841 if (y->IsAsciiRepresentation()) {
5842 Vector<const char> y_chars = y->ToAsciiVector();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005843 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005844 } else {
5845 Vector<const uc16> y_chars = y->ToUC16Vector();
5846 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5847 }
5848 } else {
5849 Vector<const uc16> x_chars = x->ToUC16Vector();
5850 if (y->IsAsciiRepresentation()) {
5851 Vector<const char> y_chars = y->ToAsciiVector();
5852 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5853 } else {
5854 Vector<const uc16> y_chars = y->ToUC16Vector();
5855 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5856 }
5857 }
5858 Object* result;
5859 if (r == 0) {
5860 result = equal_prefix_result;
5861 } else {
5862 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
5863 }
5864 ASSERT(result == StringInputBufferCompare(x, y));
5865 return result;
5866}
5867
5868
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005869static Object* Runtime_StringCompare(Arguments args) {
5870 NoHandleAllocation ha;
5871 ASSERT(args.length() == 2);
5872
5873 CONVERT_CHECKED(String, x, args[0]);
5874 CONVERT_CHECKED(String, y, args[1]);
5875
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005876 Counters::string_compare_runtime.Increment();
5877
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005878 // A few fast case tests before we flatten.
5879 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005880 if (y->length() == 0) {
5881 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005882 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005883 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005884 return Smi::FromInt(LESS);
5885 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005886
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005887 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005888 if (d < 0) return Smi::FromInt(LESS);
5889 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005890
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005891 Object* obj = Heap::PrepareForCompare(x);
5892 if (obj->IsFailure()) return obj;
5893 obj = Heap::PrepareForCompare(y);
5894 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005895
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005896 return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
5897 : StringInputBufferCompare(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005898}
5899
5900
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005901static Object* Runtime_Math_acos(Arguments args) {
5902 NoHandleAllocation ha;
5903 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005904 Counters::math_acos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005905
5906 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005907 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005908}
5909
5910
5911static Object* Runtime_Math_asin(Arguments args) {
5912 NoHandleAllocation ha;
5913 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005914 Counters::math_asin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005915
5916 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005917 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005918}
5919
5920
5921static Object* Runtime_Math_atan(Arguments args) {
5922 NoHandleAllocation ha;
5923 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005924 Counters::math_atan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005925
5926 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005927 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005928}
5929
5930
5931static Object* Runtime_Math_atan2(Arguments args) {
5932 NoHandleAllocation ha;
5933 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005934 Counters::math_atan2.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005935
5936 CONVERT_DOUBLE_CHECKED(x, args[0]);
5937 CONVERT_DOUBLE_CHECKED(y, args[1]);
5938 double result;
5939 if (isinf(x) && isinf(y)) {
5940 // Make sure that the result in case of two infinite arguments
5941 // is a multiple of Pi / 4. The sign of the result is determined
5942 // by the first argument (x) and the sign of the second argument
5943 // determines the multiplier: one or three.
5944 static double kPiDividedBy4 = 0.78539816339744830962;
5945 int multiplier = (x < 0) ? -1 : 1;
5946 if (y < 0) multiplier *= 3;
5947 result = multiplier * kPiDividedBy4;
5948 } else {
5949 result = atan2(x, y);
5950 }
5951 return Heap::AllocateHeapNumber(result);
5952}
5953
5954
5955static Object* Runtime_Math_ceil(Arguments args) {
5956 NoHandleAllocation ha;
5957 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005958 Counters::math_ceil.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005959
5960 CONVERT_DOUBLE_CHECKED(x, args[0]);
5961 return Heap::NumberFromDouble(ceiling(x));
5962}
5963
5964
5965static Object* Runtime_Math_cos(Arguments args) {
5966 NoHandleAllocation ha;
5967 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005968 Counters::math_cos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005969
5970 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005971 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005972}
5973
5974
5975static Object* Runtime_Math_exp(Arguments args) {
5976 NoHandleAllocation ha;
5977 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005978 Counters::math_exp.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005979
5980 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005981 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005982}
5983
5984
5985static Object* Runtime_Math_floor(Arguments args) {
5986 NoHandleAllocation ha;
5987 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005988 Counters::math_floor.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005989
5990 CONVERT_DOUBLE_CHECKED(x, args[0]);
5991 return Heap::NumberFromDouble(floor(x));
5992}
5993
5994
5995static Object* Runtime_Math_log(Arguments args) {
5996 NoHandleAllocation ha;
5997 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005998 Counters::math_log.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005999
6000 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006001 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006002}
6003
6004
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006005// Helper function to compute x^y, where y is known to be an
6006// integer. Uses binary decomposition to limit the number of
6007// multiplications; see the discussion in "Hacker's Delight" by Henry
6008// S. Warren, Jr., figure 11-6, page 213.
6009static double powi(double x, int y) {
6010 ASSERT(y != kMinInt);
6011 unsigned n = (y < 0) ? -y : y;
6012 double m = x;
6013 double p = 1;
6014 while (true) {
6015 if ((n & 1) != 0) p *= m;
6016 n >>= 1;
6017 if (n == 0) {
6018 if (y < 0) {
6019 // Unfortunately, we have to be careful when p has reached
6020 // infinity in the computation, because sometimes the higher
6021 // internal precision in the pow() implementation would have
6022 // given us a finite p. This happens very rarely.
6023 double result = 1.0 / p;
6024 return (result == 0 && isinf(p))
6025 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
6026 : result;
6027 } else {
6028 return p;
6029 }
6030 }
6031 m *= m;
6032 }
6033}
6034
6035
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006036static Object* Runtime_Math_pow(Arguments args) {
6037 NoHandleAllocation ha;
6038 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006039 Counters::math_pow.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006040
6041 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006042
6043 // If the second argument is a smi, it is much faster to call the
6044 // custom powi() function than the generic pow().
6045 if (args[1]->IsSmi()) {
6046 int y = Smi::cast(args[1])->value();
6047 return Heap::AllocateHeapNumber(powi(x, y));
6048 }
6049
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006050 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00006051
6052 if (!isinf(x)) {
6053 if (y == 0.5) {
6054 // It's not uncommon to use Math.pow(x, 0.5) to compute the
6055 // square root of a number. To speed up such computations, we
6056 // explictly check for this case and use the sqrt() function
6057 // which is faster than pow().
6058 return Heap::AllocateHeapNumber(sqrt(x));
6059 } else if (y == -0.5) {
6060 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
6061 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
6062 }
6063 }
6064
6065 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006066 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006067 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6068 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006069 } else {
6070 return Heap::AllocateHeapNumber(pow(x, y));
6071 }
6072}
6073
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006074// Fast version of Math.pow if we know that y is not an integer and
6075// y is not -0.5 or 0.5. Used as slowcase from codegen.
6076static Object* Runtime_Math_pow_cfunction(Arguments args) {
6077 NoHandleAllocation ha;
6078 ASSERT(args.length() == 2);
6079 CONVERT_DOUBLE_CHECKED(x, args[0]);
6080 CONVERT_DOUBLE_CHECKED(y, args[1]);
6081 if (y == 0) {
6082 return Smi::FromInt(1);
6083 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6084 return Heap::nan_value();
6085 } else {
6086 return Heap::AllocateHeapNumber(pow(x, y));
6087 }
6088}
6089
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006090
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006091static Object* Runtime_RoundNumber(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006092 NoHandleAllocation ha;
6093 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006094 Counters::math_round.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006095
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006096 if (!args[0]->IsHeapNumber()) {
6097 // Must be smi. Return the argument unchanged for all the other types
6098 // to make fuzz-natives test happy.
6099 return args[0];
6100 }
6101
6102 HeapNumber* number = reinterpret_cast<HeapNumber*>(args[0]);
6103
6104 double value = number->value();
6105 int exponent = number->get_exponent();
6106 int sign = number->get_sign();
6107
6108 // We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
6109 // should be rounded to 2^30, which is not smi.
6110 if (!sign && exponent <= kSmiValueSize - 3) {
6111 return Smi::FromInt(static_cast<int>(value + 0.5));
6112 }
6113
6114 // If the magnitude is big enough, there's no place for fraction part. If we
6115 // try to add 0.5 to this number, 1.0 will be added instead.
6116 if (exponent >= 52) {
6117 return number;
6118 }
6119
6120 if (sign && value >= -0.5) return Heap::minus_zero_value();
6121
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006122 // Do not call NumberFromDouble() to avoid extra checks.
6123 return Heap::AllocateHeapNumber(floor(value + 0.5));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006124}
6125
6126
6127static Object* Runtime_Math_sin(Arguments args) {
6128 NoHandleAllocation ha;
6129 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006130 Counters::math_sin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006131
6132 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006133 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006134}
6135
6136
6137static Object* Runtime_Math_sqrt(Arguments args) {
6138 NoHandleAllocation ha;
6139 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006140 Counters::math_sqrt.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006141
6142 CONVERT_DOUBLE_CHECKED(x, args[0]);
6143 return Heap::AllocateHeapNumber(sqrt(x));
6144}
6145
6146
6147static Object* Runtime_Math_tan(Arguments args) {
6148 NoHandleAllocation ha;
6149 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006150 Counters::math_tan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006151
6152 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006153 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006154}
6155
6156
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006157static int MakeDay(int year, int month, int day) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006158 static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
6159 181, 212, 243, 273, 304, 334};
6160 static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
6161 182, 213, 244, 274, 305, 335};
6162
6163 year += month / 12;
6164 month %= 12;
6165 if (month < 0) {
6166 year--;
6167 month += 12;
6168 }
6169
6170 ASSERT(month >= 0);
6171 ASSERT(month < 12);
6172
6173 // year_delta is an arbitrary number such that:
6174 // a) year_delta = -1 (mod 400)
6175 // b) year + year_delta > 0 for years in the range defined by
6176 // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
6177 // Jan 1 1970. This is required so that we don't run into integer
6178 // division of negative numbers.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006179 // c) there shouldn't be an overflow for 32-bit integers in the following
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006180 // operations.
6181 static const int year_delta = 399999;
6182 static const int base_day = 365 * (1970 + year_delta) +
6183 (1970 + year_delta) / 4 -
6184 (1970 + year_delta) / 100 +
6185 (1970 + year_delta) / 400;
6186
6187 int year1 = year + year_delta;
6188 int day_from_year = 365 * year1 +
6189 year1 / 4 -
6190 year1 / 100 +
6191 year1 / 400 -
6192 base_day;
6193
6194 if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006195 return day_from_year + day_from_month[month] + day - 1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006196 }
6197
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006198 return day_from_year + day_from_month_leap[month] + day - 1;
6199}
6200
6201
6202static Object* Runtime_DateMakeDay(Arguments args) {
6203 NoHandleAllocation ha;
6204 ASSERT(args.length() == 3);
6205
6206 CONVERT_SMI_CHECKED(year, args[0]);
6207 CONVERT_SMI_CHECKED(month, args[1]);
6208 CONVERT_SMI_CHECKED(date, args[2]);
6209
6210 return Smi::FromInt(MakeDay(year, month, date));
6211}
6212
6213
6214static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
6215static const int kDaysIn4Years = 4 * 365 + 1;
6216static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
6217static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
6218static const int kDays1970to2000 = 30 * 365 + 7;
6219static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
6220 kDays1970to2000;
6221static const int kYearsOffset = 400000;
6222
6223static const char kDayInYear[] = {
6224 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6225 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6226 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6227 22, 23, 24, 25, 26, 27, 28,
6228 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6229 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6230 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6231 22, 23, 24, 25, 26, 27, 28, 29, 30,
6232 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6233 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6234 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6235 22, 23, 24, 25, 26, 27, 28, 29, 30,
6236 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6237 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6238 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6239 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6240 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6241 22, 23, 24, 25, 26, 27, 28, 29, 30,
6242 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6243 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6244 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6245 22, 23, 24, 25, 26, 27, 28, 29, 30,
6246 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6247 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6248
6249 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6250 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6251 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6252 22, 23, 24, 25, 26, 27, 28,
6253 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6254 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6255 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6256 22, 23, 24, 25, 26, 27, 28, 29, 30,
6257 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6258 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6259 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6260 22, 23, 24, 25, 26, 27, 28, 29, 30,
6261 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6262 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6263 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6264 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6265 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6266 22, 23, 24, 25, 26, 27, 28, 29, 30,
6267 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6268 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6269 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,
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, 31,
6273
6274 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6275 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6276 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6277 22, 23, 24, 25, 26, 27, 28, 29,
6278 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6279 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6280 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6281 22, 23, 24, 25, 26, 27, 28, 29, 30,
6282 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6283 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6284 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6285 22, 23, 24, 25, 26, 27, 28, 29, 30,
6286 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6287 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6288 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6289 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
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,
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, 30, 31,
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,
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, 31,
6298
6299 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6300 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6301 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6302 22, 23, 24, 25, 26, 27, 28,
6303 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6304 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6305 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6306 22, 23, 24, 25, 26, 27, 28, 29, 30,
6307 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6308 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6309 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6310 22, 23, 24, 25, 26, 27, 28, 29, 30,
6311 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6312 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6313 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6314 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
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,
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, 29, 30, 31,
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,
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, 31};
6323
6324static const char kMonthInYear[] = {
6325 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,
6326 0, 0, 0, 0, 0, 0,
6327 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,
6328 1, 1, 1,
6329 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,
6330 2, 2, 2, 2, 2, 2,
6331 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,
6332 3, 3, 3, 3, 3,
6333 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,
6334 4, 4, 4, 4, 4, 4,
6335 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,
6336 5, 5, 5, 5, 5,
6337 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,
6338 6, 6, 6, 6, 6, 6,
6339 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,
6340 7, 7, 7, 7, 7, 7,
6341 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,
6342 8, 8, 8, 8, 8,
6343 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,
6344 9, 9, 9, 9, 9, 9,
6345 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6346 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6347 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6348 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6349
6350 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,
6351 0, 0, 0, 0, 0, 0,
6352 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,
6353 1, 1, 1,
6354 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,
6355 2, 2, 2, 2, 2, 2,
6356 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,
6357 3, 3, 3, 3, 3,
6358 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,
6359 4, 4, 4, 4, 4, 4,
6360 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,
6361 5, 5, 5, 5, 5,
6362 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,
6363 6, 6, 6, 6, 6, 6,
6364 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,
6365 7, 7, 7, 7, 7, 7,
6366 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,
6367 8, 8, 8, 8, 8,
6368 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,
6369 9, 9, 9, 9, 9, 9,
6370 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6371 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6372 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6373 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6374
6375 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,
6376 0, 0, 0, 0, 0, 0,
6377 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,
6378 1, 1, 1, 1,
6379 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,
6380 2, 2, 2, 2, 2, 2,
6381 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,
6382 3, 3, 3, 3, 3,
6383 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,
6384 4, 4, 4, 4, 4, 4,
6385 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,
6386 5, 5, 5, 5, 5,
6387 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,
6388 6, 6, 6, 6, 6, 6,
6389 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,
6390 7, 7, 7, 7, 7, 7,
6391 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,
6392 8, 8, 8, 8, 8,
6393 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,
6394 9, 9, 9, 9, 9, 9,
6395 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6396 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6397 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6398 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6399
6400 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,
6401 0, 0, 0, 0, 0, 0,
6402 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,
6403 1, 1, 1,
6404 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,
6405 2, 2, 2, 2, 2, 2,
6406 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,
6407 3, 3, 3, 3, 3,
6408 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,
6409 4, 4, 4, 4, 4, 4,
6410 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,
6411 5, 5, 5, 5, 5,
6412 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,
6413 6, 6, 6, 6, 6, 6,
6414 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,
6415 7, 7, 7, 7, 7, 7,
6416 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,
6417 8, 8, 8, 8, 8,
6418 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,
6419 9, 9, 9, 9, 9, 9,
6420 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6421 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6422 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6423 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
6424
6425
6426// This function works for dates from 1970 to 2099.
6427static inline void DateYMDFromTimeAfter1970(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006428 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006429#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006430 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006431#endif
6432
6433 year = 1970 + (4 * date + 2) / kDaysIn4Years;
6434 date %= kDaysIn4Years;
6435
6436 month = kMonthInYear[date];
6437 day = kDayInYear[date];
6438
6439 ASSERT(MakeDay(year, month, day) == save_date);
6440}
6441
6442
6443static inline void DateYMDFromTimeSlow(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 date += kDaysOffset;
6450 year = 400 * (date / kDaysIn400Years) - kYearsOffset;
6451 date %= kDaysIn400Years;
6452
6453 ASSERT(MakeDay(year, 0, 1) + date == save_date);
6454
6455 date--;
6456 int yd1 = date / kDaysIn100Years;
6457 date %= kDaysIn100Years;
6458 year += 100 * yd1;
6459
6460 date++;
6461 int yd2 = date / kDaysIn4Years;
6462 date %= kDaysIn4Years;
6463 year += 4 * yd2;
6464
6465 date--;
6466 int yd3 = date / 365;
6467 date %= 365;
6468 year += yd3;
6469
6470 bool is_leap = (!yd1 || yd2) && !yd3;
6471
6472 ASSERT(date >= -1);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006473 ASSERT(is_leap || (date >= 0));
6474 ASSERT((date < 365) || (is_leap && (date < 366)));
6475 ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0))));
6476 ASSERT(is_leap || ((MakeDay(year, 0, 1) + date) == save_date));
6477 ASSERT(!is_leap || ((MakeDay(year, 0, 1) + date + 1) == save_date));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006478
6479 if (is_leap) {
6480 day = kDayInYear[2*365 + 1 + date];
6481 month = kMonthInYear[2*365 + 1 + date];
6482 } else {
6483 day = kDayInYear[date];
6484 month = kMonthInYear[date];
6485 }
6486
6487 ASSERT(MakeDay(year, month, day) == save_date);
6488}
6489
6490
6491static inline void DateYMDFromTime(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006492 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006493 if (date >= 0 && date < 32 * kDaysIn4Years) {
6494 DateYMDFromTimeAfter1970(date, year, month, day);
6495 } else {
6496 DateYMDFromTimeSlow(date, year, month, day);
6497 }
6498}
6499
6500
6501static Object* Runtime_DateYMDFromTime(Arguments args) {
6502 NoHandleAllocation ha;
6503 ASSERT(args.length() == 2);
6504
6505 CONVERT_DOUBLE_CHECKED(t, args[0]);
6506 CONVERT_CHECKED(JSArray, res_array, args[1]);
6507
6508 int year, month, day;
6509 DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
6510
6511 res_array->SetElement(0, Smi::FromInt(year));
6512 res_array->SetElement(1, Smi::FromInt(month));
6513 res_array->SetElement(2, Smi::FromInt(day));
6514
6515 return Heap::undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006516}
6517
6518
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006519static Object* Runtime_NewArgumentsFast(Arguments args) {
6520 NoHandleAllocation ha;
6521 ASSERT(args.length() == 3);
6522
6523 JSFunction* callee = JSFunction::cast(args[0]);
6524 Object** parameters = reinterpret_cast<Object**>(args[1]);
6525 const int length = Smi::cast(args[2])->value();
6526
6527 Object* result = Heap::AllocateArgumentsObject(callee, length);
6528 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006529 // Allocate the elements if needed.
6530 if (length > 0) {
6531 // Allocate the fixed array.
6532 Object* obj = Heap::AllocateRawFixedArray(length);
6533 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006534
6535 AssertNoAllocation no_gc;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006536 FixedArray* array = reinterpret_cast<FixedArray*>(obj);
6537 array->set_map(Heap::fixed_array_map());
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006538 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006539
6540 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006541 for (int i = 0; i < length; i++) {
6542 array->set(i, *--parameters, mode);
6543 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006544 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006545 }
6546 return result;
6547}
6548
6549
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006550static Object* Runtime_NewClosure(Arguments args) {
6551 HandleScope scope;
6552 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00006553 CONVERT_ARG_CHECKED(Context, context, 0);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006554 CONVERT_ARG_CHECKED(SharedFunctionInfo, shared, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006555
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00006556 PretenureFlag pretenure = (context->global_context() == *context)
6557 ? TENURED // Allocate global closures in old space.
6558 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006559 Handle<JSFunction> result =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006560 Factory::NewFunctionFromSharedFunctionInfo(shared, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006561 return *result;
6562}
6563
6564
ager@chromium.org5c838252010-02-19 08:53:10 +00006565static Code* ComputeConstructStub(Handle<JSFunction> function) {
6566 Handle<Object> prototype = Factory::null_value();
6567 if (function->has_instance_prototype()) {
6568 prototype = Handle<Object>(function->instance_prototype());
6569 }
6570 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006571 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00006572 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006573 if (code->IsFailure()) {
6574 return Builtins::builtin(Builtins::JSConstructStubGeneric);
6575 }
6576 return Code::cast(code);
6577 }
6578
ager@chromium.org5c838252010-02-19 08:53:10 +00006579 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006580}
6581
6582
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006583static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006584 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006585 ASSERT(args.length() == 1);
6586
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006587 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006588
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006589 // If the constructor isn't a proper function we throw a type error.
6590 if (!constructor->IsJSFunction()) {
6591 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6592 Handle<Object> type_error =
6593 Factory::NewTypeError("not_constructor", arguments);
6594 return Top::Throw(*type_error);
6595 }
6596
6597 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006598
6599 // If function should not have prototype, construction is not allowed. In this
6600 // case generated code bailouts here, since function has no initial_map.
6601 if (!function->should_have_prototype()) {
6602 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6603 Handle<Object> type_error =
6604 Factory::NewTypeError("not_constructor", arguments);
6605 return Top::Throw(*type_error);
6606 }
6607
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006608#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006609 // Handle stepping into constructors if step into is active.
6610 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006611 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006612 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006613#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006614
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006615 if (function->has_initial_map()) {
6616 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006617 // The 'Function' function ignores the receiver object when
6618 // called using 'new' and creates a new JSFunction object that
6619 // is returned. The receiver object is only used for error
6620 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006621 // JSFunction. Factory::NewJSObject() should not be used to
6622 // allocate JSFunctions since it does not properly initialize
6623 // the shared part of the function. Since the receiver is
6624 // ignored anyway, we use the global object as the receiver
6625 // instead of a new JSFunction object. This way, errors are
6626 // reported the same way whether or not 'Function' is called
6627 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006628 return Top::context()->global();
6629 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006630 }
6631
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006632 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006633 Handle<SharedFunctionInfo> shared(function->shared());
6634 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006635
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006636 bool first_allocation = !function->has_initial_map();
6637 Handle<JSObject> result = Factory::NewJSObject(function);
6638 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006639 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00006640 ComputeConstructStub(Handle<JSFunction>(function)));
6641 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006642 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006643
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006644 Counters::constructed_objects.Increment();
6645 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006646
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006647 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006648}
6649
6650
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006651static Object* Runtime_LazyCompile(Arguments args) {
6652 HandleScope scope;
6653 ASSERT(args.length() == 1);
6654
6655 Handle<JSFunction> function = args.at<JSFunction>(0);
6656#ifdef DEBUG
6657 if (FLAG_trace_lazy) {
6658 PrintF("[lazy: ");
6659 function->shared()->name()->Print();
6660 PrintF("]\n");
6661 }
6662#endif
6663
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006664 // Compile the target function. Here we compile using CompileLazyInLoop in
6665 // order to get the optimized version. This helps code like delta-blue
6666 // that calls performance-critical routines through constructors. A
6667 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
6668 // direct call. Since the in-loop tracking takes place through CallICs
6669 // this means that things called through constructors are never known to
6670 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006671 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006672 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006673 return Failure::Exception();
6674 }
6675
6676 return function->code();
6677}
6678
6679
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006680static Object* Runtime_GetFunctionDelegate(Arguments args) {
6681 HandleScope scope;
6682 ASSERT(args.length() == 1);
6683 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6684 return *Execution::GetFunctionDelegate(args.at<Object>(0));
6685}
6686
6687
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00006688static Object* Runtime_GetConstructorDelegate(Arguments args) {
6689 HandleScope scope;
6690 ASSERT(args.length() == 1);
6691 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6692 return *Execution::GetConstructorDelegate(args.at<Object>(0));
6693}
6694
6695
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006696static Object* Runtime_NewContext(Arguments args) {
6697 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00006698 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006699
kasper.lund7276f142008-07-30 08:49:36 +00006700 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006701 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
6702 Object* result = Heap::AllocateFunctionContext(length, function);
6703 if (result->IsFailure()) return result;
6704
6705 Top::set_context(Context::cast(result));
6706
kasper.lund7276f142008-07-30 08:49:36 +00006707 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006708}
6709
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006710static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006711 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006712 Object* js_object = object;
6713 if (!js_object->IsJSObject()) {
6714 js_object = js_object->ToObject();
6715 if (js_object->IsFailure()) {
6716 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006717 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006718 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006719 Handle<Object> result =
6720 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
6721 return Top::Throw(*result);
6722 }
6723 }
6724
6725 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006726 Heap::AllocateWithContext(Top::context(),
6727 JSObject::cast(js_object),
6728 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006729 if (result->IsFailure()) return result;
6730
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006731 Context* context = Context::cast(result);
6732 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006733
kasper.lund7276f142008-07-30 08:49:36 +00006734 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006735}
6736
6737
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006738static Object* Runtime_PushContext(Arguments args) {
6739 NoHandleAllocation ha;
6740 ASSERT(args.length() == 1);
6741 return PushContextHelper(args[0], false);
6742}
6743
6744
6745static Object* Runtime_PushCatchContext(Arguments args) {
6746 NoHandleAllocation ha;
6747 ASSERT(args.length() == 1);
6748 return PushContextHelper(args[0], true);
6749}
6750
6751
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006752static Object* Runtime_LookupContext(Arguments args) {
6753 HandleScope scope;
6754 ASSERT(args.length() == 2);
6755
6756 CONVERT_ARG_CHECKED(Context, context, 0);
6757 CONVERT_ARG_CHECKED(String, name, 1);
6758
6759 int index;
6760 PropertyAttributes attributes;
6761 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006762 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006763 context->Lookup(name, flags, &index, &attributes);
6764
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006765 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006766 ASSERT(holder->IsJSObject());
6767 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006768 }
6769
6770 // No intermediate context found. Use global object by default.
6771 return Top::context()->global();
6772}
6773
6774
ager@chromium.orga1645e22009-09-09 19:27:10 +00006775// A mechanism to return a pair of Object pointers in registers (if possible).
6776// How this is achieved is calling convention-dependent.
6777// All currently supported x86 compiles uses calling conventions that are cdecl
6778// variants where a 64-bit value is returned in two 32-bit registers
6779// (edx:eax on ia32, r1:r0 on ARM).
6780// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
6781// In Win64 calling convention, a struct of two pointers is returned in memory,
6782// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006783#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006784struct ObjectPair {
6785 Object* x;
6786 Object* y;
6787};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006788
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006789static inline ObjectPair MakePair(Object* x, Object* y) {
6790 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006791 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
6792 // In Win64 they are assigned to a hidden first argument.
6793 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006794}
6795#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006796typedef uint64_t ObjectPair;
6797static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006798 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006799 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006800}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006801#endif
6802
6803
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006804static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006805 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
6806 USE(attributes);
6807 return x->IsTheHole() ? Heap::undefined_value() : x;
6808}
6809
6810
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006811static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
6812 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006813 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006814 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006815 JSFunction* context_extension_function =
6816 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006817 // If the holder isn't a context extension object, we just return it
6818 // as the receiver. This allows arguments objects to be used as
6819 // receivers, but only if they are put in the context scope chain
6820 // explicitly via a with-statement.
6821 Object* constructor = holder->map()->constructor();
6822 if (constructor != context_extension_function) return holder;
6823 // Fall back to using the global object as the receiver if the
6824 // property turns out to be a local variable allocated in a context
6825 // extension object - introduced via eval.
6826 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006827}
6828
6829
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006830static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006831 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00006832 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006833
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006834 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00006835 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006836 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006837 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006838 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006839
6840 int index;
6841 PropertyAttributes attributes;
6842 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006843 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006844 context->Lookup(name, flags, &index, &attributes);
6845
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006846 // If the index is non-negative, the slot has been found in a local
6847 // variable or a parameter. Read it from the context object or the
6848 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006849 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006850 // If the "property" we were looking for is a local variable or an
6851 // argument in a context, the receiver is the global object; see
6852 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
6853 JSObject* receiver = Top::context()->global()->global_receiver();
6854 Object* value = (holder->IsContext())
6855 ? Context::cast(*holder)->get(index)
6856 : JSObject::cast(*holder)->GetElement(index);
6857 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006858 }
6859
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006860 // If the holder is found, we read the property from it.
6861 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006862 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006863 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006864 JSObject* receiver;
6865 if (object->IsGlobalObject()) {
6866 receiver = GlobalObject::cast(object)->global_receiver();
6867 } else if (context->is_exception_holder(*holder)) {
6868 receiver = Top::context()->global()->global_receiver();
6869 } else {
6870 receiver = ComputeReceiverForNonGlobal(object);
6871 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006872 // No need to unhole the value here. This is taken care of by the
6873 // GetProperty function.
6874 Object* value = object->GetProperty(*name);
6875 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006876 }
6877
6878 if (throw_error) {
6879 // The property doesn't exist - throw exception.
6880 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006881 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006882 return MakePair(Top::Throw(*reference_error), NULL);
6883 } else {
6884 // The property doesn't exist - return undefined
6885 return MakePair(Heap::undefined_value(), Heap::undefined_value());
6886 }
6887}
6888
6889
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006890static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006891 return LoadContextSlotHelper(args, true);
6892}
6893
6894
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006895static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006896 return LoadContextSlotHelper(args, false);
6897}
6898
6899
6900static Object* Runtime_StoreContextSlot(Arguments args) {
6901 HandleScope scope;
6902 ASSERT(args.length() == 3);
6903
6904 Handle<Object> value(args[0]);
6905 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006906 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006907
6908 int index;
6909 PropertyAttributes attributes;
6910 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006911 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006912 context->Lookup(name, flags, &index, &attributes);
6913
6914 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006915 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006916 // Ignore if read_only variable.
6917 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006918 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006919 }
6920 } else {
6921 ASSERT((attributes & READ_ONLY) == 0);
6922 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006923 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006924 USE(result);
6925 ASSERT(!result->IsFailure());
6926 }
6927 return *value;
6928 }
6929
6930 // Slow case: The property is not in a FixedArray context.
6931 // It is either in an JSObject extension context or it was not found.
6932 Handle<JSObject> context_ext;
6933
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006934 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006935 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006936 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006937 } else {
6938 // The property was not found. It needs to be stored in the global context.
6939 ASSERT(attributes == ABSENT);
6940 attributes = NONE;
6941 context_ext = Handle<JSObject>(Top::context()->global());
6942 }
6943
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006944 // Set the property, but ignore if read_only variable on the context
6945 // extension object itself.
6946 if ((attributes & READ_ONLY) == 0 ||
6947 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006948 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
6949 if (set.is_null()) {
6950 // Failure::Exception is converted to a null handle in the
6951 // handle-based methods such as SetProperty. We therefore need
6952 // to convert null handles back to exceptions.
6953 ASSERT(Top::has_pending_exception());
6954 return Failure::Exception();
6955 }
6956 }
6957 return *value;
6958}
6959
6960
6961static Object* Runtime_Throw(Arguments args) {
6962 HandleScope scope;
6963 ASSERT(args.length() == 1);
6964
6965 return Top::Throw(args[0]);
6966}
6967
6968
6969static Object* Runtime_ReThrow(Arguments args) {
6970 HandleScope scope;
6971 ASSERT(args.length() == 1);
6972
6973 return Top::ReThrow(args[0]);
6974}
6975
6976
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006977static Object* Runtime_PromoteScheduledException(Arguments args) {
6978 ASSERT_EQ(0, args.length());
6979 return Top::PromoteScheduledException();
6980}
6981
6982
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006983static Object* Runtime_ThrowReferenceError(Arguments args) {
6984 HandleScope scope;
6985 ASSERT(args.length() == 1);
6986
6987 Handle<Object> name(args[0]);
6988 Handle<Object> reference_error =
6989 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
6990 return Top::Throw(*reference_error);
6991}
6992
6993
6994static Object* Runtime_StackOverflow(Arguments args) {
6995 NoHandleAllocation na;
6996 return Top::StackOverflow();
6997}
6998
6999
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007000static Object* Runtime_StackGuard(Arguments args) {
7001 ASSERT(args.length() == 1);
7002
7003 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007004 if (StackGuard::IsStackOverflow()) {
7005 return Runtime_StackOverflow(args);
7006 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007007
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007008 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007009}
7010
7011
7012// NOTE: These PrintXXX functions are defined for all builds (not just
7013// DEBUG builds) because we may want to be able to trace function
7014// calls in all modes.
7015static void PrintString(String* str) {
7016 // not uncommon to have empty strings
7017 if (str->length() > 0) {
7018 SmartPointer<char> s =
7019 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
7020 PrintF("%s", *s);
7021 }
7022}
7023
7024
7025static void PrintObject(Object* obj) {
7026 if (obj->IsSmi()) {
7027 PrintF("%d", Smi::cast(obj)->value());
7028 } else if (obj->IsString() || obj->IsSymbol()) {
7029 PrintString(String::cast(obj));
7030 } else if (obj->IsNumber()) {
7031 PrintF("%g", obj->Number());
7032 } else if (obj->IsFailure()) {
7033 PrintF("<failure>");
7034 } else if (obj->IsUndefined()) {
7035 PrintF("<undefined>");
7036 } else if (obj->IsNull()) {
7037 PrintF("<null>");
7038 } else if (obj->IsTrue()) {
7039 PrintF("<true>");
7040 } else if (obj->IsFalse()) {
7041 PrintF("<false>");
7042 } else {
7043 PrintF("%p", obj);
7044 }
7045}
7046
7047
7048static int StackSize() {
7049 int n = 0;
7050 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
7051 return n;
7052}
7053
7054
7055static void PrintTransition(Object* result) {
7056 // indentation
7057 { const int nmax = 80;
7058 int n = StackSize();
7059 if (n <= nmax)
7060 PrintF("%4d:%*s", n, n, "");
7061 else
7062 PrintF("%4d:%*s", n, nmax, "...");
7063 }
7064
7065 if (result == NULL) {
7066 // constructor calls
7067 JavaScriptFrameIterator it;
7068 JavaScriptFrame* frame = it.frame();
7069 if (frame->IsConstructor()) PrintF("new ");
7070 // function name
7071 Object* fun = frame->function();
7072 if (fun->IsJSFunction()) {
7073 PrintObject(JSFunction::cast(fun)->shared()->name());
7074 } else {
7075 PrintObject(fun);
7076 }
7077 // function arguments
7078 // (we are intentionally only printing the actually
7079 // supplied parameters, not all parameters required)
7080 PrintF("(this=");
7081 PrintObject(frame->receiver());
7082 const int length = frame->GetProvidedParametersCount();
7083 for (int i = 0; i < length; i++) {
7084 PrintF(", ");
7085 PrintObject(frame->GetParameter(i));
7086 }
7087 PrintF(") {\n");
7088
7089 } else {
7090 // function result
7091 PrintF("} -> ");
7092 PrintObject(result);
7093 PrintF("\n");
7094 }
7095}
7096
7097
7098static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007099 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007100 NoHandleAllocation ha;
7101 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007102 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007103}
7104
7105
7106static Object* Runtime_TraceExit(Arguments args) {
7107 NoHandleAllocation ha;
7108 PrintTransition(args[0]);
7109 return args[0]; // return TOS
7110}
7111
7112
7113static Object* Runtime_DebugPrint(Arguments args) {
7114 NoHandleAllocation ha;
7115 ASSERT(args.length() == 1);
7116
7117#ifdef DEBUG
7118 if (args[0]->IsString()) {
7119 // If we have a string, assume it's a code "marker"
7120 // and print some interesting cpu debugging info.
7121 JavaScriptFrameIterator it;
7122 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007123 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
7124 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007125 } else {
7126 PrintF("DebugPrint: ");
7127 }
7128 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007129 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007130 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007131 HeapObject::cast(args[0])->map()->Print();
7132 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007133#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007134 // ShortPrint is available in release mode. Print is not.
7135 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007136#endif
7137 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00007138 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007139
7140 return args[0]; // return TOS
7141}
7142
7143
7144static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007145 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007146 NoHandleAllocation ha;
7147 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007148 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007149}
7150
7151
mads.s.ager31e71382008-08-13 09:32:07 +00007152static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007153 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007154 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007155
7156 // According to ECMA-262, section 15.9.1, page 117, the precision of
7157 // the number in a Date object representing a particular instant in
7158 // time is milliseconds. Therefore, we floor the result of getting
7159 // the OS time.
7160 double millis = floor(OS::TimeCurrentMillis());
7161 return Heap::NumberFromDouble(millis);
7162}
7163
7164
7165static Object* Runtime_DateParseString(Arguments args) {
7166 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007167 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007168
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007169 CONVERT_ARG_CHECKED(String, str, 0);
7170 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007171
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007172 CONVERT_ARG_CHECKED(JSArray, output, 1);
7173 RUNTIME_ASSERT(output->HasFastElements());
7174
7175 AssertNoAllocation no_allocation;
7176
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007177 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007178 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
7179 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007180 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007181 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007182 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007183 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007184 result = DateParser::Parse(str->ToUC16Vector(), output_array);
7185 }
7186
7187 if (result) {
7188 return *output;
7189 } else {
7190 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007191 }
7192}
7193
7194
7195static Object* Runtime_DateLocalTimezone(Arguments args) {
7196 NoHandleAllocation ha;
7197 ASSERT(args.length() == 1);
7198
7199 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007200 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007201 return Heap::AllocateStringFromUtf8(CStrVector(zone));
7202}
7203
7204
7205static Object* Runtime_DateLocalTimeOffset(Arguments args) {
7206 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007207 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007208
7209 return Heap::NumberFromDouble(OS::LocalTimeOffset());
7210}
7211
7212
7213static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
7214 NoHandleAllocation ha;
7215 ASSERT(args.length() == 1);
7216
7217 CONVERT_DOUBLE_CHECKED(x, args[0]);
7218 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
7219}
7220
7221
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007222static Object* Runtime_GlobalReceiver(Arguments args) {
7223 ASSERT(args.length() == 1);
7224 Object* global = args[0];
7225 if (!global->IsJSGlobalObject()) return Heap::null_value();
7226 return JSGlobalObject::cast(global)->global_receiver();
7227}
7228
7229
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007230static Object* Runtime_CompileString(Arguments args) {
7231 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007232 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007233 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007234 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007235
ager@chromium.org381abbb2009-02-25 13:23:22 +00007236 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007237 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007238 Compiler::ValidationState validate = (is_json->IsTrue())
7239 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007240 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
7241 context,
7242 true,
7243 validate);
7244 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007245 Handle<JSFunction> fun =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007246 Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007247 return *fun;
7248}
7249
7250
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007251static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
7252 ASSERT(args.length() == 3);
7253 if (!args[0]->IsJSFunction()) {
7254 return MakePair(Top::ThrowIllegalOperation(), NULL);
7255 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007256
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007257 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007258 Handle<JSFunction> callee = args.at<JSFunction>(0);
7259 Handle<Object> receiver; // Will be overwritten.
7260
7261 // Compute the calling context.
7262 Handle<Context> context = Handle<Context>(Top::context());
7263#ifdef DEBUG
7264 // Make sure Top::context() agrees with the old code that traversed
7265 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007266 StackFrameLocator locator;
7267 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007268 ASSERT(Context::cast(frame->context()) == *context);
7269#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007270
7271 // Find where the 'eval' symbol is bound. It is unaliased only if
7272 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007273 int index = -1;
7274 PropertyAttributes attributes = ABSENT;
7275 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007276 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
7277 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007278 // Stop search when eval is found or when the global context is
7279 // reached.
7280 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007281 if (context->is_function_context()) {
7282 context = Handle<Context>(Context::cast(context->closure()->context()));
7283 } else {
7284 context = Handle<Context>(context->previous());
7285 }
7286 }
7287
iposva@chromium.org245aa852009-02-10 00:49:54 +00007288 // If eval could not be resolved, it has been deleted and we need to
7289 // throw a reference error.
7290 if (attributes == ABSENT) {
7291 Handle<Object> name = Factory::eval_symbol();
7292 Handle<Object> reference_error =
7293 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007294 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007295 }
7296
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007297 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007298 // 'eval' is not bound in the global context. Just call the function
7299 // with the given arguments. This is not necessarily the global eval.
7300 if (receiver->IsContext()) {
7301 context = Handle<Context>::cast(receiver);
7302 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007303 } else if (receiver->IsJSContextExtensionObject()) {
7304 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007305 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007306 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007307 }
7308
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007309 // 'eval' is bound in the global context, but it may have been overwritten.
7310 // Compare it to the builtin 'GlobalEval' function to make sure.
7311 if (*callee != Top::global_context()->global_eval_fun() ||
7312 !args[1]->IsString()) {
7313 return MakePair(*callee, Top::context()->global()->global_receiver());
7314 }
7315
7316 // Deal with a normal eval call with a string argument. Compile it
7317 // and return the compiled function bound in the local context.
7318 Handle<String> source = args.at<String>(1);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007319 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007320 source,
7321 Handle<Context>(Top::context()),
7322 Top::context()->IsGlobalContext(),
7323 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007324 if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
7325 callee = Factory::NewFunctionFromSharedFunctionInfo(
7326 shared,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007327 Handle<Context>(Top::context()),
7328 NOT_TENURED);
7329 return MakePair(*callee, args[2]);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007330}
7331
7332
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007333static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
7334 // This utility adjusts the property attributes for newly created Function
7335 // object ("new Function(...)") by changing the map.
7336 // All it does is changing the prototype property to enumerable
7337 // as specified in ECMA262, 15.3.5.2.
7338 HandleScope scope;
7339 ASSERT(args.length() == 1);
7340 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7341 ASSERT(func->map()->instance_type() ==
7342 Top::function_instance_map()->instance_type());
7343 ASSERT(func->map()->instance_size() ==
7344 Top::function_instance_map()->instance_size());
7345 func->set_map(*Top::function_instance_map());
7346 return *func;
7347}
7348
7349
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007350// Push an array unto an array of arrays if it is not already in the
7351// array. Returns true if the element was pushed on the stack and
7352// false otherwise.
7353static Object* Runtime_PushIfAbsent(Arguments args) {
7354 ASSERT(args.length() == 2);
7355 CONVERT_CHECKED(JSArray, array, args[0]);
7356 CONVERT_CHECKED(JSArray, element, args[1]);
7357 RUNTIME_ASSERT(array->HasFastElements());
7358 int length = Smi::cast(array->length())->value();
7359 FixedArray* elements = FixedArray::cast(array->elements());
7360 for (int i = 0; i < length; i++) {
7361 if (elements->get(i) == element) return Heap::false_value();
7362 }
7363 Object* obj = array->SetFastElement(length, element);
7364 if (obj->IsFailure()) return obj;
7365 return Heap::true_value();
7366}
7367
7368
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007369/**
7370 * A simple visitor visits every element of Array's.
7371 * The backend storage can be a fixed array for fast elements case,
7372 * or a dictionary for sparse array. Since Dictionary is a subtype
7373 * of FixedArray, the class can be used by both fast and slow cases.
7374 * The second parameter of the constructor, fast_elements, specifies
7375 * whether the storage is a FixedArray or Dictionary.
7376 *
7377 * An index limit is used to deal with the situation that a result array
7378 * length overflows 32-bit non-negative integer.
7379 */
7380class ArrayConcatVisitor {
7381 public:
7382 ArrayConcatVisitor(Handle<FixedArray> storage,
7383 uint32_t index_limit,
7384 bool fast_elements) :
7385 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007386 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007387
7388 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007389 if (i >= index_limit_ - index_offset_) return;
7390 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007391
7392 if (fast_elements_) {
7393 ASSERT(index < static_cast<uint32_t>(storage_->length()));
7394 storage_->set(index, *elm);
7395
7396 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007397 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
7398 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007399 Factory::DictionaryAtNumberPut(dict, index, elm);
7400 if (!result.is_identical_to(dict))
7401 storage_ = result;
7402 }
7403 }
7404
7405 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007406 if (index_limit_ - index_offset_ < delta) {
7407 index_offset_ = index_limit_;
7408 } else {
7409 index_offset_ += delta;
7410 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007411 }
7412
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007413 Handle<FixedArray> storage() { return storage_; }
7414
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007415 private:
7416 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007417 // Limit on the accepted indices. Elements with indices larger than the
7418 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007419 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007420 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007421 uint32_t index_offset_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007422 bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007423};
7424
7425
ager@chromium.org3811b432009-10-28 14:53:37 +00007426template<class ExternalArrayClass, class ElementType>
7427static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
7428 bool elements_are_ints,
7429 bool elements_are_guaranteed_smis,
7430 uint32_t range,
7431 ArrayConcatVisitor* visitor) {
7432 Handle<ExternalArrayClass> array(
7433 ExternalArrayClass::cast(receiver->elements()));
7434 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
7435
7436 if (visitor != NULL) {
7437 if (elements_are_ints) {
7438 if (elements_are_guaranteed_smis) {
7439 for (uint32_t j = 0; j < len; j++) {
7440 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
7441 visitor->visit(j, e);
7442 }
7443 } else {
7444 for (uint32_t j = 0; j < len; j++) {
7445 int64_t val = static_cast<int64_t>(array->get(j));
7446 if (Smi::IsValid(static_cast<intptr_t>(val))) {
7447 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
7448 visitor->visit(j, e);
7449 } else {
7450 Handle<Object> e(
7451 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
7452 visitor->visit(j, e);
7453 }
7454 }
7455 }
7456 } else {
7457 for (uint32_t j = 0; j < len; j++) {
7458 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
7459 visitor->visit(j, e);
7460 }
7461 }
7462 }
7463
7464 return len;
7465}
7466
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007467/**
7468 * A helper function that visits elements of a JSObject. Only elements
7469 * whose index between 0 and range (exclusive) are visited.
7470 *
7471 * If the third parameter, visitor, is not NULL, the visitor is called
7472 * with parameters, 'visitor_index_offset + element index' and the element.
7473 *
7474 * It returns the number of visisted elements.
7475 */
7476static uint32_t IterateElements(Handle<JSObject> receiver,
7477 uint32_t range,
7478 ArrayConcatVisitor* visitor) {
7479 uint32_t num_of_elements = 0;
7480
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007481 switch (receiver->GetElementsKind()) {
7482 case JSObject::FAST_ELEMENTS: {
7483 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
7484 uint32_t len = elements->length();
7485 if (range < len) {
7486 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007487 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007488
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007489 for (uint32_t j = 0; j < len; j++) {
7490 Handle<Object> e(elements->get(j));
7491 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007492 num_of_elements++;
7493 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007494 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007495 }
7496 }
7497 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007498 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007499 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007500 case JSObject::PIXEL_ELEMENTS: {
7501 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
7502 uint32_t len = pixels->length();
7503 if (range < len) {
7504 len = range;
7505 }
7506
7507 for (uint32_t j = 0; j < len; j++) {
7508 num_of_elements++;
7509 if (visitor != NULL) {
7510 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
7511 visitor->visit(j, e);
7512 }
7513 }
7514 break;
7515 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007516 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
7517 num_of_elements =
7518 IterateExternalArrayElements<ExternalByteArray, int8_t>(
7519 receiver, true, true, range, visitor);
7520 break;
7521 }
7522 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7523 num_of_elements =
7524 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
7525 receiver, true, true, range, visitor);
7526 break;
7527 }
7528 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
7529 num_of_elements =
7530 IterateExternalArrayElements<ExternalShortArray, int16_t>(
7531 receiver, true, true, range, visitor);
7532 break;
7533 }
7534 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7535 num_of_elements =
7536 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
7537 receiver, true, true, range, visitor);
7538 break;
7539 }
7540 case JSObject::EXTERNAL_INT_ELEMENTS: {
7541 num_of_elements =
7542 IterateExternalArrayElements<ExternalIntArray, int32_t>(
7543 receiver, true, false, range, visitor);
7544 break;
7545 }
7546 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7547 num_of_elements =
7548 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
7549 receiver, true, false, range, visitor);
7550 break;
7551 }
7552 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
7553 num_of_elements =
7554 IterateExternalArrayElements<ExternalFloatArray, float>(
7555 receiver, false, false, range, visitor);
7556 break;
7557 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007558 case JSObject::DICTIONARY_ELEMENTS: {
7559 Handle<NumberDictionary> dict(receiver->element_dictionary());
7560 uint32_t capacity = dict->Capacity();
7561 for (uint32_t j = 0; j < capacity; j++) {
7562 Handle<Object> k(dict->KeyAt(j));
7563 if (dict->IsKey(*k)) {
7564 ASSERT(k->IsNumber());
7565 uint32_t index = static_cast<uint32_t>(k->Number());
7566 if (index < range) {
7567 num_of_elements++;
7568 if (visitor) {
7569 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
7570 }
7571 }
7572 }
7573 }
7574 break;
7575 }
7576 default:
7577 UNREACHABLE();
7578 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007579 }
7580
7581 return num_of_elements;
7582}
7583
7584
7585/**
7586 * A helper function that visits elements of an Array object, and elements
7587 * on its prototypes.
7588 *
7589 * Elements on prototypes are visited first, and only elements whose indices
7590 * less than Array length are visited.
7591 *
7592 * If a ArrayConcatVisitor object is given, the visitor is called with
7593 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007594 *
7595 * The returned number of elements is an upper bound on the actual number
7596 * of elements added. If the same element occurs in more than one object
7597 * in the array's prototype chain, it will be counted more than once, but
7598 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007599 */
7600static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
7601 ArrayConcatVisitor* visitor) {
7602 uint32_t range = static_cast<uint32_t>(array->length()->Number());
7603 Handle<Object> obj = array;
7604
7605 static const int kEstimatedPrototypes = 3;
7606 List< Handle<JSObject> > objects(kEstimatedPrototypes);
7607
7608 // Visit prototype first. If an element on the prototype is shadowed by
7609 // the inheritor using the same index, the ArrayConcatVisitor visits
7610 // the prototype element before the shadowing element.
7611 // The visitor can simply overwrite the old value by new value using
7612 // the same index. This follows Array::concat semantics.
7613 while (!obj->IsNull()) {
7614 objects.Add(Handle<JSObject>::cast(obj));
7615 obj = Handle<Object>(obj->GetPrototype());
7616 }
7617
7618 uint32_t nof_elements = 0;
7619 for (int i = objects.length() - 1; i >= 0; i--) {
7620 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007621 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007622 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007623
7624 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
7625 nof_elements = JSObject::kMaxElementCount;
7626 } else {
7627 nof_elements += encountered_elements;
7628 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007629 }
7630
7631 return nof_elements;
7632}
7633
7634
7635/**
7636 * A helper function of Runtime_ArrayConcat.
7637 *
7638 * The first argument is an Array of arrays and objects. It is the
7639 * same as the arguments array of Array::concat JS function.
7640 *
7641 * If an argument is an Array object, the function visits array
7642 * elements. If an argument is not an Array object, the function
7643 * visits the object as if it is an one-element array.
7644 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007645 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007646 * non-negative number is used as new length. For example, if one
7647 * array length is 2^32 - 1, second array length is 1, the
7648 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007649 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
7650 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007651 */
7652static uint32_t IterateArguments(Handle<JSArray> arguments,
7653 ArrayConcatVisitor* visitor) {
7654 uint32_t visited_elements = 0;
7655 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7656
7657 for (uint32_t i = 0; i < num_of_args; i++) {
7658 Handle<Object> obj(arguments->GetElement(i));
7659 if (obj->IsJSArray()) {
7660 Handle<JSArray> array = Handle<JSArray>::cast(obj);
7661 uint32_t len = static_cast<uint32_t>(array->length()->Number());
7662 uint32_t nof_elements =
7663 IterateArrayAndPrototypeElements(array, visitor);
7664 // Total elements of array and its prototype chain can be more than
7665 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007666 // the array length number of elements. We use the length as an estimate
7667 // for the actual number of elements added.
7668 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
7669 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
7670 visited_elements = JSArray::kMaxElementCount;
7671 } else {
7672 visited_elements += added_elements;
7673 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007674 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007675 } else {
7676 if (visitor) {
7677 visitor->visit(0, obj);
7678 visitor->increase_index_offset(1);
7679 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007680 if (visited_elements < JSArray::kMaxElementCount) {
7681 visited_elements++;
7682 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007683 }
7684 }
7685 return visited_elements;
7686}
7687
7688
7689/**
7690 * Array::concat implementation.
7691 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007692 * TODO(lrn): Fix non-compliance for very large concatenations and update to
7693 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007694 */
7695static Object* Runtime_ArrayConcat(Arguments args) {
7696 ASSERT(args.length() == 1);
7697 HandleScope handle_scope;
7698
7699 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
7700 Handle<JSArray> arguments(arg_arrays);
7701
7702 // Pass 1: estimate the number of elements of the result
7703 // (it could be more than real numbers if prototype has elements).
7704 uint32_t result_length = 0;
7705 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7706
7707 { AssertNoAllocation nogc;
7708 for (uint32_t i = 0; i < num_of_args; i++) {
7709 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007710 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007711 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007712 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007713 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
7714 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007715 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007716 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007717 if (JSObject::kMaxElementCount - result_length < length_estimate) {
7718 result_length = JSObject::kMaxElementCount;
7719 break;
7720 }
7721 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007722 }
7723 }
7724
7725 // Allocate an empty array, will set length and content later.
7726 Handle<JSArray> result = Factory::NewJSArray(0);
7727
7728 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
7729 // If estimated number of elements is more than half of length, a
7730 // fixed array (fast case) is more time and space-efficient than a
7731 // dictionary.
7732 bool fast_case = (estimate_nof_elements * 2) >= result_length;
7733
7734 Handle<FixedArray> storage;
7735 if (fast_case) {
7736 // The backing storage array must have non-existing elements to
7737 // preserve holes across concat operations.
7738 storage = Factory::NewFixedArrayWithHoles(result_length);
7739
7740 } else {
7741 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
7742 uint32_t at_least_space_for = estimate_nof_elements +
7743 (estimate_nof_elements >> 2);
7744 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007745 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007746 }
7747
7748 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
7749
7750 ArrayConcatVisitor visitor(storage, result_length, fast_case);
7751
7752 IterateArguments(arguments, &visitor);
7753
7754 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007755 // Please note the storage might have changed in the visitor.
7756 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007757
7758 return *result;
7759}
7760
7761
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007762// This will not allocate (flatten the string), but it may run
7763// very slowly for very deeply nested ConsStrings. For debugging use only.
7764static Object* Runtime_GlobalPrint(Arguments args) {
7765 NoHandleAllocation ha;
7766 ASSERT(args.length() == 1);
7767
7768 CONVERT_CHECKED(String, string, args[0]);
7769 StringInputBuffer buffer(string);
7770 while (buffer.has_more()) {
7771 uint16_t character = buffer.GetNext();
7772 PrintF("%c", character);
7773 }
7774 return string;
7775}
7776
ager@chromium.org5ec48922009-05-05 07:25:34 +00007777// Moves all own elements of an object, that are below a limit, to positions
7778// starting at zero. All undefined values are placed after non-undefined values,
7779// and are followed by non-existing element. Does not change the length
7780// property.
7781// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007782static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007783 ASSERT(args.length() == 2);
7784 CONVERT_CHECKED(JSObject, object, args[0]);
7785 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
7786 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007787}
7788
7789
7790// Move contents of argument 0 (an array) to argument 1 (an array)
7791static Object* Runtime_MoveArrayContents(Arguments args) {
7792 ASSERT(args.length() == 2);
7793 CONVERT_CHECKED(JSArray, from, args[0]);
7794 CONVERT_CHECKED(JSArray, to, args[1]);
7795 to->SetContent(FixedArray::cast(from->elements()));
7796 to->set_length(from->length());
7797 from->SetContent(Heap::empty_fixed_array());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007798 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007799 return to;
7800}
7801
7802
7803// How many elements does this array have?
7804static Object* Runtime_EstimateNumberOfElements(Arguments args) {
7805 ASSERT(args.length() == 1);
7806 CONVERT_CHECKED(JSArray, array, args[0]);
7807 HeapObject* elements = array->elements();
7808 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007809 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007810 } else {
7811 return array->length();
7812 }
7813}
7814
7815
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007816static Object* Runtime_SwapElements(Arguments args) {
7817 HandleScope handle_scope;
7818
7819 ASSERT_EQ(3, args.length());
7820
ager@chromium.orgac091b72010-05-05 07:34:42 +00007821 CONVERT_ARG_CHECKED(JSObject, object, 0);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007822 Handle<Object> key1 = args.at<Object>(1);
7823 Handle<Object> key2 = args.at<Object>(2);
7824
7825 uint32_t index1, index2;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007826 if (!key1->ToArrayIndex(&index1)
7827 || !key2->ToArrayIndex(&index2)) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00007828 return Top::ThrowIllegalOperation();
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007829 }
7830
ager@chromium.orgac091b72010-05-05 07:34:42 +00007831 Handle<JSObject> jsobject = Handle<JSObject>::cast(object);
7832 Handle<Object> tmp1 = GetElement(jsobject, index1);
7833 Handle<Object> tmp2 = GetElement(jsobject, index2);
7834
7835 SetElement(jsobject, index1, tmp2);
7836 SetElement(jsobject, index2, tmp1);
7837
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007838 return Heap::undefined_value();
7839}
7840
7841
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007842// Returns an array that tells you where in the [0, length) interval an array
7843// might have elements. Can either return keys or intervals. Keys can have
7844// gaps in (undefined). Intervals can also span over some undefined keys.
7845static Object* Runtime_GetArrayKeys(Arguments args) {
7846 ASSERT(args.length() == 2);
7847 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007848 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007849 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007850 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007851 // Create an array and get all the keys into it, then remove all the
7852 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007853 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007854 int keys_length = keys->length();
7855 for (int i = 0; i < keys_length; i++) {
7856 Object* key = keys->get(i);
7857 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007858 if (!key->ToArrayIndex(&index) || index >= length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007859 // Zap invalid keys.
7860 keys->set_undefined(i);
7861 }
7862 }
7863 return *Factory::NewJSArrayWithElements(keys);
7864 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007865 ASSERT(array->HasFastElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007866 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
7867 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007868 single_interval->set(0, Smi::FromInt(-1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007869 uint32_t actual_length =
7870 static_cast<uint32_t>(FixedArray::cast(array->elements())->length());
ager@chromium.org5ec48922009-05-05 07:25:34 +00007871 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007872 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00007873 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007874 single_interval->set(1, *length_object);
7875 return *Factory::NewJSArrayWithElements(single_interval);
7876 }
7877}
7878
7879
7880// DefineAccessor takes an optional final argument which is the
7881// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
7882// to the way accessors are implemented, it is set for both the getter
7883// and setter on the first call to DefineAccessor and ignored on
7884// subsequent calls.
7885static Object* Runtime_DefineAccessor(Arguments args) {
7886 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
7887 // Compute attributes.
7888 PropertyAttributes attributes = NONE;
7889 if (args.length() == 5) {
7890 CONVERT_CHECKED(Smi, attrs, args[4]);
7891 int value = attrs->value();
7892 // Only attribute bits should be set.
7893 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
7894 attributes = static_cast<PropertyAttributes>(value);
7895 }
7896
7897 CONVERT_CHECKED(JSObject, obj, args[0]);
7898 CONVERT_CHECKED(String, name, args[1]);
7899 CONVERT_CHECKED(Smi, flag, args[2]);
7900 CONVERT_CHECKED(JSFunction, fun, args[3]);
7901 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
7902}
7903
7904
7905static Object* Runtime_LookupAccessor(Arguments args) {
7906 ASSERT(args.length() == 3);
7907 CONVERT_CHECKED(JSObject, obj, args[0]);
7908 CONVERT_CHECKED(String, name, args[1]);
7909 CONVERT_CHECKED(Smi, flag, args[2]);
7910 return obj->LookupAccessor(name, flag->value() == 0);
7911}
7912
7913
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007914#ifdef ENABLE_DEBUGGER_SUPPORT
7915static Object* Runtime_DebugBreak(Arguments args) {
7916 ASSERT(args.length() == 0);
7917 return Execution::DebugBreakHelper();
7918}
7919
7920
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007921// Helper functions for wrapping and unwrapping stack frame ids.
7922static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007923 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007924 return Smi::FromInt(id >> 2);
7925}
7926
7927
7928static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
7929 return static_cast<StackFrame::Id>(wrapped->value() << 2);
7930}
7931
7932
7933// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00007934// args[0]: debug event listener function to set or null or undefined for
7935// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007936// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00007937static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007938 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007939 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
7940 args[0]->IsUndefined() ||
7941 args[0]->IsNull());
7942 Handle<Object> callback = args.at<Object>(0);
7943 Handle<Object> data = args.at<Object>(1);
7944 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007945
7946 return Heap::undefined_value();
7947}
7948
7949
7950static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007951 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007952 StackGuard::DebugBreak();
7953 return Heap::undefined_value();
7954}
7955
7956
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007957static Object* DebugLookupResultValue(Object* receiver, String* name,
7958 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00007959 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007960 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007961 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007962 case NORMAL:
7963 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007964 if (value->IsTheHole()) {
7965 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007966 }
7967 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007968 case FIELD:
7969 value =
7970 JSObject::cast(
7971 result->holder())->FastPropertyAt(result->GetFieldIndex());
7972 if (value->IsTheHole()) {
7973 return Heap::undefined_value();
7974 }
7975 return value;
7976 case CONSTANT_FUNCTION:
7977 return result->GetConstantFunction();
7978 case CALLBACKS: {
7979 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007980 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00007981 value = receiver->GetPropertyWithCallback(
7982 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00007983 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007984 value = Top::pending_exception();
7985 Top::clear_pending_exception();
7986 if (caught_exception != NULL) {
7987 *caught_exception = true;
7988 }
7989 }
7990 return value;
7991 } else {
7992 return Heap::undefined_value();
7993 }
7994 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007995 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007996 case MAP_TRANSITION:
7997 case CONSTANT_TRANSITION:
7998 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007999 return Heap::undefined_value();
8000 default:
8001 UNREACHABLE();
8002 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008003 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008004 return Heap::undefined_value();
8005}
8006
8007
ager@chromium.org32912102009-01-16 10:38:43 +00008008// Get debugger related details for an object property.
8009// args[0]: object holding property
8010// args[1]: name of the property
8011//
8012// The array returned contains the following information:
8013// 0: Property value
8014// 1: Property details
8015// 2: Property value is exception
8016// 3: Getter function if defined
8017// 4: Setter function if defined
8018// Items 2-4 are only filled if the property has either a getter or a setter
8019// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00008020static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008021 HandleScope scope;
8022
8023 ASSERT(args.length() == 2);
8024
8025 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8026 CONVERT_ARG_CHECKED(String, name, 1);
8027
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008028 // Make sure to set the current context to the context before the debugger was
8029 // entered (if the debugger is entered). The reason for switching context here
8030 // is that for some property lookups (accessors and interceptors) callbacks
8031 // into the embedding application can occour, and the embedding application
8032 // could have the assumption that its own global context is the current
8033 // context and not some internal debugger context.
8034 SaveContext save;
8035 if (Debug::InDebugger()) {
8036 Top::set_context(*Debug::debugger_entry()->GetContext());
8037 }
8038
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008039 // Skip the global proxy as it has no properties and always delegates to the
8040 // real global object.
8041 if (obj->IsJSGlobalProxy()) {
8042 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
8043 }
8044
8045
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008046 // Check if the name is trivially convertible to an index and get the element
8047 // if so.
8048 uint32_t index;
8049 if (name->AsArrayIndex(&index)) {
8050 Handle<FixedArray> details = Factory::NewFixedArray(2);
8051 details->set(0, Runtime::GetElementOrCharAt(obj, index));
8052 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
8053 return *Factory::NewJSArrayWithElements(details);
8054 }
8055
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008056 // Find the number of objects making up this.
8057 int length = LocalPrototypeChainLength(*obj);
8058
8059 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008060 Handle<JSObject> jsproto = obj;
8061 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008062 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008063 jsproto->LocalLookup(*name, &result);
8064 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008065 // LookupResult is not GC safe as it holds raw object pointers.
8066 // GC can happen later in this code so put the required fields into
8067 // local variables using handles when required for later use.
8068 PropertyType result_type = result.type();
8069 Handle<Object> result_callback_obj;
8070 if (result_type == CALLBACKS) {
8071 result_callback_obj = Handle<Object>(result.GetCallbackObject());
8072 }
8073 Smi* property_details = result.GetPropertyDetails().AsSmi();
8074 // DebugLookupResultValue can cause GC so details from LookupResult needs
8075 // to be copied to handles before this.
8076 bool caught_exception = false;
8077 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
8078 &caught_exception);
8079 if (raw_value->IsFailure()) return raw_value;
8080 Handle<Object> value(raw_value);
8081
8082 // If the callback object is a fixed array then it contains JavaScript
8083 // getter and/or setter.
8084 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
8085 result_callback_obj->IsFixedArray();
8086 Handle<FixedArray> details =
8087 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
8088 details->set(0, *value);
8089 details->set(1, property_details);
8090 if (hasJavaScriptAccessors) {
8091 details->set(2,
8092 caught_exception ? Heap::true_value()
8093 : Heap::false_value());
8094 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
8095 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
8096 }
8097
8098 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008099 }
8100 if (i < length - 1) {
8101 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
8102 }
8103 }
8104
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008105 return Heap::undefined_value();
8106}
8107
8108
8109static Object* Runtime_DebugGetProperty(Arguments args) {
8110 HandleScope scope;
8111
8112 ASSERT(args.length() == 2);
8113
8114 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8115 CONVERT_ARG_CHECKED(String, name, 1);
8116
8117 LookupResult result;
8118 obj->Lookup(*name, &result);
8119 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008120 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008121 }
8122 return Heap::undefined_value();
8123}
8124
8125
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008126// Return the property type calculated from the property details.
8127// args[0]: smi with property details.
8128static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
8129 ASSERT(args.length() == 1);
8130 CONVERT_CHECKED(Smi, details, args[0]);
8131 PropertyType type = PropertyDetails(details).type();
8132 return Smi::FromInt(static_cast<int>(type));
8133}
8134
8135
8136// Return the property attribute calculated from the property details.
8137// args[0]: smi with property details.
8138static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
8139 ASSERT(args.length() == 1);
8140 CONVERT_CHECKED(Smi, details, args[0]);
8141 PropertyAttributes attributes = PropertyDetails(details).attributes();
8142 return Smi::FromInt(static_cast<int>(attributes));
8143}
8144
8145
8146// Return the property insertion index calculated from the property details.
8147// args[0]: smi with property details.
8148static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
8149 ASSERT(args.length() == 1);
8150 CONVERT_CHECKED(Smi, details, args[0]);
8151 int index = PropertyDetails(details).index();
8152 return Smi::FromInt(index);
8153}
8154
8155
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008156// Return property value from named interceptor.
8157// args[0]: object
8158// args[1]: property name
8159static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
8160 HandleScope scope;
8161 ASSERT(args.length() == 2);
8162 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8163 RUNTIME_ASSERT(obj->HasNamedInterceptor());
8164 CONVERT_ARG_CHECKED(String, name, 1);
8165
8166 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008167 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008168}
8169
8170
8171// Return element value from indexed interceptor.
8172// args[0]: object
8173// args[1]: index
8174static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
8175 HandleScope scope;
8176 ASSERT(args.length() == 2);
8177 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8178 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
8179 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
8180
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008181 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008182}
8183
8184
8185static Object* Runtime_CheckExecutionState(Arguments args) {
8186 ASSERT(args.length() >= 1);
8187 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00008188 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008189 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008190 return Top::Throw(Heap::illegal_execution_state_symbol());
8191 }
8192
8193 return Heap::true_value();
8194}
8195
8196
8197static Object* Runtime_GetFrameCount(Arguments args) {
8198 HandleScope scope;
8199 ASSERT(args.length() == 1);
8200
8201 // Check arguments.
8202 Object* result = Runtime_CheckExecutionState(args);
8203 if (result->IsFailure()) return result;
8204
8205 // Count all frames which are relevant to debugging stack trace.
8206 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008207 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008208 if (id == StackFrame::NO_ID) {
8209 // If there is no JavaScript stack frame count is 0.
8210 return Smi::FromInt(0);
8211 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008212 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
8213 return Smi::FromInt(n);
8214}
8215
8216
8217static const int kFrameDetailsFrameIdIndex = 0;
8218static const int kFrameDetailsReceiverIndex = 1;
8219static const int kFrameDetailsFunctionIndex = 2;
8220static const int kFrameDetailsArgumentCountIndex = 3;
8221static const int kFrameDetailsLocalCountIndex = 4;
8222static const int kFrameDetailsSourcePositionIndex = 5;
8223static const int kFrameDetailsConstructCallIndex = 6;
8224static const int kFrameDetailsDebuggerFrameIndex = 7;
8225static const int kFrameDetailsFirstDynamicIndex = 8;
8226
8227// Return an array with frame details
8228// args[0]: number: break id
8229// args[1]: number: frame index
8230//
8231// The array returned contains the following information:
8232// 0: Frame id
8233// 1: Receiver
8234// 2: Function
8235// 3: Argument count
8236// 4: Local count
8237// 5: Source position
8238// 6: Constructor call
8239// 7: Debugger frame
8240// Arguments name, value
8241// Locals name, value
8242static Object* Runtime_GetFrameDetails(Arguments args) {
8243 HandleScope scope;
8244 ASSERT(args.length() == 2);
8245
8246 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008247 Object* check = Runtime_CheckExecutionState(args);
8248 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008249 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8250
8251 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008252 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008253 if (id == StackFrame::NO_ID) {
8254 // If there are no JavaScript stack frames return undefined.
8255 return Heap::undefined_value();
8256 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008257 int count = 0;
8258 JavaScriptFrameIterator it(id);
8259 for (; !it.done(); it.Advance()) {
8260 if (count == index) break;
8261 count++;
8262 }
8263 if (it.done()) return Heap::undefined_value();
8264
8265 // Traverse the saved contexts chain to find the active context for the
8266 // selected frame.
8267 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008268 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008269 save = save->prev();
8270 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008271 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008272
8273 // Get the frame id.
8274 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
8275
8276 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008277 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008278
8279 // Check for constructor frame.
8280 bool constructor = it.frame()->IsConstructor();
8281
8282 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008283 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008284 ScopeInfo<> info(*code);
8285
8286 // Get the context.
8287 Handle<Context> context(Context::cast(it.frame()->context()));
8288
8289 // Get the locals names and values into a temporary array.
8290 //
8291 // TODO(1240907): Hide compiler-introduced stack variables
8292 // (e.g. .result)? For users of the debugger, they will probably be
8293 // confusing.
8294 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
8295 for (int i = 0; i < info.NumberOfLocals(); i++) {
8296 // Name of the local.
8297 locals->set(i * 2, *info.LocalName(i));
8298
8299 // Fetch the value of the local - either from the stack or from a
8300 // heap-allocated context.
8301 if (i < info.number_of_stack_slots()) {
8302 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
8303 } else {
8304 Handle<String> name = info.LocalName(i);
8305 // Traverse the context chain to the function context as all local
8306 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008307 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008308 context = Handle<Context>(context->previous());
8309 }
8310 ASSERT(context->is_function_context());
8311 locals->set(i * 2 + 1,
8312 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
8313 NULL)));
8314 }
8315 }
8316
8317 // Now advance to the arguments adapter frame (if any). If contains all
8318 // the provided parameters and
8319
8320 // Now advance to the arguments adapter frame (if any). It contains all
8321 // the provided parameters whereas the function frame always have the number
8322 // of arguments matching the functions parameters. The rest of the
8323 // information (except for what is collected above) is the same.
8324 it.AdvanceToArgumentsFrame();
8325
8326 // Find the number of arguments to fill. At least fill the number of
8327 // parameters for the function and fill more if more parameters are provided.
8328 int argument_count = info.number_of_parameters();
8329 if (argument_count < it.frame()->GetProvidedParametersCount()) {
8330 argument_count = it.frame()->GetProvidedParametersCount();
8331 }
8332
8333 // Calculate the size of the result.
8334 int details_size = kFrameDetailsFirstDynamicIndex +
8335 2 * (argument_count + info.NumberOfLocals());
8336 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8337
8338 // Add the frame id.
8339 details->set(kFrameDetailsFrameIdIndex, *frame_id);
8340
8341 // Add the function (same as in function frame).
8342 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
8343
8344 // Add the arguments count.
8345 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
8346
8347 // Add the locals count
8348 details->set(kFrameDetailsLocalCountIndex,
8349 Smi::FromInt(info.NumberOfLocals()));
8350
8351 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00008352 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008353 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
8354 } else {
8355 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
8356 }
8357
8358 // Add the constructor information.
8359 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
8360
8361 // Add information on whether this frame is invoked in the debugger context.
8362 details->set(kFrameDetailsDebuggerFrameIndex,
8363 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
8364
8365 // Fill the dynamic part.
8366 int details_index = kFrameDetailsFirstDynamicIndex;
8367
8368 // Add arguments name and value.
8369 for (int i = 0; i < argument_count; i++) {
8370 // Name of the argument.
8371 if (i < info.number_of_parameters()) {
8372 details->set(details_index++, *info.parameter_name(i));
8373 } else {
8374 details->set(details_index++, Heap::undefined_value());
8375 }
8376
8377 // Parameter value.
8378 if (i < it.frame()->GetProvidedParametersCount()) {
8379 details->set(details_index++, it.frame()->GetParameter(i));
8380 } else {
8381 details->set(details_index++, Heap::undefined_value());
8382 }
8383 }
8384
8385 // Add locals name and value from the temporary copy from the function frame.
8386 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
8387 details->set(details_index++, locals->get(i));
8388 }
8389
8390 // Add the receiver (same as in function frame).
8391 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
8392 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
8393 Handle<Object> receiver(it.frame()->receiver());
8394 if (!receiver->IsJSObject()) {
8395 // If the receiver is NOT a JSObject we have hit an optimization
8396 // where a value object is not converted into a wrapped JS objects.
8397 // To hide this optimization from the debugger, we wrap the receiver
8398 // by creating correct wrapper object based on the calling frame's
8399 // global context.
8400 it.Advance();
8401 Handle<Context> calling_frames_global_context(
8402 Context::cast(Context::cast(it.frame()->context())->global_context()));
8403 receiver = Factory::ToObject(receiver, calling_frames_global_context);
8404 }
8405 details->set(kFrameDetailsReceiverIndex, *receiver);
8406
8407 ASSERT_EQ(details_size, details_index);
8408 return *Factory::NewJSArrayWithElements(details);
8409}
8410
8411
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008412// Copy all the context locals into an object used to materialize a scope.
8413static void CopyContextLocalsToScopeObject(Handle<Code> code,
8414 ScopeInfo<>& scope_info,
8415 Handle<Context> context,
8416 Handle<JSObject> scope_object) {
8417 // Fill all context locals to the context extension.
8418 for (int i = Context::MIN_CONTEXT_SLOTS;
8419 i < scope_info.number_of_context_slots();
8420 i++) {
8421 int context_index =
8422 ScopeInfo<>::ContextSlotIndex(*code,
8423 *scope_info.context_slot_name(i),
8424 NULL);
8425
8426 // Don't include the arguments shadow (.arguments) context variable.
8427 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
8428 SetProperty(scope_object,
8429 scope_info.context_slot_name(i),
8430 Handle<Object>(context->get(context_index)), NONE);
8431 }
8432 }
8433}
8434
8435
8436// Create a plain JSObject which materializes the local scope for the specified
8437// frame.
8438static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
8439 Handle<JSFunction> function(JSFunction::cast(frame->function()));
8440 Handle<Code> code(function->code());
8441 ScopeInfo<> scope_info(*code);
8442
8443 // Allocate and initialize a JSObject with all the arguments, stack locals
8444 // heap locals and extension properties of the debugged function.
8445 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
8446
8447 // First fill all parameters.
8448 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8449 SetProperty(local_scope,
8450 scope_info.parameter_name(i),
8451 Handle<Object>(frame->GetParameter(i)), NONE);
8452 }
8453
8454 // Second fill all stack locals.
8455 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
8456 SetProperty(local_scope,
8457 scope_info.stack_slot_name(i),
8458 Handle<Object>(frame->GetExpression(i)), NONE);
8459 }
8460
8461 // Third fill all context locals.
8462 Handle<Context> frame_context(Context::cast(frame->context()));
8463 Handle<Context> function_context(frame_context->fcontext());
8464 CopyContextLocalsToScopeObject(code, scope_info,
8465 function_context, local_scope);
8466
8467 // Finally copy any properties from the function context extension. This will
8468 // be variables introduced by eval.
8469 if (function_context->closure() == *function) {
8470 if (function_context->has_extension() &&
8471 !function_context->IsGlobalContext()) {
8472 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008473 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008474 for (int i = 0; i < keys->length(); i++) {
8475 // Names of variables introduced by eval are strings.
8476 ASSERT(keys->get(i)->IsString());
8477 Handle<String> key(String::cast(keys->get(i)));
8478 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
8479 }
8480 }
8481 }
8482 return local_scope;
8483}
8484
8485
8486// Create a plain JSObject which materializes the closure content for the
8487// context.
8488static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
8489 ASSERT(context->is_function_context());
8490
8491 Handle<Code> code(context->closure()->code());
8492 ScopeInfo<> scope_info(*code);
8493
8494 // Allocate and initialize a JSObject with all the content of theis function
8495 // closure.
8496 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
8497
8498 // Check whether the arguments shadow object exists.
8499 int arguments_shadow_index =
8500 ScopeInfo<>::ContextSlotIndex(*code,
8501 Heap::arguments_shadow_symbol(),
8502 NULL);
8503 if (arguments_shadow_index >= 0) {
8504 // In this case all the arguments are available in the arguments shadow
8505 // object.
8506 Handle<JSObject> arguments_shadow(
8507 JSObject::cast(context->get(arguments_shadow_index)));
8508 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8509 SetProperty(closure_scope,
8510 scope_info.parameter_name(i),
8511 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
8512 }
8513 }
8514
8515 // Fill all context locals to the context extension.
8516 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
8517
8518 // Finally copy any properties from the function context extension. This will
8519 // be variables introduced by eval.
8520 if (context->has_extension()) {
8521 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008522 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008523 for (int i = 0; i < keys->length(); i++) {
8524 // Names of variables introduced by eval are strings.
8525 ASSERT(keys->get(i)->IsString());
8526 Handle<String> key(String::cast(keys->get(i)));
8527 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
8528 }
8529 }
8530
8531 return closure_scope;
8532}
8533
8534
8535// Iterate over the actual scopes visible from a stack frame. All scopes are
8536// backed by an actual context except the local scope, which is inserted
8537// "artifically" in the context chain.
8538class ScopeIterator {
8539 public:
8540 enum ScopeType {
8541 ScopeTypeGlobal = 0,
8542 ScopeTypeLocal,
8543 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00008544 ScopeTypeClosure,
8545 // Every catch block contains an implicit with block (its parameter is
8546 // a JSContextExtensionObject) that extends current scope with a variable
8547 // holding exception object. Such with blocks are treated as scopes of their
8548 // own type.
8549 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008550 };
8551
8552 explicit ScopeIterator(JavaScriptFrame* frame)
8553 : frame_(frame),
8554 function_(JSFunction::cast(frame->function())),
8555 context_(Context::cast(frame->context())),
8556 local_done_(false),
8557 at_local_(false) {
8558
8559 // Check whether the first scope is actually a local scope.
8560 if (context_->IsGlobalContext()) {
8561 // If there is a stack slot for .result then this local scope has been
8562 // created for evaluating top level code and it is not a real local scope.
8563 // Checking for the existence of .result seems fragile, but the scope info
8564 // saved with the code object does not otherwise have that information.
8565 Handle<Code> code(function_->code());
8566 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
8567 at_local_ = index < 0;
8568 } else if (context_->is_function_context()) {
8569 at_local_ = true;
8570 }
8571 }
8572
8573 // More scopes?
8574 bool Done() { return context_.is_null(); }
8575
8576 // Move to the next scope.
8577 void Next() {
8578 // If at a local scope mark the local scope as passed.
8579 if (at_local_) {
8580 at_local_ = false;
8581 local_done_ = true;
8582
8583 // If the current context is not associated with the local scope the
8584 // current context is the next real scope, so don't move to the next
8585 // context in this case.
8586 if (context_->closure() != *function_) {
8587 return;
8588 }
8589 }
8590
8591 // The global scope is always the last in the chain.
8592 if (context_->IsGlobalContext()) {
8593 context_ = Handle<Context>();
8594 return;
8595 }
8596
8597 // Move to the next context.
8598 if (context_->is_function_context()) {
8599 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
8600 } else {
8601 context_ = Handle<Context>(context_->previous());
8602 }
8603
8604 // If passing the local scope indicate that the current scope is now the
8605 // local scope.
8606 if (!local_done_ &&
8607 (context_->IsGlobalContext() || (context_->is_function_context()))) {
8608 at_local_ = true;
8609 }
8610 }
8611
8612 // Return the type of the current scope.
8613 int Type() {
8614 if (at_local_) {
8615 return ScopeTypeLocal;
8616 }
8617 if (context_->IsGlobalContext()) {
8618 ASSERT(context_->global()->IsGlobalObject());
8619 return ScopeTypeGlobal;
8620 }
8621 if (context_->is_function_context()) {
8622 return ScopeTypeClosure;
8623 }
8624 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00008625 // Current scope is either an explicit with statement or a with statement
8626 // implicitely generated for a catch block.
8627 // If the extension object here is a JSContextExtensionObject then
8628 // current with statement is one frome a catch block otherwise it's a
8629 // regular with statement.
8630 if (context_->extension()->IsJSContextExtensionObject()) {
8631 return ScopeTypeCatch;
8632 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008633 return ScopeTypeWith;
8634 }
8635
8636 // Return the JavaScript object with the content of the current scope.
8637 Handle<JSObject> ScopeObject() {
8638 switch (Type()) {
8639 case ScopeIterator::ScopeTypeGlobal:
8640 return Handle<JSObject>(CurrentContext()->global());
8641 break;
8642 case ScopeIterator::ScopeTypeLocal:
8643 // Materialize the content of the local scope into a JSObject.
8644 return MaterializeLocalScope(frame_);
8645 break;
8646 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00008647 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008648 // Return the with object.
8649 return Handle<JSObject>(CurrentContext()->extension());
8650 break;
8651 case ScopeIterator::ScopeTypeClosure:
8652 // Materialize the content of the closure scope into a JSObject.
8653 return MaterializeClosure(CurrentContext());
8654 break;
8655 }
8656 UNREACHABLE();
8657 return Handle<JSObject>();
8658 }
8659
8660 // Return the context for this scope. For the local context there might not
8661 // be an actual context.
8662 Handle<Context> CurrentContext() {
8663 if (at_local_ && context_->closure() != *function_) {
8664 return Handle<Context>();
8665 }
8666 return context_;
8667 }
8668
8669#ifdef DEBUG
8670 // Debug print of the content of the current scope.
8671 void DebugPrint() {
8672 switch (Type()) {
8673 case ScopeIterator::ScopeTypeGlobal:
8674 PrintF("Global:\n");
8675 CurrentContext()->Print();
8676 break;
8677
8678 case ScopeIterator::ScopeTypeLocal: {
8679 PrintF("Local:\n");
8680 Handle<Code> code(function_->code());
8681 ScopeInfo<> scope_info(*code);
8682 scope_info.Print();
8683 if (!CurrentContext().is_null()) {
8684 CurrentContext()->Print();
8685 if (CurrentContext()->has_extension()) {
8686 Handle<JSObject> extension =
8687 Handle<JSObject>(CurrentContext()->extension());
8688 if (extension->IsJSContextExtensionObject()) {
8689 extension->Print();
8690 }
8691 }
8692 }
8693 break;
8694 }
8695
8696 case ScopeIterator::ScopeTypeWith: {
8697 PrintF("With:\n");
8698 Handle<JSObject> extension =
8699 Handle<JSObject>(CurrentContext()->extension());
8700 extension->Print();
8701 break;
8702 }
8703
ager@chromium.orga1645e22009-09-09 19:27:10 +00008704 case ScopeIterator::ScopeTypeCatch: {
8705 PrintF("Catch:\n");
8706 Handle<JSObject> extension =
8707 Handle<JSObject>(CurrentContext()->extension());
8708 extension->Print();
8709 break;
8710 }
8711
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008712 case ScopeIterator::ScopeTypeClosure: {
8713 PrintF("Closure:\n");
8714 CurrentContext()->Print();
8715 if (CurrentContext()->has_extension()) {
8716 Handle<JSObject> extension =
8717 Handle<JSObject>(CurrentContext()->extension());
8718 if (extension->IsJSContextExtensionObject()) {
8719 extension->Print();
8720 }
8721 }
8722 break;
8723 }
8724
8725 default:
8726 UNREACHABLE();
8727 }
8728 PrintF("\n");
8729 }
8730#endif
8731
8732 private:
8733 JavaScriptFrame* frame_;
8734 Handle<JSFunction> function_;
8735 Handle<Context> context_;
8736 bool local_done_;
8737 bool at_local_;
8738
8739 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
8740};
8741
8742
8743static Object* Runtime_GetScopeCount(Arguments args) {
8744 HandleScope scope;
8745 ASSERT(args.length() == 2);
8746
8747 // Check arguments.
8748 Object* check = Runtime_CheckExecutionState(args);
8749 if (check->IsFailure()) return check;
8750 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8751
8752 // Get the frame where the debugging is performed.
8753 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8754 JavaScriptFrameIterator it(id);
8755 JavaScriptFrame* frame = it.frame();
8756
8757 // Count the visible scopes.
8758 int n = 0;
8759 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8760 n++;
8761 }
8762
8763 return Smi::FromInt(n);
8764}
8765
8766
8767static const int kScopeDetailsTypeIndex = 0;
8768static const int kScopeDetailsObjectIndex = 1;
8769static const int kScopeDetailsSize = 2;
8770
8771// Return an array with scope details
8772// args[0]: number: break id
8773// args[1]: number: frame index
8774// args[2]: number: scope index
8775//
8776// The array returned contains the following information:
8777// 0: Scope type
8778// 1: Scope object
8779static Object* Runtime_GetScopeDetails(Arguments args) {
8780 HandleScope scope;
8781 ASSERT(args.length() == 3);
8782
8783 // Check arguments.
8784 Object* check = Runtime_CheckExecutionState(args);
8785 if (check->IsFailure()) return check;
8786 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8787 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
8788
8789 // Get the frame where the debugging is performed.
8790 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8791 JavaScriptFrameIterator frame_it(id);
8792 JavaScriptFrame* frame = frame_it.frame();
8793
8794 // Find the requested scope.
8795 int n = 0;
8796 ScopeIterator it(frame);
8797 for (; !it.Done() && n < index; it.Next()) {
8798 n++;
8799 }
8800 if (it.Done()) {
8801 return Heap::undefined_value();
8802 }
8803
8804 // Calculate the size of the result.
8805 int details_size = kScopeDetailsSize;
8806 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8807
8808 // Fill in scope details.
8809 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
8810 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
8811
8812 return *Factory::NewJSArrayWithElements(details);
8813}
8814
8815
8816static Object* Runtime_DebugPrintScopes(Arguments args) {
8817 HandleScope scope;
8818 ASSERT(args.length() == 0);
8819
8820#ifdef DEBUG
8821 // Print the scopes for the top frame.
8822 StackFrameLocator locator;
8823 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
8824 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8825 it.DebugPrint();
8826 }
8827#endif
8828 return Heap::undefined_value();
8829}
8830
8831
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008832static Object* Runtime_GetCFrames(Arguments args) {
8833 HandleScope scope;
8834 ASSERT(args.length() == 1);
8835 Object* result = Runtime_CheckExecutionState(args);
8836 if (result->IsFailure()) return result;
8837
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008838#if V8_HOST_ARCH_64_BIT
8839 UNIMPLEMENTED();
8840 return Heap::undefined_value();
8841#else
8842
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008843 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008844 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
8845 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008846 if (frames_count == OS::kStackWalkError) {
8847 return Heap::undefined_value();
8848 }
8849
8850 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
8851 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
8852 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
8853 for (int i = 0; i < frames_count; i++) {
8854 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
8855 frame_value->SetProperty(
8856 *address_str,
8857 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
8858 NONE);
8859
8860 // Get the stack walk text for this frame.
8861 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008862 int frame_text_length = StrLength(frames[i].text);
8863 if (frame_text_length > 0) {
8864 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008865 frame_text = Factory::NewStringFromAscii(str);
8866 }
8867
8868 if (!frame_text.is_null()) {
8869 frame_value->SetProperty(*text_str, *frame_text, NONE);
8870 }
8871
8872 frames_array->set(i, *frame_value);
8873 }
8874 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008875#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008876}
8877
8878
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00008879static Object* Runtime_GetThreadCount(Arguments args) {
8880 HandleScope scope;
8881 ASSERT(args.length() == 1);
8882
8883 // Check arguments.
8884 Object* result = Runtime_CheckExecutionState(args);
8885 if (result->IsFailure()) return result;
8886
8887 // Count all archived V8 threads.
8888 int n = 0;
8889 for (ThreadState* thread = ThreadState::FirstInUse();
8890 thread != NULL;
8891 thread = thread->Next()) {
8892 n++;
8893 }
8894
8895 // Total number of threads is current thread and archived threads.
8896 return Smi::FromInt(n + 1);
8897}
8898
8899
8900static const int kThreadDetailsCurrentThreadIndex = 0;
8901static const int kThreadDetailsThreadIdIndex = 1;
8902static const int kThreadDetailsSize = 2;
8903
8904// Return an array with thread details
8905// args[0]: number: break id
8906// args[1]: number: thread index
8907//
8908// The array returned contains the following information:
8909// 0: Is current thread?
8910// 1: Thread id
8911static Object* Runtime_GetThreadDetails(Arguments args) {
8912 HandleScope scope;
8913 ASSERT(args.length() == 2);
8914
8915 // Check arguments.
8916 Object* check = Runtime_CheckExecutionState(args);
8917 if (check->IsFailure()) return check;
8918 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8919
8920 // Allocate array for result.
8921 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
8922
8923 // Thread index 0 is current thread.
8924 if (index == 0) {
8925 // Fill the details.
8926 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
8927 details->set(kThreadDetailsThreadIdIndex,
8928 Smi::FromInt(ThreadManager::CurrentId()));
8929 } else {
8930 // Find the thread with the requested index.
8931 int n = 1;
8932 ThreadState* thread = ThreadState::FirstInUse();
8933 while (index != n && thread != NULL) {
8934 thread = thread->Next();
8935 n++;
8936 }
8937 if (thread == NULL) {
8938 return Heap::undefined_value();
8939 }
8940
8941 // Fill the details.
8942 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
8943 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
8944 }
8945
8946 // Convert to JS array and return.
8947 return *Factory::NewJSArrayWithElements(details);
8948}
8949
8950
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008951static Object* Runtime_GetBreakLocations(Arguments args) {
8952 HandleScope scope;
8953 ASSERT(args.length() == 1);
8954
ager@chromium.org5aa501c2009-06-23 07:57:28 +00008955 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
8956 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008957 // Find the number of break points
8958 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
8959 if (break_locations->IsUndefined()) return Heap::undefined_value();
8960 // Return array as JS array
8961 return *Factory::NewJSArrayWithElements(
8962 Handle<FixedArray>::cast(break_locations));
8963}
8964
8965
8966// Set a break point in a function
8967// args[0]: function
8968// args[1]: number: break source position (within the function source)
8969// args[2]: number: break point object
8970static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
8971 HandleScope scope;
8972 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00008973 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
8974 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008975 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
8976 RUNTIME_ASSERT(source_position >= 0);
8977 Handle<Object> break_point_object_arg = args.at<Object>(2);
8978
8979 // Set break point.
8980 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
8981
8982 return Heap::undefined_value();
8983}
8984
8985
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00008986Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
8987 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008988 // Iterate the heap looking for SharedFunctionInfo generated from the
8989 // script. The inner most SharedFunctionInfo containing the source position
8990 // for the requested break point is found.
8991 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
8992 // which is found is not compiled it is compiled and the heap is iterated
8993 // again as the compilation might create inner functions from the newly
8994 // compiled function and the actual requested break point might be in one of
8995 // these functions.
8996 bool done = false;
8997 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00008998 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008999 Handle<SharedFunctionInfo> target;
9000 // The current candidate for the last function in script:
9001 Handle<SharedFunctionInfo> last;
9002 while (!done) {
9003 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009004 for (HeapObject* obj = iterator.next();
9005 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009006 if (obj->IsSharedFunctionInfo()) {
9007 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
9008 if (shared->script() == *script) {
9009 // If the SharedFunctionInfo found has the requested script data and
9010 // contains the source position it is a candidate.
9011 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00009012 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009013 start_position = shared->start_position();
9014 }
9015 if (start_position <= position &&
9016 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00009017 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009018 // candidate this is the new candidate.
9019 if (target.is_null()) {
9020 target_start_position = start_position;
9021 target = shared;
9022 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00009023 if (target_start_position == start_position &&
9024 shared->end_position() == target->end_position()) {
9025 // If a top-level function contain only one function
9026 // declartion the source for the top-level and the function is
9027 // the same. In that case prefer the non top-level function.
9028 if (!shared->is_toplevel()) {
9029 target_start_position = start_position;
9030 target = shared;
9031 }
9032 } else if (target_start_position <= start_position &&
9033 shared->end_position() <= target->end_position()) {
9034 // This containment check includes equality as a function inside
9035 // a top-level function can share either start or end position
9036 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009037 target_start_position = start_position;
9038 target = shared;
9039 }
9040 }
9041 }
9042
9043 // Keep track of the last function in the script.
9044 if (last.is_null() ||
9045 shared->end_position() > last->start_position()) {
9046 last = shared;
9047 }
9048 }
9049 }
9050 }
9051
9052 // Make sure some candidate is selected.
9053 if (target.is_null()) {
9054 if (!last.is_null()) {
9055 // Position after the last function - use last.
9056 target = last;
9057 } else {
9058 // Unable to find function - possibly script without any function.
9059 return Heap::undefined_value();
9060 }
9061 }
9062
9063 // If the candidate found is compiled we are done. NOTE: when lazy
9064 // compilation of inner functions is introduced some additional checking
9065 // needs to be done here to compile inner functions.
9066 done = target->is_compiled();
9067 if (!done) {
9068 // If the candidate is not compiled compile it to reveal any inner
9069 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009070 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009071 }
9072 }
9073
9074 return *target;
9075}
9076
9077
9078// Change the state of a break point in a script. NOTE: Regarding performance
9079// see the NOTE for GetScriptFromScriptData.
9080// args[0]: script to set break point in
9081// args[1]: number: break source position (within the script source)
9082// args[2]: number: break point object
9083static Object* Runtime_SetScriptBreakPoint(Arguments args) {
9084 HandleScope scope;
9085 ASSERT(args.length() == 3);
9086 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
9087 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9088 RUNTIME_ASSERT(source_position >= 0);
9089 Handle<Object> break_point_object_arg = args.at<Object>(2);
9090
9091 // Get the script from the script wrapper.
9092 RUNTIME_ASSERT(wrapper->value()->IsScript());
9093 Handle<Script> script(Script::cast(wrapper->value()));
9094
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009095 Object* result = Runtime::FindSharedFunctionInfoInScript(
9096 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009097 if (!result->IsUndefined()) {
9098 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
9099 // Find position within function. The script position might be before the
9100 // source position of the first function.
9101 int position;
9102 if (shared->start_position() > source_position) {
9103 position = 0;
9104 } else {
9105 position = source_position - shared->start_position();
9106 }
9107 Debug::SetBreakPoint(shared, position, break_point_object_arg);
9108 }
9109 return Heap::undefined_value();
9110}
9111
9112
9113// Clear a break point
9114// args[0]: number: break point object
9115static Object* Runtime_ClearBreakPoint(Arguments args) {
9116 HandleScope scope;
9117 ASSERT(args.length() == 1);
9118 Handle<Object> break_point_object_arg = args.at<Object>(0);
9119
9120 // Clear break point.
9121 Debug::ClearBreakPoint(break_point_object_arg);
9122
9123 return Heap::undefined_value();
9124}
9125
9126
9127// Change the state of break on exceptions
9128// args[0]: boolean indicating uncaught exceptions
9129// args[1]: boolean indicating on/off
9130static Object* Runtime_ChangeBreakOnException(Arguments args) {
9131 HandleScope scope;
9132 ASSERT(args.length() == 2);
9133 ASSERT(args[0]->IsNumber());
9134 ASSERT(args[1]->IsBoolean());
9135
9136 // Update break point state
9137 ExceptionBreakType type =
9138 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
9139 bool enable = args[1]->ToBoolean()->IsTrue();
9140 Debug::ChangeBreakOnException(type, enable);
9141 return Heap::undefined_value();
9142}
9143
9144
9145// Prepare for stepping
9146// args[0]: break id for checking execution state
9147// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00009148// args[2]: number of times to perform the step, for step out it is the number
9149// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009150static Object* Runtime_PrepareStep(Arguments args) {
9151 HandleScope scope;
9152 ASSERT(args.length() == 3);
9153 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009154 Object* check = Runtime_CheckExecutionState(args);
9155 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009156 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
9157 return Top::Throw(Heap::illegal_argument_symbol());
9158 }
9159
9160 // Get the step action and check validity.
9161 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
9162 if (step_action != StepIn &&
9163 step_action != StepNext &&
9164 step_action != StepOut &&
9165 step_action != StepInMin &&
9166 step_action != StepMin) {
9167 return Top::Throw(Heap::illegal_argument_symbol());
9168 }
9169
9170 // Get the number of steps.
9171 int step_count = NumberToInt32(args[2]);
9172 if (step_count < 1) {
9173 return Top::Throw(Heap::illegal_argument_symbol());
9174 }
9175
ager@chromium.orga1645e22009-09-09 19:27:10 +00009176 // Clear all current stepping setup.
9177 Debug::ClearStepping();
9178
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009179 // Prepare step.
9180 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
9181 return Heap::undefined_value();
9182}
9183
9184
9185// Clear all stepping set by PrepareStep.
9186static Object* Runtime_ClearStepping(Arguments args) {
9187 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009188 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009189 Debug::ClearStepping();
9190 return Heap::undefined_value();
9191}
9192
9193
9194// Creates a copy of the with context chain. The copy of the context chain is
9195// is linked to the function context supplied.
9196static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
9197 Handle<Context> function_context) {
9198 // At the bottom of the chain. Return the function context to link to.
9199 if (context_chain->is_function_context()) {
9200 return function_context;
9201 }
9202
9203 // Recursively copy the with contexts.
9204 Handle<Context> previous(context_chain->previous());
9205 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
9206 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009207 CopyWithContextChain(function_context, previous),
9208 extension,
9209 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009210}
9211
9212
9213// Helper function to find or create the arguments object for
9214// Runtime_DebugEvaluate.
9215static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
9216 Handle<JSFunction> function,
9217 Handle<Code> code,
9218 const ScopeInfo<>* sinfo,
9219 Handle<Context> function_context) {
9220 // Try to find the value of 'arguments' to pass as parameter. If it is not
9221 // found (that is the debugged function does not reference 'arguments' and
9222 // does not support eval) then create an 'arguments' object.
9223 int index;
9224 if (sinfo->number_of_stack_slots() > 0) {
9225 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
9226 if (index != -1) {
9227 return Handle<Object>(frame->GetExpression(index));
9228 }
9229 }
9230
9231 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
9232 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
9233 NULL);
9234 if (index != -1) {
9235 return Handle<Object>(function_context->get(index));
9236 }
9237 }
9238
9239 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009240 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
9241 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009242
9243 AssertNoAllocation no_gc;
9244 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009245 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009246 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009247 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009248 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009249 return arguments;
9250}
9251
9252
9253// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00009254// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009255// extension part has all the parameters and locals of the function on the
9256// stack frame. A function which calls eval with the code to evaluate is then
9257// compiled in this context and called in this context. As this context
9258// replaces the context of the function on the stack frame a new (empty)
9259// function is created as well to be used as the closure for the context.
9260// This function and the context acts as replacements for the function on the
9261// stack frame presenting the same view of the values of parameters and
9262// local variables as if the piece of JavaScript was evaluated at the point
9263// where the function on the stack frame is currently stopped.
9264static Object* Runtime_DebugEvaluate(Arguments args) {
9265 HandleScope scope;
9266
9267 // Check the execution state and decode arguments frame and source to be
9268 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009269 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009270 Object* check_result = Runtime_CheckExecutionState(args);
9271 if (check_result->IsFailure()) return check_result;
9272 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9273 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009274 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
9275
9276 // Handle the processing of break.
9277 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009278
9279 // Get the frame where the debugging is performed.
9280 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9281 JavaScriptFrameIterator it(id);
9282 JavaScriptFrame* frame = it.frame();
9283 Handle<JSFunction> function(JSFunction::cast(frame->function()));
9284 Handle<Code> code(function->code());
9285 ScopeInfo<> sinfo(*code);
9286
9287 // Traverse the saved contexts chain to find the active context for the
9288 // selected frame.
9289 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009290 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009291 save = save->prev();
9292 }
9293 ASSERT(save != NULL);
9294 SaveContext savex;
9295 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009296
9297 // Create the (empty) function replacing the function on the stack frame for
9298 // the purpose of evaluating in the context created below. It is important
9299 // that this function does not describe any parameters and local variables
9300 // in the context. If it does then this will cause problems with the lookup
9301 // in Context::Lookup, where context slots for parameters and local variables
9302 // are looked at before the extension object.
9303 Handle<JSFunction> go_between =
9304 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
9305 go_between->set_context(function->context());
9306#ifdef DEBUG
9307 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
9308 ASSERT(go_between_sinfo.number_of_parameters() == 0);
9309 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
9310#endif
9311
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009312 // Materialize the content of the local scope into a JSObject.
9313 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009314
9315 // Allocate a new context for the debug evaluation and set the extension
9316 // object build.
9317 Handle<Context> context =
9318 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009319 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009320 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009321 Handle<Context> frame_context(Context::cast(frame->context()));
9322 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009323 context = CopyWithContextChain(frame_context, context);
9324
9325 // Wrap the evaluation statement in a new function compiled in the newly
9326 // created context. The function has one parameter which has to be called
9327 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00009328 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009329 // function(arguments,__source__) {return eval(__source__);}
9330 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00009331 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009332 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009333 Handle<String> function_source =
9334 Factory::NewStringFromAscii(Vector<const char>(source_str,
9335 source_str_length));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009336 Handle<SharedFunctionInfo> shared =
ager@chromium.org381abbb2009-02-25 13:23:22 +00009337 Compiler::CompileEval(function_source,
9338 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00009339 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00009340 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009341 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009342 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009343 Factory::NewFunctionFromSharedFunctionInfo(shared, context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009344
9345 // Invoke the result of the compilation to get the evaluation function.
9346 bool has_pending_exception;
9347 Handle<Object> receiver(frame->receiver());
9348 Handle<Object> evaluation_function =
9349 Execution::Call(compiled_function, receiver, 0, NULL,
9350 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009351 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009352
9353 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
9354 function_context);
9355
9356 // Invoke the evaluation function and return the result.
9357 const int argc = 2;
9358 Object** argv[argc] = { arguments.location(),
9359 Handle<Object>::cast(source).location() };
9360 Handle<Object> result =
9361 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
9362 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009363 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009364
9365 // Skip the global proxy as it has no properties and always delegates to the
9366 // real global object.
9367 if (result->IsJSGlobalProxy()) {
9368 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
9369 }
9370
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009371 return *result;
9372}
9373
9374
9375static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
9376 HandleScope scope;
9377
9378 // Check the execution state and decode arguments frame and source to be
9379 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009380 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009381 Object* check_result = Runtime_CheckExecutionState(args);
9382 if (check_result->IsFailure()) return check_result;
9383 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009384 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
9385
9386 // Handle the processing of break.
9387 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009388
9389 // Enter the top context from before the debugger was invoked.
9390 SaveContext save;
9391 SaveContext* top = &save;
9392 while (top != NULL && *top->context() == *Debug::debug_context()) {
9393 top = top->prev();
9394 }
9395 if (top != NULL) {
9396 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009397 }
9398
9399 // Get the global context now set to the top context from before the
9400 // debugger was invoked.
9401 Handle<Context> context = Top::global_context();
9402
9403 // Compile the source to be evaluated.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009404 Handle<SharedFunctionInfo> shared =
9405 Compiler::CompileEval(source,
9406 context,
9407 true,
9408 Compiler::DONT_VALIDATE_JSON);
9409 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009410 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009411 Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
9412 context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009413
9414 // Invoke the result of the compilation to get the evaluation function.
9415 bool has_pending_exception;
9416 Handle<Object> receiver = Top::global();
9417 Handle<Object> result =
9418 Execution::Call(compiled_function, receiver, 0, NULL,
9419 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009420 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009421 return *result;
9422}
9423
9424
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009425static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
9426 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009427 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009428
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009429 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009430 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009431
9432 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009433 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00009434 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
9435 // Get the script wrapper in a local handle before calling GetScriptWrapper,
9436 // because using
9437 // instances->set(i, *GetScriptWrapper(script))
9438 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
9439 // already have deferenced the instances handle.
9440 Handle<JSValue> wrapper = GetScriptWrapper(script);
9441 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009442 }
9443
9444 // Return result as a JS array.
9445 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
9446 Handle<JSArray>::cast(result)->SetContent(*instances);
9447 return *result;
9448}
9449
9450
9451// Helper function used by Runtime_DebugReferencedBy below.
9452static int DebugReferencedBy(JSObject* target,
9453 Object* instance_filter, int max_references,
9454 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009455 JSFunction* arguments_function) {
9456 NoHandleAllocation ha;
9457 AssertNoAllocation no_alloc;
9458
9459 // Iterate the heap.
9460 int count = 0;
9461 JSObject* last = NULL;
9462 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009463 HeapObject* heap_obj = NULL;
9464 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009465 (max_references == 0 || count < max_references)) {
9466 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009467 if (heap_obj->IsJSObject()) {
9468 // Skip context extension objects and argument arrays as these are
9469 // checked in the context of functions using them.
9470 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00009471 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009472 obj->map()->constructor() == arguments_function) {
9473 continue;
9474 }
9475
9476 // Check if the JS object has a reference to the object looked for.
9477 if (obj->ReferencesObject(target)) {
9478 // Check instance filter if supplied. This is normally used to avoid
9479 // references from mirror objects (see Runtime_IsInPrototypeChain).
9480 if (!instance_filter->IsUndefined()) {
9481 Object* V = obj;
9482 while (true) {
9483 Object* prototype = V->GetPrototype();
9484 if (prototype->IsNull()) {
9485 break;
9486 }
9487 if (instance_filter == prototype) {
9488 obj = NULL; // Don't add this object.
9489 break;
9490 }
9491 V = prototype;
9492 }
9493 }
9494
9495 if (obj != NULL) {
9496 // Valid reference found add to instance array if supplied an update
9497 // count.
9498 if (instances != NULL && count < instances_size) {
9499 instances->set(count, obj);
9500 }
9501 last = obj;
9502 count++;
9503 }
9504 }
9505 }
9506 }
9507
9508 // Check for circular reference only. This can happen when the object is only
9509 // referenced from mirrors and has a circular reference in which case the
9510 // object is not really alive and would have been garbage collected if not
9511 // referenced from the mirror.
9512 if (count == 1 && last == target) {
9513 count = 0;
9514 }
9515
9516 // Return the number of referencing objects found.
9517 return count;
9518}
9519
9520
9521// Scan the heap for objects with direct references to an object
9522// args[0]: the object to find references to
9523// args[1]: constructor function for instances to exclude (Mirror)
9524// args[2]: the the maximum number of objects to return
9525static Object* Runtime_DebugReferencedBy(Arguments args) {
9526 ASSERT(args.length() == 3);
9527
9528 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009529 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009530
9531 // Check parameters.
9532 CONVERT_CHECKED(JSObject, target, args[0]);
9533 Object* instance_filter = args[1];
9534 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
9535 instance_filter->IsJSObject());
9536 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
9537 RUNTIME_ASSERT(max_references >= 0);
9538
9539 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009540 JSObject* arguments_boilerplate =
9541 Top::context()->global_context()->arguments_boilerplate();
9542 JSFunction* arguments_function =
9543 JSFunction::cast(arguments_boilerplate->map()->constructor());
9544
9545 // Get the number of referencing objects.
9546 int count;
9547 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009548 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009549
9550 // Allocate an array to hold the result.
9551 Object* object = Heap::AllocateFixedArray(count);
9552 if (object->IsFailure()) return object;
9553 FixedArray* instances = FixedArray::cast(object);
9554
9555 // Fill the referencing objects.
9556 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009557 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009558
9559 // Return result as JS array.
9560 Object* result =
9561 Heap::AllocateJSObject(
9562 Top::context()->global_context()->array_function());
9563 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9564 return result;
9565}
9566
9567
9568// Helper function used by Runtime_DebugConstructedBy below.
9569static int DebugConstructedBy(JSFunction* constructor, int max_references,
9570 FixedArray* instances, int instances_size) {
9571 AssertNoAllocation no_alloc;
9572
9573 // Iterate the heap.
9574 int count = 0;
9575 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009576 HeapObject* heap_obj = NULL;
9577 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009578 (max_references == 0 || count < max_references)) {
9579 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009580 if (heap_obj->IsJSObject()) {
9581 JSObject* obj = JSObject::cast(heap_obj);
9582 if (obj->map()->constructor() == constructor) {
9583 // Valid reference found add to instance array if supplied an update
9584 // count.
9585 if (instances != NULL && count < instances_size) {
9586 instances->set(count, obj);
9587 }
9588 count++;
9589 }
9590 }
9591 }
9592
9593 // Return the number of referencing objects found.
9594 return count;
9595}
9596
9597
9598// Scan the heap for objects constructed by a specific function.
9599// args[0]: the constructor to find instances of
9600// args[1]: the the maximum number of objects to return
9601static Object* Runtime_DebugConstructedBy(Arguments args) {
9602 ASSERT(args.length() == 2);
9603
9604 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009605 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009606
9607 // Check parameters.
9608 CONVERT_CHECKED(JSFunction, constructor, args[0]);
9609 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
9610 RUNTIME_ASSERT(max_references >= 0);
9611
9612 // Get the number of referencing objects.
9613 int count;
9614 count = DebugConstructedBy(constructor, max_references, NULL, 0);
9615
9616 // Allocate an array to hold the result.
9617 Object* object = Heap::AllocateFixedArray(count);
9618 if (object->IsFailure()) return object;
9619 FixedArray* instances = FixedArray::cast(object);
9620
9621 // Fill the referencing objects.
9622 count = DebugConstructedBy(constructor, max_references, instances, count);
9623
9624 // Return result as JS array.
9625 Object* result =
9626 Heap::AllocateJSObject(
9627 Top::context()->global_context()->array_function());
9628 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9629 return result;
9630}
9631
9632
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009633// Find the effective prototype object as returned by __proto__.
9634// args[0]: the object to find the prototype for.
9635static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009636 ASSERT(args.length() == 1);
9637
9638 CONVERT_CHECKED(JSObject, obj, args[0]);
9639
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009640 // Use the __proto__ accessor.
9641 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009642}
9643
9644
9645static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009646 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009647 CPU::DebugBreak();
9648 return Heap::undefined_value();
9649}
9650
9651
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009652static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009653#ifdef DEBUG
9654 HandleScope scope;
9655 ASSERT(args.length() == 1);
9656 // Get the function and make sure it is compiled.
9657 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009658 Handle<SharedFunctionInfo> shared(func->shared());
9659 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009660 return Failure::Exception();
9661 }
9662 func->code()->PrintLn();
9663#endif // DEBUG
9664 return Heap::undefined_value();
9665}
ager@chromium.org9085a012009-05-11 19:22:57 +00009666
9667
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009668static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
9669#ifdef DEBUG
9670 HandleScope scope;
9671 ASSERT(args.length() == 1);
9672 // Get the function and make sure it is compiled.
9673 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009674 Handle<SharedFunctionInfo> shared(func->shared());
9675 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009676 return Failure::Exception();
9677 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009678 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009679#endif // DEBUG
9680 return Heap::undefined_value();
9681}
9682
9683
ager@chromium.org9085a012009-05-11 19:22:57 +00009684static Object* Runtime_FunctionGetInferredName(Arguments args) {
9685 NoHandleAllocation ha;
9686 ASSERT(args.length() == 1);
9687
9688 CONVERT_CHECKED(JSFunction, f, args[0]);
9689 return f->shared()->inferred_name();
9690}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009691
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009692
9693static int FindSharedFunctionInfosForScript(Script* script,
9694 FixedArray* buffer) {
9695 AssertNoAllocation no_allocations;
9696
9697 int counter = 0;
9698 int buffer_size = buffer->length();
9699 HeapIterator iterator;
9700 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
9701 ASSERT(obj != NULL);
9702 if (!obj->IsSharedFunctionInfo()) {
9703 continue;
9704 }
9705 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
9706 if (shared->script() != script) {
9707 continue;
9708 }
9709 if (counter < buffer_size) {
9710 buffer->set(counter, shared);
9711 }
9712 counter++;
9713 }
9714 return counter;
9715}
9716
9717// For a script finds all SharedFunctionInfo's in the heap that points
9718// to this script. Returns JSArray of SharedFunctionInfo wrapped
9719// in OpaqueReferences.
9720static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
9721 Arguments args) {
9722 ASSERT(args.length() == 1);
9723 HandleScope scope;
9724 CONVERT_CHECKED(JSValue, script_value, args[0]);
9725
9726 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
9727
9728 const int kBufferSize = 32;
9729
9730 Handle<FixedArray> array;
9731 array = Factory::NewFixedArray(kBufferSize);
9732 int number = FindSharedFunctionInfosForScript(*script, *array);
9733 if (number > kBufferSize) {
9734 array = Factory::NewFixedArray(number);
9735 FindSharedFunctionInfosForScript(*script, *array);
9736 }
9737
9738 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
9739 result->set_length(Smi::FromInt(number));
9740
9741 LiveEdit::WrapSharedFunctionInfos(result);
9742
9743 return *result;
9744}
9745
9746// For a script calculates compilation information about all its functions.
9747// The script source is explicitly specified by the second argument.
9748// The source of the actual script is not used, however it is important that
9749// all generated code keeps references to this particular instance of script.
9750// Returns a JSArray of compilation infos. The array is ordered so that
9751// each function with all its descendant is always stored in a continues range
9752// with the function itself going first. The root function is a script function.
9753static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
9754 ASSERT(args.length() == 2);
9755 HandleScope scope;
9756 CONVERT_CHECKED(JSValue, script, args[0]);
9757 CONVERT_ARG_CHECKED(String, source, 1);
9758 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
9759
9760 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
9761
9762 if (Top::has_pending_exception()) {
9763 return Failure::Exception();
9764 }
9765
9766 return result;
9767}
9768
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009769// Changes the source of the script to a new_source.
9770// If old_script_name is provided (i.e. is a String), also creates a copy of
9771// the script with its original source and sends notification to debugger.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009772static Object* Runtime_LiveEditReplaceScript(Arguments args) {
9773 ASSERT(args.length() == 3);
9774 HandleScope scope;
9775 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
9776 CONVERT_ARG_CHECKED(String, new_source, 1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009777 Handle<Object> old_script_name(args[2]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009778
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009779 CONVERT_CHECKED(Script, original_script_pointer,
9780 original_script_value->value());
9781 Handle<Script> original_script(original_script_pointer);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009782
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009783 Object* old_script = LiveEdit::ChangeScriptSource(original_script,
9784 new_source,
9785 old_script_name);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009786
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009787 if (old_script->IsScript()) {
9788 Handle<Script> script_handle(Script::cast(old_script));
9789 return *(GetScriptWrapper(script_handle));
9790 } else {
9791 return Heap::null_value();
9792 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009793}
9794
9795// Replaces code of SharedFunctionInfo with a new one.
9796static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
9797 ASSERT(args.length() == 2);
9798 HandleScope scope;
9799 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
9800 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
9801
ager@chromium.orgac091b72010-05-05 07:34:42 +00009802 return LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009803}
9804
9805// Connects SharedFunctionInfo to another script.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009806static Object* Runtime_LiveEditFunctionSetScript(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009807 ASSERT(args.length() == 2);
9808 HandleScope scope;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009809 Handle<Object> function_object(args[0]);
9810 Handle<Object> script_object(args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009811
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009812 if (function_object->IsJSValue()) {
9813 Handle<JSValue> function_wrapper = Handle<JSValue>::cast(function_object);
9814 if (script_object->IsJSValue()) {
9815 CONVERT_CHECKED(Script, script, JSValue::cast(*script_object)->value());
9816 script_object = Handle<Object>(script);
9817 }
9818
9819 LiveEdit::SetFunctionScript(function_wrapper, script_object);
9820 } else {
9821 // Just ignore this. We may not have a SharedFunctionInfo for some functions
9822 // and we check it in this function.
9823 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009824
9825 return Heap::undefined_value();
9826}
9827
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009828
9829// In a code of a parent function replaces original function as embedded object
9830// with a substitution one.
9831static Object* Runtime_LiveEditReplaceRefToNestedFunction(Arguments args) {
9832 ASSERT(args.length() == 3);
9833 HandleScope scope;
9834
9835 CONVERT_ARG_CHECKED(JSValue, parent_wrapper, 0);
9836 CONVERT_ARG_CHECKED(JSValue, orig_wrapper, 1);
9837 CONVERT_ARG_CHECKED(JSValue, subst_wrapper, 2);
9838
9839 LiveEdit::ReplaceRefToNestedFunction(parent_wrapper, orig_wrapper,
9840 subst_wrapper);
9841
9842 return Heap::undefined_value();
9843}
9844
9845
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009846// Updates positions of a shared function info (first parameter) according
9847// to script source change. Text change is described in second parameter as
9848// array of groups of 3 numbers:
9849// (change_begin, change_end, change_end_new_position).
9850// Each group describes a change in text; groups are sorted by change_begin.
9851static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
9852 ASSERT(args.length() == 2);
9853 HandleScope scope;
9854 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
9855 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
9856
ager@chromium.orgac091b72010-05-05 07:34:42 +00009857 return LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009858}
9859
9860
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009861// For array of SharedFunctionInfo's (each wrapped in JSValue)
9862// checks that none of them have activations on stacks (of any thread).
9863// Returns array of the same length with corresponding results of
9864// LiveEdit::FunctionPatchabilityStatus type.
ager@chromium.org357bf652010-04-12 11:30:10 +00009865static Object* Runtime_LiveEditCheckAndDropActivations(Arguments args) {
9866 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009867 HandleScope scope;
9868 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +00009869 CONVERT_BOOLEAN_CHECKED(do_drop, args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009870
ager@chromium.org357bf652010-04-12 11:30:10 +00009871 return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009872}
9873
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009874// Compares 2 strings line-by-line and returns diff in form of JSArray of
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00009875// triplets (pos1, pos1_end, pos2_end) describing list of diff chunks.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009876static Object* Runtime_LiveEditCompareStringsLinewise(Arguments args) {
9877 ASSERT(args.length() == 2);
9878 HandleScope scope;
9879 CONVERT_ARG_CHECKED(String, s1, 0);
9880 CONVERT_ARG_CHECKED(String, s2, 1);
9881
9882 return *LiveEdit::CompareStringsLinewise(s1, s2);
9883}
9884
9885
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009886
fschneider@chromium.org086aac62010-03-17 13:18:24 +00009887// A testing entry. Returns statement position which is the closest to
9888// source_position.
9889static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
9890 ASSERT(args.length() == 2);
9891 HandleScope scope;
9892 CONVERT_ARG_CHECKED(JSFunction, function, 0);
9893 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9894
9895 Handle<Code> code(function->code());
9896
9897 RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
9898 int closest_pc = 0;
9899 int distance = kMaxInt;
9900 while (!it.done()) {
9901 int statement_position = static_cast<int>(it.rinfo()->data());
9902 // Check if this break point is closer that what was previously found.
9903 if (source_position <= statement_position &&
9904 statement_position - source_position < distance) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00009905 closest_pc =
9906 static_cast<int>(it.rinfo()->pc() - code->instruction_start());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00009907 distance = statement_position - source_position;
9908 // Check whether we can't get any closer.
9909 if (distance == 0) break;
9910 }
9911 it.next();
9912 }
9913
9914 return Smi::FromInt(closest_pc);
9915}
9916
9917
ager@chromium.org357bf652010-04-12 11:30:10 +00009918// Calls specified function with or without entering the debugger.
9919// This is used in unit tests to run code as if debugger is entered or simply
9920// to have a stack with C++ frame in the middle.
9921static Object* Runtime_ExecuteInDebugContext(Arguments args) {
9922 ASSERT(args.length() == 2);
9923 HandleScope scope;
9924 CONVERT_ARG_CHECKED(JSFunction, function, 0);
9925 CONVERT_BOOLEAN_CHECKED(without_debugger, args[1]);
9926
9927 Handle<Object> result;
9928 bool pending_exception;
9929 {
9930 if (without_debugger) {
9931 result = Execution::Call(function, Top::global(), 0, NULL,
9932 &pending_exception);
9933 } else {
9934 EnterDebugger enter_debugger;
9935 result = Execution::Call(function, Top::global(), 0, NULL,
9936 &pending_exception);
9937 }
9938 }
9939 if (!pending_exception) {
9940 return *result;
9941 } else {
9942 return Failure::Exception();
9943 }
9944}
9945
9946
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009947#endif // ENABLE_DEBUGGER_SUPPORT
9948
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009949#ifdef ENABLE_LOGGING_AND_PROFILING
9950
9951static Object* Runtime_ProfilerResume(Arguments args) {
9952 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +00009953 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009954
9955 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +00009956 CONVERT_CHECKED(Smi, smi_tag, args[1]);
9957 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009958 return Heap::undefined_value();
9959}
9960
9961
9962static Object* Runtime_ProfilerPause(Arguments args) {
9963 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +00009964 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009965
9966 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +00009967 CONVERT_CHECKED(Smi, smi_tag, args[1]);
9968 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009969 return Heap::undefined_value();
9970}
9971
9972#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009973
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009974// Finds the script object from the script data. NOTE: This operation uses
9975// heap traversal to find the function generated for the source position
9976// for the requested break point. For lazily compiled functions several heap
9977// traversals might be required rendering this operation as a rather slow
9978// operation. However for setting break points which is normally done through
9979// some kind of user interaction the performance is not crucial.
9980static Handle<Object> Runtime_GetScriptFromScriptName(
9981 Handle<String> script_name) {
9982 // Scan the heap for Script objects to find the script with the requested
9983 // script data.
9984 Handle<Script> script;
9985 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009986 HeapObject* obj = NULL;
9987 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009988 // If a script is found check if it has the script data requested.
9989 if (obj->IsScript()) {
9990 if (Script::cast(obj)->name()->IsString()) {
9991 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
9992 script = Handle<Script>(Script::cast(obj));
9993 }
9994 }
9995 }
9996 }
9997
9998 // If no script with the requested script data is found return undefined.
9999 if (script.is_null()) return Factory::undefined_value();
10000
10001 // Return the script found.
10002 return GetScriptWrapper(script);
10003}
10004
10005
10006// Get the script object from script data. NOTE: Regarding performance
10007// see the NOTE for GetScriptFromScriptData.
10008// args[0]: script data for the script to find the source for
10009static Object* Runtime_GetScript(Arguments args) {
10010 HandleScope scope;
10011
10012 ASSERT(args.length() == 1);
10013
10014 CONVERT_CHECKED(String, script_name, args[0]);
10015
10016 // Find the requested script.
10017 Handle<Object> result =
10018 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
10019 return *result;
10020}
10021
10022
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010023// Determines whether the given stack frame should be displayed in
10024// a stack trace. The caller is the error constructor that asked
10025// for the stack trace to be collected. The first time a construct
10026// call to this function is encountered it is skipped. The seen_caller
10027// in/out parameter is used to remember if the caller has been seen
10028// yet.
10029static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
10030 bool* seen_caller) {
10031 // Only display JS frames.
10032 if (!raw_frame->is_java_script())
10033 return false;
10034 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
10035 Object* raw_fun = frame->function();
10036 // Not sure when this can happen but skip it just in case.
10037 if (!raw_fun->IsJSFunction())
10038 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010039 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010040 *seen_caller = true;
10041 return false;
10042 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010043 // Skip all frames until we've seen the caller. Also, skip the most
10044 // obvious builtin calls. Some builtin calls (such as Number.ADD
10045 // which is invoked using 'call') are very difficult to recognize
10046 // so we're leaving them in for now.
10047 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010048}
10049
10050
10051// Collect the raw data for a stack trace. Returns an array of three
10052// element segments each containing a receiver, function and native
10053// code offset.
10054static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010055 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010056 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010057 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
10058
10059 HandleScope scope;
10060
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000010061 limit = Max(limit, 0); // Ensure that limit is not negative.
10062 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010063 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010064
10065 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010066 // If the caller parameter is a function we skip frames until we're
10067 // under it before starting to collect.
10068 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010069 int cursor = 0;
10070 int frames_seen = 0;
10071 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010072 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010073 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010074 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010075 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010076 Object* recv = frame->receiver();
10077 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010078 Address pc = frame->pc();
10079 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010080 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010081 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010082 if (cursor + 2 < elements->length()) {
10083 elements->set(cursor++, recv);
10084 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010085 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010086 } else {
10087 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010088 Handle<Object> recv_handle(recv);
10089 Handle<Object> fun_handle(fun);
10090 SetElement(result, cursor++, recv_handle);
10091 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010092 SetElement(result, cursor++, Handle<Smi>(offset));
10093 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010094 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010095 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010096 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010097
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010098 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010099 return *result;
10100}
10101
10102
ager@chromium.org3811b432009-10-28 14:53:37 +000010103// Returns V8 version as a string.
10104static Object* Runtime_GetV8Version(Arguments args) {
10105 ASSERT_EQ(args.length(), 0);
10106
10107 NoHandleAllocation ha;
10108
10109 const char* version_string = v8::V8::GetVersion();
10110
10111 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
10112}
10113
10114
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010115static Object* Runtime_Abort(Arguments args) {
10116 ASSERT(args.length() == 2);
10117 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
10118 Smi::cast(args[1])->value());
10119 Top::PrintStack();
10120 OS::Abort();
10121 UNREACHABLE();
10122 return NULL;
10123}
10124
10125
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010126static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
10127 ASSERT(args.length() == 0);
10128 HandleScope::DeleteExtensions();
10129 return Heap::undefined_value();
10130}
10131
10132
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010133static Object* CacheMiss(FixedArray* cache_obj, int index, Object* key_obj) {
10134 ASSERT(index % 2 == 0); // index of the key
10135 ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
10136 ASSERT(index < cache_obj->length());
10137
10138 HandleScope scope;
10139
10140 Handle<FixedArray> cache(cache_obj);
10141 Handle<Object> key(key_obj);
10142 Handle<JSFunction> factory(JSFunction::cast(
10143 cache->get(JSFunctionResultCache::kFactoryIndex)));
10144 // TODO(antonm): consider passing a receiver when constructing a cache.
10145 Handle<Object> receiver(Top::global_context()->global());
10146
10147 Handle<Object> value;
10148 {
10149 // This handle is nor shared, nor used later, so it's safe.
10150 Object** argv[] = { key.location() };
10151 bool pending_exception = false;
10152 value = Execution::Call(factory,
10153 receiver,
10154 1,
10155 argv,
10156 &pending_exception);
10157 if (pending_exception) return Failure::Exception();
10158 }
10159
10160 cache->set(index, *key);
10161 cache->set(index + 1, *value);
10162 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(index));
10163
10164 return *value;
10165}
10166
10167
10168static Object* Runtime_GetFromCache(Arguments args) {
10169 // This is only called from codegen, so checks might be more lax.
10170 CONVERT_CHECKED(FixedArray, cache, args[0]);
10171 Object* key = args[1];
10172
10173 const int finger_index =
10174 Smi::cast(cache->get(JSFunctionResultCache::kFingerIndex))->value();
10175
10176 Object* o = cache->get(finger_index);
10177 if (o == key) {
10178 // The fastest case: hit the same place again.
10179 return cache->get(finger_index + 1);
10180 }
10181
10182 for (int i = finger_index - 2;
10183 i >= JSFunctionResultCache::kEntriesIndex;
10184 i -= 2) {
10185 o = cache->get(i);
10186 if (o == key) {
10187 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10188 return cache->get(i + 1);
10189 }
10190 }
10191
10192 const int size =
10193 Smi::cast(cache->get(JSFunctionResultCache::kCacheSizeIndex))->value();
10194 ASSERT(size <= cache->length());
10195
10196 for (int i = size - 2; i > finger_index; i -= 2) {
10197 o = cache->get(i);
10198 if (o == key) {
10199 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10200 return cache->get(i + 1);
10201 }
10202 }
10203
10204 // Cache miss. If we have spare room, put new data into it, otherwise
10205 // evict post finger entry which must be least recently used.
10206 if (size < cache->length()) {
10207 cache->set(JSFunctionResultCache::kCacheSizeIndex, Smi::FromInt(size + 2));
10208 return CacheMiss(cache, size, key);
10209 } else {
antonm@chromium.org397e23c2010-04-21 12:00:05 +000010210 int target_index = finger_index + JSFunctionResultCache::kEntrySize;
10211 if (target_index == cache->length()) {
10212 target_index = JSFunctionResultCache::kEntriesIndex;
10213 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010214 return CacheMiss(cache, target_index, key);
10215 }
10216}
10217
kasper.lund44510672008-07-25 07:37:58 +000010218#ifdef DEBUG
10219// ListNatives is ONLY used by the fuzz-natives.js in debug mode
10220// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010221static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +000010222 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010223 HandleScope scope;
10224 Handle<JSArray> result = Factory::NewJSArray(0);
10225 int index = 0;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010226 bool inline_runtime_functions = false;
ager@chromium.orga1645e22009-09-09 19:27:10 +000010227#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010228 { \
10229 HandleScope inner; \
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010230 Handle<String> name; \
10231 /* Inline runtime functions have an underscore in front of the name. */ \
10232 if (inline_runtime_functions) { \
10233 name = Factory::NewStringFromAscii( \
10234 Vector<const char>("_" #Name, StrLength("_" #Name))); \
10235 } else { \
10236 name = Factory::NewStringFromAscii( \
10237 Vector<const char>(#Name, StrLength(#Name))); \
10238 } \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010239 Handle<JSArray> pair = Factory::NewJSArray(0); \
10240 SetElement(pair, 0, name); \
10241 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
10242 SetElement(result, index++, pair); \
10243 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010244 inline_runtime_functions = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010245 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010246 inline_runtime_functions = true;
10247 INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010248#undef ADD_ENTRY
10249 return *result;
10250}
kasper.lund44510672008-07-25 07:37:58 +000010251#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010252
10253
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010254static Object* Runtime_Log(Arguments args) {
10255 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +000010256 CONVERT_CHECKED(String, format, args[0]);
10257 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010258 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010259 Logger::LogRuntime(chars, elms);
10260 return Heap::undefined_value();
10261}
10262
10263
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010264static Object* Runtime_IS_VAR(Arguments args) {
10265 UNREACHABLE(); // implemented as macro in the parser
10266 return NULL;
10267}
10268
10269
10270// ----------------------------------------------------------------------------
10271// Implementation of Runtime
10272
ager@chromium.orga1645e22009-09-09 19:27:10 +000010273#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010274 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +000010275 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010276
10277static Runtime::Function Runtime_functions[] = {
10278 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010279 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010280};
10281
10282#undef F
10283
10284
10285Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
10286 ASSERT(0 <= fid && fid < kNofFunctions);
10287 return &Runtime_functions[fid];
10288}
10289
10290
10291Runtime::Function* Runtime::FunctionForName(const char* name) {
10292 for (Function* f = Runtime_functions; f->name != NULL; f++) {
10293 if (strcmp(f->name, name) == 0) {
10294 return f;
10295 }
10296 }
10297 return NULL;
10298}
10299
10300
10301void Runtime::PerformGC(Object* result) {
10302 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010303 if (failure->IsRetryAfterGC()) {
10304 // Try to do a garbage collection; ignore it if it fails. The C
10305 // entry stub will throw an out-of-memory exception in that case.
10306 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
10307 } else {
10308 // Handle last resort GC and make sure to allow future allocations
10309 // to grow the heap without causing GCs (if possible).
10310 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +000010311 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010312 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010313}
10314
10315
10316} } // namespace v8::internal