blob: 22e80b33e0bbe253cd3b563619550cdf4c1920f9 [file] [log] [blame]
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001// Copyright 2006-2009 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include <stdlib.h>
29
30#include "v8.h"
31
32#include "accessors.h"
33#include "api.h"
34#include "arguments.h"
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000035#include "codegen.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000036#include "compiler.h"
37#include "cpu.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000038#include "dateparser-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000039#include "debug.h"
40#include "execution.h"
41#include "jsregexp.h"
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000042#include "liveedit.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000043#include "parser.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000044#include "platform.h"
45#include "runtime.h"
46#include "scopeinfo.h"
ager@chromium.org7c537e22008-10-16 08:43:32 +000047#include "smart-pointer.h"
ager@chromium.org18ad94b2009-09-02 08:22:29 +000048#include "stub-cache.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000049#include "v8threads.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000050
kasperl@chromium.org71affb52009-05-26 05:44:31 +000051namespace v8 {
52namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000053
54
ager@chromium.org3e875802009-06-29 08:26:34 +000055#define RUNTIME_ASSERT(value) \
56 if (!(value)) return Top::ThrowIllegalOperation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000057
58// Cast the given object to a value of the specified type and store
59// it in a variable with the given name. If the object is not of the
60// expected type call IllegalOperation and return.
61#define CONVERT_CHECKED(Type, name, obj) \
62 RUNTIME_ASSERT(obj->Is##Type()); \
63 Type* name = Type::cast(obj);
64
65#define CONVERT_ARG_CHECKED(Type, name, index) \
66 RUNTIME_ASSERT(args[index]->Is##Type()); \
67 Handle<Type> name = args.at<Type>(index);
68
kasper.lundbd3ec4e2008-07-09 11:06:54 +000069// Cast the given object to a boolean and store it in a variable with
70// the given name. If the object is not a boolean call IllegalOperation
71// and return.
72#define CONVERT_BOOLEAN_CHECKED(name, obj) \
73 RUNTIME_ASSERT(obj->IsBoolean()); \
74 bool name = (obj)->IsTrue();
75
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000076// Cast the given object to a Smi and store its value in an int variable
77// with the given name. If the object is not a Smi call IllegalOperation
78// and return.
79#define CONVERT_SMI_CHECKED(name, obj) \
80 RUNTIME_ASSERT(obj->IsSmi()); \
81 int name = Smi::cast(obj)->value();
82
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000083// Cast the given object to a double and store it in a variable with
84// the given name. If the object is not a number (as opposed to
85// the number not-a-number) call IllegalOperation and return.
86#define CONVERT_DOUBLE_CHECKED(name, obj) \
87 RUNTIME_ASSERT(obj->IsNumber()); \
88 double name = (obj)->Number();
89
90// Call the specified converter on the object *comand store the result in
91// a variable of the specified type with the given name. If the
92// object is not a Number call IllegalOperation and return.
93#define CONVERT_NUMBER_CHECKED(type, name, Type, obj) \
94 RUNTIME_ASSERT(obj->IsNumber()); \
95 type name = NumberTo##Type(obj);
96
97// Non-reentrant string buffer for efficient general use in this file.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000098static StaticResource<StringInputBuffer> runtime_string_input_buffer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000099
100
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000101static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
102 StackLimitCheck check;
103 if (check.HasOverflowed()) return Top::StackOverflow();
104
105 Object* result = Heap::CopyJSObject(boilerplate);
106 if (result->IsFailure()) return result;
107 JSObject* copy = JSObject::cast(result);
108
109 // Deep copy local properties.
110 if (copy->HasFastProperties()) {
111 FixedArray* properties = copy->properties();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000112 for (int i = 0; i < properties->length(); i++) {
113 Object* value = properties->get(i);
114 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000115 JSObject* js_object = JSObject::cast(value);
116 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000117 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000118 properties->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000119 }
120 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000121 int nof = copy->map()->inobject_properties();
122 for (int i = 0; i < nof; i++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000123 Object* value = copy->InObjectPropertyAt(i);
124 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000125 JSObject* js_object = JSObject::cast(value);
126 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000127 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000128 copy->InObjectPropertyAtPut(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000129 }
130 }
131 } else {
132 result = Heap::AllocateFixedArray(copy->NumberOfLocalProperties(NONE));
133 if (result->IsFailure()) return result;
134 FixedArray* names = FixedArray::cast(result);
135 copy->GetLocalPropertyNames(names, 0);
136 for (int i = 0; i < names->length(); i++) {
137 ASSERT(names->get(i)->IsString());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000138 String* key_string = String::cast(names->get(i));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000139 PropertyAttributes attributes =
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000140 copy->GetLocalPropertyAttribute(key_string);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000141 // Only deep copy fields from the object literal expression.
142 // In particular, don't try to copy the length attribute of
143 // an array.
144 if (attributes != NONE) continue;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000145 Object* value = copy->GetProperty(key_string, &attributes);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000146 ASSERT(!value->IsFailure());
147 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000148 JSObject* js_object = JSObject::cast(value);
149 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000150 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000151 result = copy->SetProperty(key_string, result, NONE);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000152 if (result->IsFailure()) return result;
153 }
154 }
155 }
156
157 // Deep copy local elements.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000158 // Pixel elements cannot be created using an object literal.
ager@chromium.org3811b432009-10-28 14:53:37 +0000159 ASSERT(!copy->HasPixelElements() && !copy->HasExternalArrayElements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000160 switch (copy->GetElementsKind()) {
161 case JSObject::FAST_ELEMENTS: {
162 FixedArray* elements = FixedArray::cast(copy->elements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000163 for (int i = 0; i < elements->length(); i++) {
164 Object* value = elements->get(i);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000165 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000166 JSObject* js_object = JSObject::cast(value);
167 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000168 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000169 elements->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000170 }
171 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000172 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000173 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000174 case JSObject::DICTIONARY_ELEMENTS: {
175 NumberDictionary* element_dictionary = copy->element_dictionary();
176 int capacity = element_dictionary->Capacity();
177 for (int i = 0; i < capacity; i++) {
178 Object* k = element_dictionary->KeyAt(i);
179 if (element_dictionary->IsKey(k)) {
180 Object* value = element_dictionary->ValueAt(i);
181 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000182 JSObject* js_object = JSObject::cast(value);
183 result = DeepCopyBoilerplate(js_object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000184 if (result->IsFailure()) return result;
185 element_dictionary->ValueAtPut(i, result);
186 }
187 }
188 }
189 break;
190 }
191 default:
192 UNREACHABLE();
193 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000194 }
195 return copy;
196}
197
198
199static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
200 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
201 return DeepCopyBoilerplate(boilerplate);
202}
203
204
205static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000206 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000207 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000208}
209
210
ager@chromium.org236ad962008-09-25 09:45:57 +0000211static Handle<Map> ComputeObjectLiteralMap(
212 Handle<Context> context,
213 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000214 bool* is_result_from_cache) {
ager@chromium.org32912102009-01-16 10:38:43 +0000215 int number_of_properties = constant_properties->length() / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000216 if (FLAG_canonicalize_object_literal_maps) {
217 // First find prefix of consecutive symbol keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000218 int number_of_symbol_keys = 0;
219 while ((number_of_symbol_keys < number_of_properties) &&
220 (constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
221 number_of_symbol_keys++;
222 }
223 // Based on the number of prefix symbols key we decide whether
224 // to use the map cache in the global context.
225 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000226 if ((number_of_symbol_keys == number_of_properties) &&
227 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000228 // Create the fixed array with the key.
229 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
230 for (int i = 0; i < number_of_symbol_keys; i++) {
231 keys->set(i, constant_properties->get(i*2));
232 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000233 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000234 return Factory::ObjectLiteralMapFromCache(context, keys);
235 }
236 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000237 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000238 return Factory::CopyMap(
239 Handle<Map>(context->object_function()->initial_map()),
240 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000241}
242
243
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000244static Handle<Object> CreateLiteralBoilerplate(
245 Handle<FixedArray> literals,
246 Handle<FixedArray> constant_properties);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000247
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000248
249static Handle<Object> CreateObjectLiteralBoilerplate(
250 Handle<FixedArray> literals,
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000251 Handle<FixedArray> constant_properties,
252 bool should_have_fast_elements) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000253 // Get the global context from the literals array. This is the
254 // context in which the function was created and we use the object
255 // function from this context to create the object literal. We do
256 // not use the object function from the current global context
257 // because this might be the object function from another context
258 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000259 Handle<Context> context =
260 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
261
262 bool is_result_from_cache;
263 Handle<Map> map = ComputeObjectLiteralMap(context,
264 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000265 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000266
ager@chromium.org236ad962008-09-25 09:45:57 +0000267 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000268
269 // Normalize the elements of the boilerplate to save space if needed.
270 if (!should_have_fast_elements) NormalizeElements(boilerplate);
271
ager@chromium.org32912102009-01-16 10:38:43 +0000272 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000273 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000274 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000275 length / 2,
ager@chromium.org236ad962008-09-25 09:45:57 +0000276 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000277 for (int index = 0; index < length; index +=2) {
278 Handle<Object> key(constant_properties->get(index+0));
279 Handle<Object> value(constant_properties->get(index+1));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000280 if (value->IsFixedArray()) {
281 // The value contains the constant_properties of a
282 // simple object literal.
283 Handle<FixedArray> array = Handle<FixedArray>::cast(value);
284 value = CreateLiteralBoilerplate(literals, array);
285 if (value.is_null()) return value;
286 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000287 Handle<Object> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000288 uint32_t element_index = 0;
289 if (key->IsSymbol()) {
290 // If key is a symbol it is not an array element.
291 Handle<String> name(String::cast(*key));
292 ASSERT(!name->AsArrayIndex(&element_index));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000293 result = SetProperty(boilerplate, name, value, NONE);
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000294 } else if (key->ToArrayIndex(&element_index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000295 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000296 result = SetElement(boilerplate, element_index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000297 } else {
298 // Non-uint32 number.
299 ASSERT(key->IsNumber());
300 double num = key->Number();
301 char arr[100];
302 Vector<char> buffer(arr, ARRAY_SIZE(arr));
303 const char* str = DoubleToCString(num, buffer);
304 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000305 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000306 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000307 // If setting the property on the boilerplate throws an
308 // exception, the exception is converted to an empty handle in
309 // the handle based operations. In that case, we need to
310 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000311 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000312 }
313 }
314
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000315 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000316}
317
318
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000319static Handle<Object> CreateArrayLiteralBoilerplate(
320 Handle<FixedArray> literals,
321 Handle<FixedArray> elements) {
322 // Create the JSArray.
323 Handle<JSFunction> constructor(
324 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
325 Handle<Object> object = Factory::NewJSObject(constructor);
326
327 Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
328
329 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
330 for (int i = 0; i < content->length(); i++) {
331 if (content->get(i)->IsFixedArray()) {
332 // The value contains the constant_properties of a
333 // simple object literal.
334 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
335 Handle<Object> result =
336 CreateLiteralBoilerplate(literals, fa);
337 if (result.is_null()) return result;
338 content->set(i, *result);
339 }
340 }
341
342 // Set the elements.
343 Handle<JSArray>::cast(object)->SetContent(*content);
344 return object;
345}
346
347
348static Handle<Object> CreateLiteralBoilerplate(
349 Handle<FixedArray> literals,
350 Handle<FixedArray> array) {
351 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
352 switch (CompileTimeValue::GetType(array)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000353 case CompileTimeValue::OBJECT_LITERAL_FAST_ELEMENTS:
354 return CreateObjectLiteralBoilerplate(literals, elements, true);
355 case CompileTimeValue::OBJECT_LITERAL_SLOW_ELEMENTS:
356 return CreateObjectLiteralBoilerplate(literals, elements, false);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000357 case CompileTimeValue::ARRAY_LITERAL:
358 return CreateArrayLiteralBoilerplate(literals, elements);
359 default:
360 UNREACHABLE();
361 return Handle<Object>::null();
362 }
363}
364
365
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000366static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000367 // Takes a FixedArray of elements containing the literal elements of
368 // the array literal and produces JSArray with those elements.
369 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000370 // which contains the context from which to get the Array function
371 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000372 HandleScope scope;
373 ASSERT(args.length() == 3);
374 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
375 CONVERT_SMI_CHECKED(literals_index, args[1]);
376 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000377
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000378 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
379 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000380
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000381 // Update the functions literal and return the boilerplate.
382 literals->set(literals_index, *object);
383 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000384}
385
386
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000387static Object* Runtime_CreateObjectLiteral(Arguments args) {
388 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000389 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000390 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
391 CONVERT_SMI_CHECKED(literals_index, args[1]);
392 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000393 CONVERT_SMI_CHECKED(fast_elements, args[3]);
394 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000395
396 // Check if boilerplate exists. If not, create it first.
397 Handle<Object> boilerplate(literals->get(literals_index));
398 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000399 boilerplate = CreateObjectLiteralBoilerplate(literals,
400 constant_properties,
401 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000402 if (boilerplate.is_null()) return Failure::Exception();
403 // Update the functions literal and return the boilerplate.
404 literals->set(literals_index, *boilerplate);
405 }
406 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
407}
408
409
410static Object* Runtime_CreateObjectLiteralShallow(Arguments args) {
411 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000412 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000413 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
414 CONVERT_SMI_CHECKED(literals_index, args[1]);
415 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000416 CONVERT_SMI_CHECKED(fast_elements, args[3]);
417 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000418
419 // Check if boilerplate exists. If not, create it first.
420 Handle<Object> boilerplate(literals->get(literals_index));
421 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000422 boilerplate = CreateObjectLiteralBoilerplate(literals,
423 constant_properties,
424 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000425 if (boilerplate.is_null()) return Failure::Exception();
426 // Update the functions literal and return the boilerplate.
427 literals->set(literals_index, *boilerplate);
428 }
429 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
430}
431
432
433static Object* Runtime_CreateArrayLiteral(Arguments args) {
434 HandleScope scope;
435 ASSERT(args.length() == 3);
436 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
437 CONVERT_SMI_CHECKED(literals_index, args[1]);
438 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
439
440 // Check if boilerplate exists. If not, create it first.
441 Handle<Object> boilerplate(literals->get(literals_index));
442 if (*boilerplate == Heap::undefined_value()) {
443 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
444 if (boilerplate.is_null()) return Failure::Exception();
445 // Update the functions literal and return the boilerplate.
446 literals->set(literals_index, *boilerplate);
447 }
448 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
449}
450
451
452static Object* Runtime_CreateArrayLiteralShallow(Arguments args) {
453 HandleScope scope;
454 ASSERT(args.length() == 3);
455 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
456 CONVERT_SMI_CHECKED(literals_index, args[1]);
457 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
458
459 // Check if boilerplate exists. If not, create it first.
460 Handle<Object> boilerplate(literals->get(literals_index));
461 if (*boilerplate == Heap::undefined_value()) {
462 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
463 if (boilerplate.is_null()) return Failure::Exception();
464 // Update the functions literal and return the boilerplate.
465 literals->set(literals_index, *boilerplate);
466 }
467 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
468}
469
470
ager@chromium.org32912102009-01-16 10:38:43 +0000471static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
472 ASSERT(args.length() == 2);
473 CONVERT_CHECKED(String, key, args[0]);
474 Object* value = args[1];
475 // Create a catch context extension object.
476 JSFunction* constructor =
477 Top::context()->global_context()->context_extension_function();
478 Object* object = Heap::AllocateJSObject(constructor);
479 if (object->IsFailure()) return object;
480 // Assign the exception value to the catch variable and make sure
481 // that the catch variable is DontDelete.
482 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
483 if (value->IsFailure()) return value;
484 return object;
485}
486
487
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000488static Object* Runtime_ClassOf(Arguments args) {
489 NoHandleAllocation ha;
490 ASSERT(args.length() == 1);
491 Object* obj = args[0];
492 if (!obj->IsJSObject()) return Heap::null_value();
493 return JSObject::cast(obj)->class_name();
494}
495
ager@chromium.org7c537e22008-10-16 08:43:32 +0000496
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000497static Object* Runtime_IsInPrototypeChain(Arguments args) {
498 NoHandleAllocation ha;
499 ASSERT(args.length() == 2);
500 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
501 Object* O = args[0];
502 Object* V = args[1];
503 while (true) {
504 Object* prototype = V->GetPrototype();
505 if (prototype->IsNull()) return Heap::false_value();
506 if (O == prototype) return Heap::true_value();
507 V = prototype;
508 }
509}
510
511
ager@chromium.org9085a012009-05-11 19:22:57 +0000512// Inserts an object as the hidden prototype of another object.
513static Object* Runtime_SetHiddenPrototype(Arguments args) {
514 NoHandleAllocation ha;
515 ASSERT(args.length() == 2);
516 CONVERT_CHECKED(JSObject, jsobject, args[0]);
517 CONVERT_CHECKED(JSObject, proto, args[1]);
518
519 // Sanity checks. The old prototype (that we are replacing) could
520 // theoretically be null, but if it is not null then check that we
521 // didn't already install a hidden prototype here.
522 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
523 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
524 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
525
526 // Allocate up front before we start altering state in case we get a GC.
527 Object* map_or_failure = proto->map()->CopyDropTransitions();
528 if (map_or_failure->IsFailure()) return map_or_failure;
529 Map* new_proto_map = Map::cast(map_or_failure);
530
531 map_or_failure = jsobject->map()->CopyDropTransitions();
532 if (map_or_failure->IsFailure()) return map_or_failure;
533 Map* new_map = Map::cast(map_or_failure);
534
535 // Set proto's prototype to be the old prototype of the object.
536 new_proto_map->set_prototype(jsobject->GetPrototype());
537 proto->set_map(new_proto_map);
538 new_proto_map->set_is_hidden_prototype();
539
540 // Set the object's prototype to proto.
541 new_map->set_prototype(proto);
542 jsobject->set_map(new_map);
543
544 return Heap::undefined_value();
545}
546
547
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000548static Object* Runtime_IsConstructCall(Arguments args) {
549 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000550 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000551 JavaScriptFrameIterator it;
552 return Heap::ToBoolean(it.frame()->IsConstructor());
553}
554
555
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000556// Recursively traverses hidden prototypes if property is not found
557static void GetOwnPropertyImplementation(JSObject* obj,
558 String* name,
559 LookupResult* result) {
560 obj->LocalLookupRealNamedProperty(name, result);
561
562 if (!result->IsProperty()) {
563 Object* proto = obj->GetPrototype();
564 if (proto->IsJSObject() &&
565 JSObject::cast(proto)->map()->is_hidden_prototype())
566 GetOwnPropertyImplementation(JSObject::cast(proto),
567 name, result);
568 }
569}
570
571
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000572// Enumerator used as indices into the array returned from GetOwnProperty
573enum PropertyDescriptorIndices {
574 IS_ACCESSOR_INDEX,
575 VALUE_INDEX,
576 GETTER_INDEX,
577 SETTER_INDEX,
578 WRITABLE_INDEX,
579 ENUMERABLE_INDEX,
580 CONFIGURABLE_INDEX,
581 DESCRIPTOR_SIZE
582};
583
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000584// Returns an array with the property description:
585// if args[1] is not a property on args[0]
586// returns undefined
587// if args[1] is a data property on args[0]
588// [false, value, Writeable, Enumerable, Configurable]
589// if args[1] is an accessor on args[0]
590// [true, GetFunction, SetFunction, Enumerable, Configurable]
591static Object* Runtime_GetOwnProperty(Arguments args) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000592 ASSERT(args.length() == 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000593 HandleScope scope;
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000594 Handle<FixedArray> elms = Factory::NewFixedArray(DESCRIPTOR_SIZE);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000595 Handle<JSArray> desc = Factory::NewJSArrayWithElements(elms);
596 LookupResult result;
597 CONVERT_CHECKED(JSObject, obj, args[0]);
598 CONVERT_CHECKED(String, name, args[1]);
599
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000600 // This could be an element.
601 uint32_t index;
602 if (name->AsArrayIndex(&index)) {
603 if (!obj->HasLocalElement(index)) {
604 return Heap::undefined_value();
605 }
606
607 // Special handling of string objects according to ECMAScript 5 15.5.5.2.
608 // Note that this might be a string object with elements other than the
609 // actual string value. This is covered by the subsequent cases.
610 if (obj->IsStringObjectWithCharacterAt(index)) {
611 JSValue* js_value = JSValue::cast(obj);
612 String* str = String::cast(js_value->value());
613 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
614 elms->set(VALUE_INDEX, str->SubString(index, index+1));
615 elms->set(WRITABLE_INDEX, Heap::false_value());
616 elms->set(ENUMERABLE_INDEX, Heap::false_value());
617 elms->set(CONFIGURABLE_INDEX, Heap::false_value());
618 return *desc;
619 }
620
621 // This can potentially be an element in the elements dictionary or
622 // a fast element.
623 if (obj->HasDictionaryElements()) {
624 NumberDictionary* dictionary = obj->element_dictionary();
625 int entry = dictionary->FindEntry(index);
626 PropertyDetails details = dictionary->DetailsAt(entry);
627 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
628 elms->set(VALUE_INDEX, dictionary->ValueAt(entry));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000629 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!details.IsReadOnly()));
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000630 elms->set(ENUMERABLE_INDEX, Heap::ToBoolean(!details.IsDontEnum()));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000631 elms->set(CONFIGURABLE_INDEX, Heap::ToBoolean(!details.IsDontDelete()));
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000632 return *desc;
633 } else {
634 // Elements that are stored as array elements always has:
635 // writable: true, configurable: true, enumerable: true.
636 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
637 elms->set(VALUE_INDEX, obj->GetElement(index));
638 elms->set(WRITABLE_INDEX, Heap::true_value());
639 elms->set(ENUMERABLE_INDEX, Heap::true_value());
640 elms->set(CONFIGURABLE_INDEX, Heap::true_value());
641 return *desc;
642 }
643 }
644
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000645 // Use recursive implementation to also traverse hidden prototypes
646 GetOwnPropertyImplementation(obj, name, &result);
647
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000648 if (!result.IsProperty()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000649 return Heap::undefined_value();
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000650 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000651 if (result.type() == CALLBACKS) {
652 Object* structure = result.GetCallbackObject();
ager@chromium.org5c838252010-02-19 08:53:10 +0000653 if (structure->IsProxy() || structure->IsAccessorInfo()) {
654 // Property that is internally implemented as a callback or
655 // an API defined callback.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000656 Object* value = obj->GetPropertyWithCallback(
657 obj, structure, name, result.holder());
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000658 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
659 elms->set(VALUE_INDEX, value);
660 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!result.IsReadOnly()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000661 } else if (structure->IsFixedArray()) {
662 // __defineGetter__/__defineSetter__ callback.
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000663 elms->set(IS_ACCESSOR_INDEX, Heap::true_value());
664 elms->set(GETTER_INDEX, FixedArray::cast(structure)->get(0));
665 elms->set(SETTER_INDEX, FixedArray::cast(structure)->get(1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000666 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000667 return Heap::undefined_value();
668 }
669 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000670 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
671 elms->set(VALUE_INDEX, result.GetLazyValue());
672 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!result.IsReadOnly()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000673 }
674
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000675 elms->set(ENUMERABLE_INDEX, Heap::ToBoolean(!result.IsDontEnum()));
676 elms->set(CONFIGURABLE_INDEX, Heap::ToBoolean(!result.IsDontDelete()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000677 return *desc;
678}
679
680
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000681static Object* Runtime_IsExtensible(Arguments args) {
682 ASSERT(args.length() == 1);
683 CONVERT_CHECKED(JSObject, obj, args[0]);
684 return obj->map()->is_extensible() ? Heap::true_value()
685 : Heap::false_value();
686}
687
688
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000689static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000690 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000691 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000692 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
693 CONVERT_ARG_CHECKED(String, pattern, 1);
694 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000695 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
696 if (result.is_null()) return Failure::Exception();
697 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000698}
699
700
701static Object* Runtime_CreateApiFunction(Arguments args) {
702 HandleScope scope;
703 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000704 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000705 return *Factory::CreateApiFunction(data);
706}
707
708
709static Object* Runtime_IsTemplate(Arguments args) {
710 ASSERT(args.length() == 1);
711 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000712 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000713 return Heap::ToBoolean(result);
714}
715
716
717static Object* Runtime_GetTemplateField(Arguments args) {
718 ASSERT(args.length() == 2);
719 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000720 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000721 int index = field->value();
722 int offset = index * kPointerSize + HeapObject::kHeaderSize;
723 InstanceType type = templ->map()->instance_type();
724 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
725 type == OBJECT_TEMPLATE_INFO_TYPE);
726 RUNTIME_ASSERT(offset > 0);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000727 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000728 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
729 } else {
730 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
731 }
732 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000733}
734
735
ager@chromium.org870a0b62008-11-04 11:43:05 +0000736static Object* Runtime_DisableAccessChecks(Arguments args) {
737 ASSERT(args.length() == 1);
738 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000739 Map* old_map = object->map();
740 bool needs_access_checks = old_map->is_access_check_needed();
741 if (needs_access_checks) {
742 // Copy map so it won't interfere constructor's initial map.
743 Object* new_map = old_map->CopyDropTransitions();
744 if (new_map->IsFailure()) return new_map;
745
746 Map::cast(new_map)->set_is_access_check_needed(false);
747 object->set_map(Map::cast(new_map));
748 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000749 return needs_access_checks ? Heap::true_value() : Heap::false_value();
750}
751
752
753static Object* Runtime_EnableAccessChecks(Arguments args) {
754 ASSERT(args.length() == 1);
755 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000756 Map* old_map = object->map();
757 if (!old_map->is_access_check_needed()) {
758 // Copy map so it won't interfere constructor's initial map.
759 Object* new_map = old_map->CopyDropTransitions();
760 if (new_map->IsFailure()) return new_map;
761
762 Map::cast(new_map)->set_is_access_check_needed(true);
763 object->set_map(Map::cast(new_map));
764 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000765 return Heap::undefined_value();
766}
767
768
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000769static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
770 HandleScope scope;
771 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
772 Handle<Object> args[2] = { type_handle, name };
773 Handle<Object> error =
774 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
775 return Top::Throw(*error);
776}
777
778
779static Object* Runtime_DeclareGlobals(Arguments args) {
780 HandleScope scope;
781 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
782
ager@chromium.org3811b432009-10-28 14:53:37 +0000783 Handle<Context> context = args.at<Context>(0);
784 CONVERT_ARG_CHECKED(FixedArray, pairs, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000785 bool is_eval = Smi::cast(args[2])->value() == 1;
786
787 // Compute the property attributes. According to ECMA-262, section
788 // 13, page 71, the property must be read-only and
789 // non-deletable. However, neither SpiderMonkey nor KJS creates the
790 // property as read-only, so we don't either.
791 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
792
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000793 // Traverse the name/value pairs and set the properties.
794 int length = pairs->length();
795 for (int i = 0; i < length; i += 2) {
796 HandleScope scope;
797 Handle<String> name(String::cast(pairs->get(i)));
798 Handle<Object> value(pairs->get(i + 1));
799
800 // We have to declare a global const property. To capture we only
801 // assign to it when evaluating the assignment for "const x =
802 // <expr>" the initial value is the hole.
803 bool is_const_property = value->IsTheHole();
804
805 if (value->IsUndefined() || is_const_property) {
806 // Lookup the property in the global object, and don't set the
807 // value of the variable if the property is already there.
808 LookupResult lookup;
809 global->Lookup(*name, &lookup);
810 if (lookup.IsProperty()) {
811 // Determine if the property is local by comparing the holder
812 // against the global object. The information will be used to
813 // avoid throwing re-declaration errors when declaring
814 // variables or constants that exist in the prototype chain.
815 bool is_local = (*global == lookup.holder());
816 // Get the property attributes and determine if the property is
817 // read-only.
818 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
819 bool is_read_only = (attributes & READ_ONLY) != 0;
820 if (lookup.type() == INTERCEPTOR) {
821 // If the interceptor says the property is there, we
822 // just return undefined without overwriting the property.
823 // Otherwise, we continue to setting the property.
824 if (attributes != ABSENT) {
825 // Check if the existing property conflicts with regards to const.
826 if (is_local && (is_read_only || is_const_property)) {
827 const char* type = (is_read_only) ? "const" : "var";
828 return ThrowRedeclarationError(type, name);
829 };
830 // The property already exists without conflicting: Go to
831 // the next declaration.
832 continue;
833 }
834 // Fall-through and introduce the absent property by using
835 // SetProperty.
836 } else {
837 if (is_local && (is_read_only || is_const_property)) {
838 const char* type = (is_read_only) ? "const" : "var";
839 return ThrowRedeclarationError(type, name);
840 }
841 // The property already exists without conflicting: Go to
842 // the next declaration.
843 continue;
844 }
845 }
846 } else {
847 // Copy the function and update its context. Use it as value.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000848 Handle<SharedFunctionInfo> shared =
849 Handle<SharedFunctionInfo>::cast(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000850 Handle<JSFunction> function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000851 Factory::NewFunctionFromSharedFunctionInfo(shared, context, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000852 value = function;
853 }
854
855 LookupResult lookup;
856 global->LocalLookup(*name, &lookup);
857
858 PropertyAttributes attributes = is_const_property
859 ? static_cast<PropertyAttributes>(base | READ_ONLY)
860 : base;
861
862 if (lookup.IsProperty()) {
863 // There's a local property that we need to overwrite because
864 // we're either declaring a function or there's an interceptor
865 // that claims the property is absent.
866
867 // Check for conflicting re-declarations. We cannot have
868 // conflicting types in case of intercepted properties because
869 // they are absent.
870 if (lookup.type() != INTERCEPTOR &&
871 (lookup.IsReadOnly() || is_const_property)) {
872 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
873 return ThrowRedeclarationError(type, name);
874 }
875 SetProperty(global, name, value, attributes);
876 } else {
877 // If a property with this name does not already exist on the
878 // global object add the property locally. We take special
879 // precautions to always add it as a local property even in case
880 // of callbacks in the prototype chain (this rules out using
881 // SetProperty). Also, we must use the handle-based version to
882 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000883 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000884 }
885 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000886
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000887 return Heap::undefined_value();
888}
889
890
891static Object* Runtime_DeclareContextSlot(Arguments args) {
892 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000893 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000894
ager@chromium.org7c537e22008-10-16 08:43:32 +0000895 CONVERT_ARG_CHECKED(Context, context, 0);
896 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000897 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000898 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000899 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000900 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000901
902 // Declarations are always done in the function context.
903 context = Handle<Context>(context->fcontext());
904
905 int index;
906 PropertyAttributes attributes;
907 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000908 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000909 context->Lookup(name, flags, &index, &attributes);
910
911 if (attributes != ABSENT) {
912 // The name was declared before; check for conflicting
913 // re-declarations: This is similar to the code in parser.cc in
914 // the AstBuildingParser::Declare function.
915 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
916 // Functions are not read-only.
917 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
918 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
919 return ThrowRedeclarationError(type, name);
920 }
921
922 // Initialize it if necessary.
923 if (*initial_value != NULL) {
924 if (index >= 0) {
925 // The variable or constant context slot should always be in
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000926 // the function context or the arguments object.
927 if (holder->IsContext()) {
928 ASSERT(holder.is_identical_to(context));
929 if (((attributes & READ_ONLY) == 0) ||
930 context->get(index)->IsTheHole()) {
931 context->set(index, *initial_value);
932 }
933 } else {
934 Handle<JSObject>::cast(holder)->SetElement(index, *initial_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000935 }
936 } else {
937 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000938 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000939 SetProperty(context_ext, name, initial_value, mode);
940 }
941 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000942
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000943 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000944 // The property is not in the function context. It needs to be
945 // "declared" in the function context's extension context, or in the
946 // global context.
947 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000948 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000949 // The function context's extension context exists - use it.
950 context_ext = Handle<JSObject>(context->extension());
951 } else {
952 // The function context's extension context does not exists - allocate
953 // it.
954 context_ext = Factory::NewJSObject(Top::context_extension_function());
955 // And store it in the extension slot.
956 context->set_extension(*context_ext);
957 }
958 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000959
ager@chromium.org7c537e22008-10-16 08:43:32 +0000960 // Declare the property by setting it to the initial value if provided,
961 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
962 // constant declarations).
963 ASSERT(!context_ext->HasLocalProperty(*name));
964 Handle<Object> value(Heap::undefined_value());
965 if (*initial_value != NULL) value = initial_value;
966 SetProperty(context_ext, name, value, mode);
967 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
968 }
969
970 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000971}
972
973
974static Object* Runtime_InitializeVarGlobal(Arguments args) {
975 NoHandleAllocation nha;
976
977 // Determine if we need to assign to the variable if it already
978 // exists (based on the number of arguments).
979 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
980 bool assign = args.length() == 2;
981
982 CONVERT_ARG_CHECKED(String, name, 0);
983 GlobalObject* global = Top::context()->global();
984
985 // According to ECMA-262, section 12.2, page 62, the property must
986 // not be deletable.
987 PropertyAttributes attributes = DONT_DELETE;
988
989 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000990 // there, there is a property with this name in the prototype chain.
991 // We follow Safari and Firefox behavior and only set the property
992 // locally if there is an explicit initialization value that we have
993 // to assign to the property. When adding the property we take
994 // special precautions to always add it as a local property even in
995 // case of callbacks in the prototype chain (this rules out using
996 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
997 // this.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000998 // Note that objects can have hidden prototypes, so we need to traverse
999 // the whole chain of hidden prototypes to do a 'local' lookup.
1000 JSObject* real_holder = global;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001001 LookupResult lookup;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001002 while (true) {
1003 real_holder->LocalLookup(*name, &lookup);
1004 if (lookup.IsProperty()) {
1005 // Determine if this is a redeclaration of something read-only.
1006 if (lookup.IsReadOnly()) {
1007 // If we found readonly property on one of hidden prototypes,
1008 // just shadow it.
1009 if (real_holder != Top::context()->global()) break;
1010 return ThrowRedeclarationError("const", name);
1011 }
1012
1013 // Determine if this is a redeclaration of an intercepted read-only
1014 // property and figure out if the property exists at all.
1015 bool found = true;
1016 PropertyType type = lookup.type();
1017 if (type == INTERCEPTOR) {
1018 HandleScope handle_scope;
1019 Handle<JSObject> holder(real_holder);
1020 PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
1021 real_holder = *holder;
1022 if (intercepted == ABSENT) {
1023 // The interceptor claims the property isn't there. We need to
1024 // make sure to introduce it.
1025 found = false;
1026 } else if ((intercepted & READ_ONLY) != 0) {
1027 // The property is present, but read-only. Since we're trying to
1028 // overwrite it with a variable declaration we must throw a
1029 // re-declaration error. However if we found readonly property
1030 // on one of hidden prototypes, just shadow it.
1031 if (real_holder != Top::context()->global()) break;
1032 return ThrowRedeclarationError("const", name);
1033 }
1034 }
1035
1036 if (found && !assign) {
1037 // The global property is there and we're not assigning any value
1038 // to it. Just return.
1039 return Heap::undefined_value();
1040 }
1041
1042 // Assign the value (or undefined) to the property.
1043 Object* value = (assign) ? args[1] : Heap::undefined_value();
1044 return real_holder->SetProperty(&lookup, *name, value, attributes);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001045 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001046
1047 Object* proto = real_holder->GetPrototype();
1048 if (!proto->IsJSObject())
1049 break;
1050
1051 if (!JSObject::cast(proto)->map()->is_hidden_prototype())
1052 break;
1053
1054 real_holder = JSObject::cast(proto);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001055 }
1056
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001057 global = Top::context()->global();
1058 if (assign) {
1059 return global->IgnoreAttributesAndSetLocalProperty(*name,
1060 args[1],
1061 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001062 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001063 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001064}
1065
1066
1067static Object* Runtime_InitializeConstGlobal(Arguments args) {
1068 // All constants are declared with an initial value. The name
1069 // of the constant is the first argument and the initial value
1070 // is the second.
1071 RUNTIME_ASSERT(args.length() == 2);
1072 CONVERT_ARG_CHECKED(String, name, 0);
1073 Handle<Object> value = args.at<Object>(1);
1074
1075 // Get the current global object from top.
1076 GlobalObject* global = Top::context()->global();
1077
1078 // According to ECMA-262, section 12.2, page 62, the property must
1079 // not be deletable. Since it's a const, it must be READ_ONLY too.
1080 PropertyAttributes attributes =
1081 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
1082
1083 // Lookup the property locally in the global object. If it isn't
1084 // there, we add the property and take special precautions to always
1085 // add it as a local property even in case of callbacks in the
1086 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001087 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001088 LookupResult lookup;
1089 global->LocalLookup(*name, &lookup);
1090 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001091 return global->IgnoreAttributesAndSetLocalProperty(*name,
1092 *value,
1093 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001094 }
1095
1096 // Determine if this is a redeclaration of something not
1097 // read-only. In case the result is hidden behind an interceptor we
1098 // need to ask it for the property attributes.
1099 if (!lookup.IsReadOnly()) {
1100 if (lookup.type() != INTERCEPTOR) {
1101 return ThrowRedeclarationError("var", name);
1102 }
1103
1104 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
1105
1106 // Throw re-declaration error if the intercepted property is present
1107 // but not read-only.
1108 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
1109 return ThrowRedeclarationError("var", name);
1110 }
1111
1112 // Restore global object from context (in case of GC) and continue
1113 // with setting the value because the property is either absent or
1114 // read-only. We also have to do redo the lookup.
1115 global = Top::context()->global();
1116
1117 // BUG 1213579: Handle the case where we have to set a read-only
1118 // property through an interceptor and only do it if it's
1119 // uninitialized, e.g. the hole. Nirk...
1120 global->SetProperty(*name, *value, attributes);
1121 return *value;
1122 }
1123
1124 // Set the value, but only we're assigning the initial value to a
1125 // constant. For now, we determine this by checking if the
1126 // current value is the hole.
1127 PropertyType type = lookup.type();
1128 if (type == FIELD) {
1129 FixedArray* properties = global->properties();
1130 int index = lookup.GetFieldIndex();
1131 if (properties->get(index)->IsTheHole()) {
1132 properties->set(index, *value);
1133 }
1134 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001135 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
1136 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001137 }
1138 } else {
1139 // Ignore re-initialization of constants that have already been
1140 // assigned a function value.
1141 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
1142 }
1143
1144 // Use the set value as the result of the operation.
1145 return *value;
1146}
1147
1148
1149static Object* Runtime_InitializeConstContextSlot(Arguments args) {
1150 HandleScope scope;
1151 ASSERT(args.length() == 3);
1152
1153 Handle<Object> value(args[0]);
1154 ASSERT(!value->IsTheHole());
1155 CONVERT_ARG_CHECKED(Context, context, 1);
1156 Handle<String> name(String::cast(args[2]));
1157
1158 // Initializations are always done in the function context.
1159 context = Handle<Context>(context->fcontext());
1160
1161 int index;
1162 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001163 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001164 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001165 context->Lookup(name, flags, &index, &attributes);
1166
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001167 // In most situations, the property introduced by the const
1168 // declaration should be present in the context extension object.
1169 // However, because declaration and initialization are separate, the
1170 // property might have been deleted (if it was introduced by eval)
1171 // before we reach the initialization point.
1172 //
1173 // Example:
1174 //
1175 // function f() { eval("delete x; const x;"); }
1176 //
1177 // In that case, the initialization behaves like a normal assignment
1178 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001179 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001180 // Property was found in a context.
1181 if (holder->IsContext()) {
1182 // The holder cannot be the function context. If it is, there
1183 // should have been a const redeclaration error when declaring
1184 // the const property.
1185 ASSERT(!holder.is_identical_to(context));
1186 if ((attributes & READ_ONLY) == 0) {
1187 Handle<Context>::cast(holder)->set(index, *value);
1188 }
1189 } else {
1190 // The holder is an arguments object.
1191 ASSERT((attributes & READ_ONLY) == 0);
1192 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001193 }
1194 return *value;
1195 }
1196
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001197 // The property could not be found, we introduce it in the global
1198 // context.
1199 if (attributes == ABSENT) {
1200 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1201 SetProperty(global, name, value, NONE);
1202 return *value;
1203 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001204
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001205 // The property was present in a context extension object.
1206 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001207
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001208 if (*context_ext == context->extension()) {
1209 // This is the property that was introduced by the const
1210 // declaration. Set it if it hasn't been set before. NOTE: We
1211 // cannot use GetProperty() to get the current value as it
1212 // 'unholes' the value.
1213 LookupResult lookup;
1214 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1215 ASSERT(lookup.IsProperty()); // the property was declared
1216 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1217
1218 PropertyType type = lookup.type();
1219 if (type == FIELD) {
1220 FixedArray* properties = context_ext->properties();
1221 int index = lookup.GetFieldIndex();
1222 if (properties->get(index)->IsTheHole()) {
1223 properties->set(index, *value);
1224 }
1225 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001226 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
1227 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001228 }
1229 } else {
1230 // We should not reach here. Any real, named property should be
1231 // either a field or a dictionary slot.
1232 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001233 }
1234 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001235 // The property was found in a different context extension object.
1236 // Set it if it is not a read-only property.
1237 if ((attributes & READ_ONLY) == 0) {
1238 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1239 // Setting a property might throw an exception. Exceptions
1240 // are converted to empty handles in handle operations. We
1241 // need to convert back to exceptions here.
1242 if (set.is_null()) {
1243 ASSERT(Top::has_pending_exception());
1244 return Failure::Exception();
1245 }
1246 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001247 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001248
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001249 return *value;
1250}
1251
1252
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001253static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1254 Arguments args) {
1255 HandleScope scope;
1256 ASSERT(args.length() == 2);
1257 CONVERT_ARG_CHECKED(JSObject, object, 0);
1258 CONVERT_SMI_CHECKED(properties, args[1]);
1259 if (object->HasFastProperties()) {
1260 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1261 }
1262 return *object;
1263}
1264
1265
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001266static Object* Runtime_RegExpExec(Arguments args) {
1267 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001268 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001269 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1270 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001271 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001272 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001273 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001274 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001275 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001276 RUNTIME_ASSERT(index >= 0);
1277 RUNTIME_ASSERT(index <= subject->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001278 Counters::regexp_entry_runtime.Increment();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001279 Handle<Object> result = RegExpImpl::Exec(regexp,
1280 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001281 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001282 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001283 if (result.is_null()) return Failure::Exception();
1284 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001285}
1286
1287
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00001288static Object* Runtime_RegExpConstructResult(Arguments args) {
1289 ASSERT(args.length() == 3);
1290 CONVERT_SMI_CHECKED(elements_count, args[0]);
1291 if (elements_count > JSArray::kMaxFastElementsLength) {
1292 return Top::ThrowIllegalOperation();
1293 }
1294 Object* new_object = Heap::AllocateFixedArrayWithHoles(elements_count);
1295 if (new_object->IsFailure()) return new_object;
1296 FixedArray* elements = FixedArray::cast(new_object);
1297 new_object = Heap::AllocateRaw(JSRegExpResult::kSize,
1298 NEW_SPACE,
1299 OLD_POINTER_SPACE);
1300 if (new_object->IsFailure()) return new_object;
1301 {
1302 AssertNoAllocation no_gc;
1303 HandleScope scope;
1304 reinterpret_cast<HeapObject*>(new_object)->
1305 set_map(Top::global_context()->regexp_result_map());
1306 }
1307 JSArray* array = JSArray::cast(new_object);
1308 array->set_properties(Heap::empty_fixed_array());
1309 array->set_elements(elements);
1310 array->set_length(Smi::FromInt(elements_count));
1311 // Write in-object properties after the length of the array.
1312 array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, args[1]);
1313 array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, args[2]);
1314 return array;
1315}
1316
1317
lrn@chromium.org25156de2010-04-06 13:10:27 +00001318static Object* Runtime_RegExpInitializeObject(Arguments args) {
1319 AssertNoAllocation no_alloc;
1320 ASSERT(args.length() == 5);
1321 CONVERT_CHECKED(JSRegExp, regexp, args[0]);
1322 CONVERT_CHECKED(String, source, args[1]);
1323
1324 Object* global = args[2];
1325 if (!global->IsTrue()) global = Heap::false_value();
1326
1327 Object* ignoreCase = args[3];
1328 if (!ignoreCase->IsTrue()) ignoreCase = Heap::false_value();
1329
1330 Object* multiline = args[4];
1331 if (!multiline->IsTrue()) multiline = Heap::false_value();
1332
1333 Map* map = regexp->map();
1334 Object* constructor = map->constructor();
1335 if (constructor->IsJSFunction() &&
1336 JSFunction::cast(constructor)->initial_map() == map) {
1337 // If we still have the original map, set in-object properties directly.
1338 regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, source);
1339 // TODO(lrn): Consider skipping write barrier on booleans as well.
1340 // Both true and false should be in oldspace at all times.
1341 regexp->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, global);
1342 regexp->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex, ignoreCase);
1343 regexp->InObjectPropertyAtPut(JSRegExp::kMultilineFieldIndex, multiline);
1344 regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex,
1345 Smi::FromInt(0),
1346 SKIP_WRITE_BARRIER);
1347 return regexp;
1348 }
1349
1350 // Map has changed, so use generic, but slower, method.
1351 PropertyAttributes final =
1352 static_cast<PropertyAttributes>(READ_ONLY | DONT_ENUM | DONT_DELETE);
1353 PropertyAttributes writable =
1354 static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
1355 regexp->IgnoreAttributesAndSetLocalProperty(Heap::source_symbol(),
1356 source,
1357 final);
1358 regexp->IgnoreAttributesAndSetLocalProperty(Heap::global_symbol(),
1359 global,
1360 final);
1361 regexp->IgnoreAttributesAndSetLocalProperty(Heap::ignore_case_symbol(),
1362 ignoreCase,
1363 final);
1364 regexp->IgnoreAttributesAndSetLocalProperty(Heap::multiline_symbol(),
1365 multiline,
1366 final);
1367 regexp->IgnoreAttributesAndSetLocalProperty(Heap::last_index_symbol(),
1368 Smi::FromInt(0),
1369 writable);
1370 return regexp;
1371}
1372
1373
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001374static Object* Runtime_FinishArrayPrototypeSetup(Arguments args) {
1375 HandleScope scope;
1376 ASSERT(args.length() == 1);
1377 CONVERT_ARG_CHECKED(JSArray, prototype, 0);
1378 // This is necessary to enable fast checks for absence of elements
1379 // on Array.prototype and below.
1380 prototype->set_elements(Heap::empty_fixed_array());
1381 return Smi::FromInt(0);
1382}
1383
1384
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001385static Handle<JSFunction> InstallBuiltin(Handle<JSObject> holder,
1386 const char* name,
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001387 Builtins::Name builtin_name) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001388 Handle<String> key = Factory::LookupAsciiSymbol(name);
1389 Handle<Code> code(Builtins::builtin(builtin_name));
1390 Handle<JSFunction> optimized = Factory::NewFunction(key,
1391 JS_OBJECT_TYPE,
1392 JSObject::kHeaderSize,
1393 code,
1394 false);
1395 optimized->shared()->DontAdaptArguments();
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001396 SetProperty(holder, key, optimized, NONE);
1397 return optimized;
1398}
1399
1400
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001401static Object* Runtime_SpecialArrayFunctions(Arguments args) {
1402 HandleScope scope;
1403 ASSERT(args.length() == 1);
1404 CONVERT_ARG_CHECKED(JSObject, holder, 0);
1405
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001406 InstallBuiltin(holder, "pop", Builtins::ArrayPop);
1407 InstallBuiltin(holder, "push", Builtins::ArrayPush);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001408 InstallBuiltin(holder, "shift", Builtins::ArrayShift);
1409 InstallBuiltin(holder, "unshift", Builtins::ArrayUnshift);
1410 InstallBuiltin(holder, "slice", Builtins::ArraySlice);
1411 InstallBuiltin(holder, "splice", Builtins::ArraySplice);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001412 InstallBuiltin(holder, "concat", Builtins::ArrayConcat);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001413
1414 return *holder;
1415}
1416
1417
ager@chromium.org357bf652010-04-12 11:30:10 +00001418static Object* Runtime_GetGlobalReceiver(Arguments args) {
1419 // Returns a real global receiver, not one of builtins object.
1420 Context* global_context = Top::context()->global()->global_context();
1421 return global_context->global()->global_receiver();
1422}
1423
1424
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001425static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1426 HandleScope scope;
1427 ASSERT(args.length() == 4);
1428 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1429 int index = Smi::cast(args[1])->value();
1430 Handle<String> pattern = args.at<String>(2);
1431 Handle<String> flags = args.at<String>(3);
1432
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001433 // Get the RegExp function from the context in the literals array.
1434 // This is the RegExp function from the context in which the
1435 // function was created. We do not use the RegExp function from the
1436 // current global context because this might be the RegExp function
1437 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001438 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001439 Handle<JSFunction>(
1440 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001441 // Compute the regular expression literal.
1442 bool has_pending_exception;
1443 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001444 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1445 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001446 if (has_pending_exception) {
1447 ASSERT(Top::has_pending_exception());
1448 return Failure::Exception();
1449 }
1450 literals->set(index, *regexp);
1451 return *regexp;
1452}
1453
1454
1455static Object* Runtime_FunctionGetName(Arguments args) {
1456 NoHandleAllocation ha;
1457 ASSERT(args.length() == 1);
1458
1459 CONVERT_CHECKED(JSFunction, f, args[0]);
1460 return f->shared()->name();
1461}
1462
1463
ager@chromium.org236ad962008-09-25 09:45:57 +00001464static Object* Runtime_FunctionSetName(Arguments args) {
1465 NoHandleAllocation ha;
1466 ASSERT(args.length() == 2);
1467
1468 CONVERT_CHECKED(JSFunction, f, args[0]);
1469 CONVERT_CHECKED(String, name, args[1]);
1470 f->shared()->set_name(name);
1471 return Heap::undefined_value();
1472}
1473
1474
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001475static Object* Runtime_FunctionRemovePrototype(Arguments args) {
1476 NoHandleAllocation ha;
1477 ASSERT(args.length() == 1);
1478
1479 CONVERT_CHECKED(JSFunction, f, args[0]);
1480 Object* obj = f->RemovePrototype();
1481 if (obj->IsFailure()) return obj;
1482
1483 return Heap::undefined_value();
1484}
1485
1486
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001487static Object* Runtime_FunctionGetScript(Arguments args) {
1488 HandleScope scope;
1489 ASSERT(args.length() == 1);
1490
1491 CONVERT_CHECKED(JSFunction, fun, args[0]);
1492 Handle<Object> script = Handle<Object>(fun->shared()->script());
1493 if (!script->IsScript()) return Heap::undefined_value();
1494
1495 return *GetScriptWrapper(Handle<Script>::cast(script));
1496}
1497
1498
1499static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1500 NoHandleAllocation ha;
1501 ASSERT(args.length() == 1);
1502
1503 CONVERT_CHECKED(JSFunction, f, args[0]);
1504 return f->shared()->GetSourceCode();
1505}
1506
1507
1508static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1509 NoHandleAllocation ha;
1510 ASSERT(args.length() == 1);
1511
1512 CONVERT_CHECKED(JSFunction, fun, args[0]);
1513 int pos = fun->shared()->start_position();
1514 return Smi::FromInt(pos);
1515}
1516
1517
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001518static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1519 ASSERT(args.length() == 2);
1520
1521 CONVERT_CHECKED(JSFunction, fun, args[0]);
1522 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1523
1524 Code* code = fun->code();
1525 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1526
1527 Address pc = code->address() + offset;
1528 return Smi::FromInt(fun->code()->SourcePosition(pc));
1529}
1530
1531
1532
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001533static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1534 NoHandleAllocation ha;
1535 ASSERT(args.length() == 2);
1536
1537 CONVERT_CHECKED(JSFunction, fun, args[0]);
1538 CONVERT_CHECKED(String, name, args[1]);
1539 fun->SetInstanceClassName(name);
1540 return Heap::undefined_value();
1541}
1542
1543
1544static Object* Runtime_FunctionSetLength(Arguments args) {
1545 NoHandleAllocation ha;
1546 ASSERT(args.length() == 2);
1547
1548 CONVERT_CHECKED(JSFunction, fun, args[0]);
1549 CONVERT_CHECKED(Smi, length, args[1]);
1550 fun->shared()->set_length(length->value());
1551 return length;
1552}
1553
1554
1555static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001556 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001557 ASSERT(args.length() == 2);
1558
1559 CONVERT_CHECKED(JSFunction, fun, args[0]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001560 ASSERT(fun->should_have_prototype());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001561 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1562 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001563 return args[0]; // return TOS
1564}
1565
1566
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001567static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1568 NoHandleAllocation ha;
1569 ASSERT(args.length() == 1);
1570
1571 CONVERT_CHECKED(JSFunction, f, args[0]);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001572 return f->shared()->IsApiFunction() ? Heap::true_value()
1573 : Heap::false_value();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001574}
1575
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001576static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1577 NoHandleAllocation ha;
1578 ASSERT(args.length() == 1);
1579
1580 CONVERT_CHECKED(JSFunction, f, args[0]);
1581 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1582}
1583
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001584
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001585static Object* Runtime_SetCode(Arguments args) {
1586 HandleScope scope;
1587 ASSERT(args.length() == 2);
1588
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001589 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001590 Handle<Object> code = args.at<Object>(1);
1591
1592 Handle<Context> context(target->context());
1593
1594 if (!code->IsNull()) {
1595 RUNTIME_ASSERT(code->IsJSFunction());
1596 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001597 Handle<SharedFunctionInfo> shared(fun->shared());
1598 SetExpectedNofProperties(target, shared->expected_nof_properties());
1599
1600 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001601 return Failure::Exception();
1602 }
1603 // Set the code, formal parameter count, and the length of the target
1604 // function.
1605 target->set_code(fun->code());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001606 target->shared()->set_length(shared->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001607 target->shared()->set_formal_parameter_count(
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001608 shared->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001609 // Set the source code of the target function to undefined.
1610 // SetCode is only used for built-in constructors like String,
1611 // Array, and Object, and some web code
1612 // doesn't like seeing source code for constructors.
1613 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001614 // Clear the optimization hints related to the compiled code as these are no
1615 // longer valid when the code is overwritten.
1616 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001617 context = Handle<Context>(fun->context());
1618
1619 // Make sure we get a fresh copy of the literal vector to avoid
1620 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001621 int number_of_literals = fun->NumberOfLiterals();
1622 Handle<FixedArray> literals =
1623 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001624 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001625 // Insert the object, regexp and array functions in the literals
1626 // array prefix. These are the functions that will be used when
1627 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001628 literals->set(JSFunction::kLiteralGlobalContextIndex,
1629 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001630 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001631 // It's okay to skip the write barrier here because the literals
1632 // are guaranteed to be in old space.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001633 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001634 }
1635
1636 target->set_context(*context);
1637 return *target;
1638}
1639
1640
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001641static Object* CharFromCode(Object* char_code) {
1642 uint32_t code;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001643 if (char_code->ToArrayIndex(&code)) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001644 if (code <= 0xffff) {
1645 return Heap::LookupSingleCharacterStringFromCode(code);
1646 }
1647 }
1648 return Heap::empty_string();
1649}
1650
1651
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001652static Object* Runtime_StringCharCodeAt(Arguments args) {
1653 NoHandleAllocation ha;
1654 ASSERT(args.length() == 2);
1655
1656 CONVERT_CHECKED(String, subject, args[0]);
1657 Object* index = args[1];
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001658 RUNTIME_ASSERT(index->IsNumber());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001659
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001660 uint32_t i = 0;
1661 if (index->IsSmi()) {
1662 int value = Smi::cast(index)->value();
1663 if (value < 0) return Heap::nan_value();
1664 i = value;
1665 } else {
1666 ASSERT(index->IsHeapNumber());
1667 double value = HeapNumber::cast(index)->value();
1668 i = static_cast<uint32_t>(DoubleToInteger(value));
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001669 }
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001670
1671 // Flatten the string. If someone wants to get a char at an index
1672 // in a cons string, it is likely that more indices will be
1673 // accessed.
1674 Object* flat = subject->TryFlatten();
1675 if (flat->IsFailure()) return flat;
1676 subject = String::cast(flat);
1677
1678 if (i >= static_cast<uint32_t>(subject->length())) {
1679 return Heap::nan_value();
1680 }
1681
1682 return Smi::FromInt(subject->Get(i));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001683}
1684
1685
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001686static Object* Runtime_CharFromCode(Arguments args) {
1687 NoHandleAllocation ha;
1688 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001689 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001690}
1691
lrn@chromium.org25156de2010-04-06 13:10:27 +00001692
1693class FixedArrayBuilder {
1694 public:
1695 explicit FixedArrayBuilder(int initial_capacity)
1696 : array_(Factory::NewFixedArrayWithHoles(initial_capacity)),
1697 length_(0) {
1698 // Require a non-zero initial size. Ensures that doubling the size to
1699 // extend the array will work.
1700 ASSERT(initial_capacity > 0);
1701 }
1702
1703 explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
1704 : array_(backing_store),
1705 length_(0) {
1706 // Require a non-zero initial size. Ensures that doubling the size to
1707 // extend the array will work.
1708 ASSERT(backing_store->length() > 0);
1709 }
1710
1711 bool HasCapacity(int elements) {
1712 int length = array_->length();
1713 int required_length = length_ + elements;
1714 return (length >= required_length);
1715 }
1716
1717 void EnsureCapacity(int elements) {
1718 int length = array_->length();
1719 int required_length = length_ + elements;
1720 if (length < required_length) {
1721 int new_length = length;
1722 do {
1723 new_length *= 2;
1724 } while (new_length < required_length);
1725 Handle<FixedArray> extended_array =
1726 Factory::NewFixedArrayWithHoles(new_length);
1727 array_->CopyTo(0, *extended_array, 0, length_);
1728 array_ = extended_array;
1729 }
1730 }
1731
1732 void Add(Object* value) {
1733 ASSERT(length_ < capacity());
1734 array_->set(length_, value);
1735 length_++;
1736 }
1737
1738 void Add(Smi* value) {
1739 ASSERT(length_ < capacity());
1740 array_->set(length_, value);
1741 length_++;
1742 }
1743
1744 Handle<FixedArray> array() {
1745 return array_;
1746 }
1747
1748 int length() {
1749 return length_;
1750 }
1751
1752 int capacity() {
1753 return array_->length();
1754 }
1755
1756 Handle<JSArray> ToJSArray() {
1757 Handle<JSArray> result_array = Factory::NewJSArrayWithElements(array_);
1758 result_array->set_length(Smi::FromInt(length_));
1759 return result_array;
1760 }
1761
1762 Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
1763 target_array->set_elements(*array_);
1764 target_array->set_length(Smi::FromInt(length_));
1765 return target_array;
1766 }
1767
1768 private:
1769 Handle<FixedArray> array_;
1770 int length_;
1771};
1772
1773
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001774// Forward declarations.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001775const int kStringBuilderConcatHelperLengthBits = 11;
1776const int kStringBuilderConcatHelperPositionBits = 19;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001777
1778template <typename schar>
1779static inline void StringBuilderConcatHelper(String*,
1780 schar*,
1781 FixedArray*,
1782 int);
1783
lrn@chromium.org25156de2010-04-06 13:10:27 +00001784typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
1785 StringBuilderSubstringLength;
1786typedef BitField<int,
1787 kStringBuilderConcatHelperLengthBits,
1788 kStringBuilderConcatHelperPositionBits>
1789 StringBuilderSubstringPosition;
1790
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001791
1792class ReplacementStringBuilder {
1793 public:
1794 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
lrn@chromium.org25156de2010-04-06 13:10:27 +00001795 : array_builder_(estimated_part_count),
1796 subject_(subject),
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001797 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001798 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001799 // Require a non-zero initial size. Ensures that doubling the size to
1800 // extend the array will work.
1801 ASSERT(estimated_part_count > 0);
1802 }
1803
lrn@chromium.org25156de2010-04-06 13:10:27 +00001804 static inline void AddSubjectSlice(FixedArrayBuilder* builder,
1805 int from,
1806 int to) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001807 ASSERT(from >= 0);
1808 int length = to - from;
1809 ASSERT(length > 0);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001810 if (StringBuilderSubstringLength::is_valid(length) &&
1811 StringBuilderSubstringPosition::is_valid(from)) {
1812 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1813 StringBuilderSubstringPosition::encode(from);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001814 builder->Add(Smi::FromInt(encoded_slice));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001815 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001816 // Otherwise encode as two smis.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001817 builder->Add(Smi::FromInt(-length));
1818 builder->Add(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001819 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00001820 }
1821
1822
1823 void EnsureCapacity(int elements) {
1824 array_builder_.EnsureCapacity(elements);
1825 }
1826
1827
1828 void AddSubjectSlice(int from, int to) {
1829 AddSubjectSlice(&array_builder_, from, to);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001830 IncrementCharacterCount(to - from);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001831 }
1832
1833
1834 void AddString(Handle<String> string) {
1835 int length = string->length();
1836 ASSERT(length > 0);
1837 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001838 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001839 is_ascii_ = false;
1840 }
1841 IncrementCharacterCount(length);
1842 }
1843
1844
1845 Handle<String> ToString() {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001846 if (array_builder_.length() == 0) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001847 return Factory::empty_string();
1848 }
1849
1850 Handle<String> joined_string;
1851 if (is_ascii_) {
1852 joined_string = NewRawAsciiString(character_count_);
1853 AssertNoAllocation no_alloc;
1854 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1855 char* char_buffer = seq->GetChars();
1856 StringBuilderConcatHelper(*subject_,
1857 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001858 *array_builder_.array(),
1859 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001860 } else {
1861 // Non-ASCII.
1862 joined_string = NewRawTwoByteString(character_count_);
1863 AssertNoAllocation no_alloc;
1864 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1865 uc16* char_buffer = seq->GetChars();
1866 StringBuilderConcatHelper(*subject_,
1867 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001868 *array_builder_.array(),
1869 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001870 }
1871 return joined_string;
1872 }
1873
1874
1875 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001876 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001877 V8::FatalProcessOutOfMemory("String.replace result too large.");
1878 }
1879 character_count_ += by;
1880 }
1881
lrn@chromium.org25156de2010-04-06 13:10:27 +00001882 Handle<JSArray> GetParts() {
1883 Handle<JSArray> result =
1884 Factory::NewJSArrayWithElements(array_builder_.array());
1885 result->set_length(Smi::FromInt(array_builder_.length()));
1886 return result;
1887 }
kmillikin@chromium.orgd9825192010-03-30 08:36:16 +00001888
lrn@chromium.org25156de2010-04-06 13:10:27 +00001889 private:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001890 Handle<String> NewRawAsciiString(int size) {
1891 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1892 }
1893
1894
1895 Handle<String> NewRawTwoByteString(int size) {
1896 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1897 }
1898
1899
1900 void AddElement(Object* element) {
1901 ASSERT(element->IsSmi() || element->IsString());
lrn@chromium.org25156de2010-04-06 13:10:27 +00001902 ASSERT(array_builder_.capacity() > array_builder_.length());
1903 array_builder_.Add(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001904 }
1905
lrn@chromium.org25156de2010-04-06 13:10:27 +00001906 FixedArrayBuilder array_builder_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001907 Handle<String> subject_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001908 int character_count_;
1909 bool is_ascii_;
1910};
1911
1912
1913class CompiledReplacement {
1914 public:
1915 CompiledReplacement()
1916 : parts_(1), replacement_substrings_(0) {}
1917
1918 void Compile(Handle<String> replacement,
1919 int capture_count,
1920 int subject_length);
1921
1922 void Apply(ReplacementStringBuilder* builder,
1923 int match_from,
1924 int match_to,
1925 Handle<JSArray> last_match_info);
1926
1927 // Number of distinct parts of the replacement pattern.
1928 int parts() {
1929 return parts_.length();
1930 }
1931 private:
1932 enum PartType {
1933 SUBJECT_PREFIX = 1,
1934 SUBJECT_SUFFIX,
1935 SUBJECT_CAPTURE,
1936 REPLACEMENT_SUBSTRING,
1937 REPLACEMENT_STRING,
1938
1939 NUMBER_OF_PART_TYPES
1940 };
1941
1942 struct ReplacementPart {
1943 static inline ReplacementPart SubjectMatch() {
1944 return ReplacementPart(SUBJECT_CAPTURE, 0);
1945 }
1946 static inline ReplacementPart SubjectCapture(int capture_index) {
1947 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1948 }
1949 static inline ReplacementPart SubjectPrefix() {
1950 return ReplacementPart(SUBJECT_PREFIX, 0);
1951 }
1952 static inline ReplacementPart SubjectSuffix(int subject_length) {
1953 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1954 }
1955 static inline ReplacementPart ReplacementString() {
1956 return ReplacementPart(REPLACEMENT_STRING, 0);
1957 }
1958 static inline ReplacementPart ReplacementSubString(int from, int to) {
1959 ASSERT(from >= 0);
1960 ASSERT(to > from);
1961 return ReplacementPart(-from, to);
1962 }
1963
1964 // If tag <= 0 then it is the negation of a start index of a substring of
1965 // the replacement pattern, otherwise it's a value from PartType.
1966 ReplacementPart(int tag, int data)
1967 : tag(tag), data(data) {
1968 // Must be non-positive or a PartType value.
1969 ASSERT(tag < NUMBER_OF_PART_TYPES);
1970 }
1971 // Either a value of PartType or a non-positive number that is
1972 // the negation of an index into the replacement string.
1973 int tag;
1974 // The data value's interpretation depends on the value of tag:
1975 // tag == SUBJECT_PREFIX ||
1976 // tag == SUBJECT_SUFFIX: data is unused.
1977 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1978 // tag == REPLACEMENT_SUBSTRING ||
1979 // tag == REPLACEMENT_STRING: data is index into array of substrings
1980 // of the replacement string.
1981 // tag <= 0: Temporary representation of the substring of the replacement
1982 // string ranging over -tag .. data.
1983 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1984 // substring objects.
1985 int data;
1986 };
1987
1988 template<typename Char>
1989 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1990 Vector<Char> characters,
1991 int capture_count,
1992 int subject_length) {
1993 int length = characters.length();
1994 int last = 0;
1995 for (int i = 0; i < length; i++) {
1996 Char c = characters[i];
1997 if (c == '$') {
1998 int next_index = i + 1;
1999 if (next_index == length) { // No next character!
2000 break;
2001 }
2002 Char c2 = characters[next_index];
2003 switch (c2) {
2004 case '$':
2005 if (i > last) {
2006 // There is a substring before. Include the first "$".
2007 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
2008 last = next_index + 1; // Continue after the second "$".
2009 } else {
2010 // Let the next substring start with the second "$".
2011 last = next_index;
2012 }
2013 i = next_index;
2014 break;
2015 case '`':
2016 if (i > last) {
2017 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2018 }
2019 parts->Add(ReplacementPart::SubjectPrefix());
2020 i = next_index;
2021 last = i + 1;
2022 break;
2023 case '\'':
2024 if (i > last) {
2025 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2026 }
2027 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
2028 i = next_index;
2029 last = i + 1;
2030 break;
2031 case '&':
2032 if (i > last) {
2033 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2034 }
2035 parts->Add(ReplacementPart::SubjectMatch());
2036 i = next_index;
2037 last = i + 1;
2038 break;
2039 case '0':
2040 case '1':
2041 case '2':
2042 case '3':
2043 case '4':
2044 case '5':
2045 case '6':
2046 case '7':
2047 case '8':
2048 case '9': {
2049 int capture_ref = c2 - '0';
2050 if (capture_ref > capture_count) {
2051 i = next_index;
2052 continue;
2053 }
2054 int second_digit_index = next_index + 1;
2055 if (second_digit_index < length) {
2056 // Peek ahead to see if we have two digits.
2057 Char c3 = characters[second_digit_index];
2058 if ('0' <= c3 && c3 <= '9') { // Double digits.
2059 int double_digit_ref = capture_ref * 10 + c3 - '0';
2060 if (double_digit_ref <= capture_count) {
2061 next_index = second_digit_index;
2062 capture_ref = double_digit_ref;
2063 }
2064 }
2065 }
2066 if (capture_ref > 0) {
2067 if (i > last) {
2068 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2069 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002070 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002071 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
2072 last = next_index + 1;
2073 }
2074 i = next_index;
2075 break;
2076 }
2077 default:
2078 i = next_index;
2079 break;
2080 }
2081 }
2082 }
2083 if (length > last) {
2084 if (last == 0) {
2085 parts->Add(ReplacementPart::ReplacementString());
2086 } else {
2087 parts->Add(ReplacementPart::ReplacementSubString(last, length));
2088 }
2089 }
2090 }
2091
2092 ZoneList<ReplacementPart> parts_;
2093 ZoneList<Handle<String> > replacement_substrings_;
2094};
2095
2096
2097void CompiledReplacement::Compile(Handle<String> replacement,
2098 int capture_count,
2099 int subject_length) {
2100 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00002101 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002102 AssertNoAllocation no_alloc;
2103 ParseReplacementPattern(&parts_,
2104 replacement->ToAsciiVector(),
2105 capture_count,
2106 subject_length);
2107 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00002108 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002109 AssertNoAllocation no_alloc;
2110
2111 ParseReplacementPattern(&parts_,
2112 replacement->ToUC16Vector(),
2113 capture_count,
2114 subject_length);
2115 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002116 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002117 int substring_index = 0;
2118 for (int i = 0, n = parts_.length(); i < n; i++) {
2119 int tag = parts_[i].tag;
2120 if (tag <= 0) { // A replacement string slice.
2121 int from = -tag;
2122 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002123 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002124 parts_[i].tag = REPLACEMENT_SUBSTRING;
2125 parts_[i].data = substring_index;
2126 substring_index++;
2127 } else if (tag == REPLACEMENT_STRING) {
2128 replacement_substrings_.Add(replacement);
2129 parts_[i].data = substring_index;
2130 substring_index++;
2131 }
2132 }
2133}
2134
2135
2136void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
2137 int match_from,
2138 int match_to,
2139 Handle<JSArray> last_match_info) {
2140 for (int i = 0, n = parts_.length(); i < n; i++) {
2141 ReplacementPart part = parts_[i];
2142 switch (part.tag) {
2143 case SUBJECT_PREFIX:
2144 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
2145 break;
2146 case SUBJECT_SUFFIX: {
2147 int subject_length = part.data;
2148 if (match_to < subject_length) {
2149 builder->AddSubjectSlice(match_to, subject_length);
2150 }
2151 break;
2152 }
2153 case SUBJECT_CAPTURE: {
2154 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002155 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002156 int from = RegExpImpl::GetCapture(match_info, capture * 2);
2157 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
2158 if (from >= 0 && to > from) {
2159 builder->AddSubjectSlice(from, to);
2160 }
2161 break;
2162 }
2163 case REPLACEMENT_SUBSTRING:
2164 case REPLACEMENT_STRING:
2165 builder->AddString(replacement_substrings_[part.data]);
2166 break;
2167 default:
2168 UNREACHABLE();
2169 }
2170 }
2171}
2172
2173
2174
2175static Object* StringReplaceRegExpWithString(String* subject,
2176 JSRegExp* regexp,
2177 String* replacement,
2178 JSArray* last_match_info) {
2179 ASSERT(subject->IsFlat());
2180 ASSERT(replacement->IsFlat());
2181
2182 HandleScope handles;
2183
2184 int length = subject->length();
2185 Handle<String> subject_handle(subject);
2186 Handle<JSRegExp> regexp_handle(regexp);
2187 Handle<String> replacement_handle(replacement);
2188 Handle<JSArray> last_match_info_handle(last_match_info);
2189 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2190 subject_handle,
2191 0,
2192 last_match_info_handle);
2193 if (match.is_null()) {
2194 return Failure::Exception();
2195 }
2196 if (match->IsNull()) {
2197 return *subject_handle;
2198 }
2199
2200 int capture_count = regexp_handle->CaptureCount();
2201
2202 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002203 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002204 CompiledReplacement compiled_replacement;
2205 compiled_replacement.Compile(replacement_handle,
2206 capture_count,
2207 length);
2208
2209 bool is_global = regexp_handle->GetFlags().is_global();
2210
2211 // Guessing the number of parts that the final result string is built
2212 // from. Global regexps can match any number of times, so we guess
2213 // conservatively.
2214 int expected_parts =
2215 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
2216 ReplacementStringBuilder builder(subject_handle, expected_parts);
2217
2218 // Index of end of last match.
2219 int prev = 0;
2220
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002221 // Number of parts added by compiled replacement plus preceeding
2222 // string and possibly suffix after last match. It is possible for
2223 // all components to use two elements when encoded as two smis.
2224 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002225 bool matched = true;
2226 do {
2227 ASSERT(last_match_info_handle->HasFastElements());
2228 // Increase the capacity of the builder before entering local handle-scope,
2229 // so its internal buffer can safely allocate a new handle if it grows.
2230 builder.EnsureCapacity(parts_added_per_loop);
2231
2232 HandleScope loop_scope;
2233 int start, end;
2234 {
2235 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002236 FixedArray* match_info_array =
2237 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002238
2239 ASSERT_EQ(capture_count * 2 + 2,
2240 RegExpImpl::GetLastCaptureCount(match_info_array));
2241 start = RegExpImpl::GetCapture(match_info_array, 0);
2242 end = RegExpImpl::GetCapture(match_info_array, 1);
2243 }
2244
2245 if (prev < start) {
2246 builder.AddSubjectSlice(prev, start);
2247 }
2248 compiled_replacement.Apply(&builder,
2249 start,
2250 end,
2251 last_match_info_handle);
2252 prev = end;
2253
2254 // Only continue checking for global regexps.
2255 if (!is_global) break;
2256
2257 // Continue from where the match ended, unless it was an empty match.
2258 int next = end;
2259 if (start == end) {
2260 next = end + 1;
2261 if (next > length) break;
2262 }
2263
2264 match = RegExpImpl::Exec(regexp_handle,
2265 subject_handle,
2266 next,
2267 last_match_info_handle);
2268 if (match.is_null()) {
2269 return Failure::Exception();
2270 }
2271 matched = !match->IsNull();
2272 } while (matched);
2273
2274 if (prev < length) {
2275 builder.AddSubjectSlice(prev, length);
2276 }
2277
2278 return *(builder.ToString());
2279}
2280
2281
2282static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2283 ASSERT(args.length() == 4);
2284
2285 CONVERT_CHECKED(String, subject, args[0]);
2286 if (!subject->IsFlat()) {
2287 Object* flat_subject = subject->TryFlatten();
2288 if (flat_subject->IsFailure()) {
2289 return flat_subject;
2290 }
2291 subject = String::cast(flat_subject);
2292 }
2293
2294 CONVERT_CHECKED(String, replacement, args[2]);
2295 if (!replacement->IsFlat()) {
2296 Object* flat_replacement = replacement->TryFlatten();
2297 if (flat_replacement->IsFailure()) {
2298 return flat_replacement;
2299 }
2300 replacement = String::cast(flat_replacement);
2301 }
2302
2303 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2304 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2305
2306 ASSERT(last_match_info->HasFastElements());
2307
2308 return StringReplaceRegExpWithString(subject,
2309 regexp,
2310 replacement,
2311 last_match_info);
2312}
2313
2314
ager@chromium.org7c537e22008-10-16 08:43:32 +00002315// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2316// limit, we can fix the size of tables.
2317static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002318// Reduce alphabet to this size.
2319static const int kBMAlphabetSize = 0x100;
2320// For patterns below this length, the skip length of Boyer-Moore is too short
2321// to compensate for the algorithmic overhead compared to simple brute force.
2322static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002323
ager@chromium.org7c537e22008-10-16 08:43:32 +00002324// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2325// shift. Only allows the last kBMMaxShift characters of the needle
2326// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002327class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002328 public:
2329 BMGoodSuffixBuffers() {}
2330 inline void init(int needle_length) {
2331 ASSERT(needle_length > 1);
2332 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2333 int len = needle_length - start;
2334 biased_suffixes_ = suffixes_ - start;
2335 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2336 for (int i = 0; i <= len; i++) {
2337 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002338 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002339 }
2340 inline int& suffix(int index) {
2341 ASSERT(biased_suffixes_ + index >= suffixes_);
2342 return biased_suffixes_[index];
2343 }
2344 inline int& shift(int index) {
2345 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2346 return biased_good_suffix_shift_[index];
2347 }
2348 private:
2349 int suffixes_[kBMMaxShift + 1];
2350 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002351 int* biased_suffixes_;
2352 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002353 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2354};
2355
2356// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002357static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002358static BMGoodSuffixBuffers bmgs_buffers;
2359
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002360// State of the string match tables.
2361// SIMPLE: No usable content in the buffers.
2362// BOYER_MOORE_HORSPOOL: The bad_char_occurences table has been populated.
2363// BOYER_MOORE: The bmgs_buffers tables have also been populated.
2364// Whenever starting with a new needle, one should call InitializeStringSearch
2365// to determine which search strategy to use, and in the case of a long-needle
2366// strategy, the call also initializes the algorithm to SIMPLE.
2367enum StringSearchAlgorithm { SIMPLE_SEARCH, BOYER_MOORE_HORSPOOL, BOYER_MOORE };
2368static StringSearchAlgorithm algorithm;
2369
2370
ager@chromium.org7c537e22008-10-16 08:43:32 +00002371// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002372template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002373static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern) {
2374 // Only preprocess at most kBMMaxShift last characters of pattern.
2375 int start = pattern.length() < kBMMaxShift ? 0
2376 : pattern.length() - kBMMaxShift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002377 // Run forwards to populate bad_char_table, so that *last* instance
2378 // of character equivalence class is the one registered.
2379 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002380 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2381 : kBMAlphabetSize;
2382 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002383 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002384 } else {
2385 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002386 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002387 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002388 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002389 for (int i = start; i < pattern.length() - 1; i++) {
2390 pchar c = pattern[i];
2391 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002392 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002393 }
2394}
2395
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002396
ager@chromium.org7c537e22008-10-16 08:43:32 +00002397template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002398static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002399 int m = pattern.length();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002400 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002401 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002402 // Compute Good Suffix tables.
2403 bmgs_buffers.init(m);
2404
2405 bmgs_buffers.shift(m-1) = 1;
2406 bmgs_buffers.suffix(m) = m + 1;
2407 pchar last_char = pattern[m - 1];
2408 int suffix = m + 1;
2409 for (int i = m; i > start;) {
2410 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2411 if (bmgs_buffers.shift(suffix) == len) {
2412 bmgs_buffers.shift(suffix) = suffix - i;
2413 }
2414 suffix = bmgs_buffers.suffix(suffix);
2415 }
2416 i--;
2417 suffix--;
2418 bmgs_buffers.suffix(i) = suffix;
2419 if (suffix == m) {
2420 // No suffix to extend, so we check against last_char only.
2421 while (i > start && pattern[i - 1] != last_char) {
2422 if (bmgs_buffers.shift(m) == len) {
2423 bmgs_buffers.shift(m) = m - i;
2424 }
2425 i--;
2426 bmgs_buffers.suffix(i) = m;
2427 }
2428 if (i > start) {
2429 i--;
2430 suffix--;
2431 bmgs_buffers.suffix(i) = suffix;
2432 }
2433 }
2434 }
2435 if (suffix < m) {
2436 for (int i = start; i <= m; i++) {
2437 if (bmgs_buffers.shift(i) == len) {
2438 bmgs_buffers.shift(i) = suffix - start;
2439 }
2440 if (i == suffix) {
2441 suffix = bmgs_buffers.suffix(suffix);
2442 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002443 }
2444 }
2445}
2446
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002447
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002448template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002449static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002450 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002451 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002452 }
2453 if (sizeof(pchar) == 1) {
2454 if (char_code > String::kMaxAsciiCharCode) {
2455 return -1;
2456 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002457 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002458 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002459 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002460}
2461
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002462
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002463// Restricted simplified Boyer-Moore string matching.
2464// Uses only the bad-shift table of Boyer-Moore and only uses it
2465// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002466template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002467static int BoyerMooreHorspool(Vector<const schar> subject,
2468 Vector<const pchar> pattern,
2469 int start_index,
2470 bool* complete) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002471 ASSERT(algorithm <= BOYER_MOORE_HORSPOOL);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002472 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002473 int m = pattern.length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002474
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002475 int badness = -m;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002476
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002477 // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002478 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002479 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002480 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002481 // Perform search
2482 for (idx = start_index; idx <= n - m;) {
2483 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002484 int c;
2485 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002486 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002487 int shift = j - bc_occ;
2488 idx += shift;
2489 badness += 1 - shift; // at most zero, so badness cannot increase.
2490 if (idx > n - m) {
2491 *complete = true;
2492 return -1;
2493 }
2494 }
2495 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002496 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002497 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002498 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002499 return idx;
2500 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002501 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002502 // Badness increases by the number of characters we have
2503 // checked, and decreases by the number of characters we
2504 // can skip by shifting. It's a measure of how we are doing
2505 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002506 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002507 if (badness > 0) {
2508 *complete = false;
2509 return idx;
2510 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002511 }
2512 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002513 *complete = true;
2514 return -1;
2515}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002516
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002517
2518template <typename schar, typename pchar>
2519static int BoyerMooreIndexOf(Vector<const schar> subject,
2520 Vector<const pchar> pattern,
2521 int idx) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002522 ASSERT(algorithm <= BOYER_MOORE);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002523 int n = subject.length();
2524 int m = pattern.length();
2525 // Only preprocess at most kBMMaxShift last characters of pattern.
2526 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2527
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002528 pchar last_char = pattern[m - 1];
2529 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002530 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002531 int j = m - 1;
2532 schar c;
2533 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002534 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002535 idx += shift;
2536 if (idx > n - m) {
2537 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002538 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002539 }
2540 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2541 if (j < 0) {
2542 return idx;
2543 } else if (j < start) {
2544 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002545 // Fall back on BMH shift.
2546 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002547 } else {
2548 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002549 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002550 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002551 if (gs_shift > shift) {
2552 shift = gs_shift;
2553 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002554 idx += shift;
2555 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002556 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002557
2558 return -1;
2559}
2560
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002561
2562template <typename schar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002563static inline int SingleCharIndexOf(Vector<const schar> string,
2564 schar pattern_char,
2565 int start_index) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002566 if (sizeof(schar) == 1) {
2567 const schar* pos = reinterpret_cast<const schar*>(
2568 memchr(string.start() + start_index,
2569 pattern_char,
2570 string.length() - start_index));
2571 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002572 return static_cast<int>(pos - string.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002573 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002574 for (int i = start_index, n = string.length(); i < n; i++) {
2575 if (pattern_char == string[i]) {
2576 return i;
2577 }
2578 }
2579 return -1;
2580}
2581
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002582
2583template <typename schar>
2584static int SingleCharLastIndexOf(Vector<const schar> string,
2585 schar pattern_char,
2586 int start_index) {
2587 for (int i = start_index; i >= 0; i--) {
2588 if (pattern_char == string[i]) {
2589 return i;
2590 }
2591 }
2592 return -1;
2593}
2594
2595
ager@chromium.org7c537e22008-10-16 08:43:32 +00002596// Trivial string search for shorter strings.
2597// On return, if "complete" is set to true, the return value is the
2598// final result of searching for the patter in the subject.
2599// If "complete" is set to false, the return value is the index where
2600// further checking should start, i.e., it's guaranteed that the pattern
2601// does not occur at a position prior to the returned index.
2602template <typename pchar, typename schar>
2603static int SimpleIndexOf(Vector<const schar> subject,
2604 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002605 int idx,
2606 bool* complete) {
2607 // Badness is a count of how much work we have done. When we have
2608 // done enough work we decide it's probably worth switching to a better
2609 // algorithm.
2610 int badness = -10 - (pattern.length() << 2);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002611
ager@chromium.org7c537e22008-10-16 08:43:32 +00002612 // We know our pattern is at least 2 characters, we cache the first so
2613 // the common case of the first character not matching is faster.
2614 pchar pattern_first_char = pattern[0];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002615 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2616 badness++;
2617 if (badness > 0) {
2618 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002619 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002620 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002621 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2622 const schar* pos = reinterpret_cast<const schar*>(
2623 memchr(subject.start() + i,
2624 pattern_first_char,
2625 n - i + 1));
2626 if (pos == NULL) {
2627 *complete = true;
2628 return -1;
2629 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002630 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002631 } else {
2632 if (subject[i] != pattern_first_char) continue;
2633 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002634 int j = 1;
2635 do {
2636 if (pattern[j] != subject[i+j]) {
2637 break;
2638 }
2639 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002640 } while (j < pattern.length());
2641 if (j == pattern.length()) {
2642 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002643 return i;
2644 }
2645 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002646 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002647 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002648 return -1;
2649}
2650
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002651// Simple indexOf that never bails out. For short patterns only.
2652template <typename pchar, typename schar>
2653static int SimpleIndexOf(Vector<const schar> subject,
2654 Vector<const pchar> pattern,
2655 int idx) {
2656 pchar pattern_first_char = pattern[0];
2657 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002658 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2659 const schar* pos = reinterpret_cast<const schar*>(
2660 memchr(subject.start() + i,
2661 pattern_first_char,
2662 n - i + 1));
2663 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002664 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002665 } else {
2666 if (subject[i] != pattern_first_char) continue;
2667 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002668 int j = 1;
2669 do {
2670 if (pattern[j] != subject[i+j]) {
2671 break;
2672 }
2673 j++;
2674 } while (j < pattern.length());
2675 if (j == pattern.length()) {
2676 return i;
2677 }
2678 }
2679 return -1;
2680}
2681
2682
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002683// Strategy for searching for a string in another string.
2684enum StringSearchStrategy { SEARCH_FAIL, SEARCH_SHORT, SEARCH_LONG };
ager@chromium.org7c537e22008-10-16 08:43:32 +00002685
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002686
2687template <typename pchar>
2688static inline StringSearchStrategy InitializeStringSearch(
2689 Vector<const pchar> pat, bool ascii_subject) {
2690 ASSERT(pat.length() > 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002691 // We have an ASCII haystack and a non-ASCII needle. Check if there
2692 // really is a non-ASCII character in the needle and bail out if there
2693 // is.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002694 if (ascii_subject && sizeof(pchar) > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002695 for (int i = 0; i < pat.length(); i++) {
2696 uc16 c = pat[i];
2697 if (c > String::kMaxAsciiCharCode) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002698 return SEARCH_FAIL;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002699 }
2700 }
2701 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002702 if (pat.length() < kBMMinPatternLength) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002703 return SEARCH_SHORT;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002704 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002705 algorithm = SIMPLE_SEARCH;
2706 return SEARCH_LONG;
2707}
2708
2709
2710// Dispatch long needle searches to different algorithms.
2711template <typename schar, typename pchar>
2712static int ComplexIndexOf(Vector<const schar> sub,
2713 Vector<const pchar> pat,
2714 int start_index) {
2715 ASSERT(pat.length() >= kBMMinPatternLength);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002716 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002717 bool complete;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002718 int idx = start_index;
2719 switch (algorithm) {
2720 case SIMPLE_SEARCH:
2721 idx = SimpleIndexOf(sub, pat, idx, &complete);
2722 if (complete) return idx;
2723 BoyerMoorePopulateBadCharTable(pat);
2724 algorithm = BOYER_MOORE_HORSPOOL;
2725 // FALLTHROUGH.
2726 case BOYER_MOORE_HORSPOOL:
2727 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
2728 if (complete) return idx;
2729 // Build the Good Suffix table and continue searching.
2730 BoyerMoorePopulateGoodSuffixTable(pat);
2731 algorithm = BOYER_MOORE;
2732 // FALLTHROUGH.
2733 case BOYER_MOORE:
2734 return BoyerMooreIndexOf(sub, pat, idx);
2735 }
2736 UNREACHABLE();
2737 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002738}
2739
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002740
2741// Dispatch to different search strategies for a single search.
2742// If searching multiple times on the same needle, the search
2743// strategy should only be computed once and then dispatch to different
2744// loops.
2745template <typename schar, typename pchar>
2746static int StringSearch(Vector<const schar> sub,
2747 Vector<const pchar> pat,
2748 int start_index) {
2749 bool ascii_subject = (sizeof(schar) == 1);
2750 StringSearchStrategy strategy = InitializeStringSearch(pat, ascii_subject);
2751 switch (strategy) {
2752 case SEARCH_FAIL: return -1;
2753 case SEARCH_SHORT: return SimpleIndexOf(sub, pat, start_index);
2754 case SEARCH_LONG: return ComplexIndexOf(sub, pat, start_index);
2755 }
2756 UNREACHABLE();
2757 return -1;
2758}
2759
2760
ager@chromium.org7c537e22008-10-16 08:43:32 +00002761// Perform string match of pattern on subject, starting at start index.
2762// Caller must ensure that 0 <= start_index <= sub->length(),
2763// and should check that pat->length() + start_index <= sub->length()
2764int Runtime::StringMatch(Handle<String> sub,
2765 Handle<String> pat,
2766 int start_index) {
2767 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002768 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002769
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002770 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002771 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002772
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002773 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002774 if (start_index + pattern_length > subject_length) return -1;
2775
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002776 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002777 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002778 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002779
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002780 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002781 // character patterns linear search is necessary, so any smart
2782 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002783 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002784 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002785 String* seq_sub = *sub;
2786 if (seq_sub->IsConsString()) {
2787 seq_sub = ConsString::cast(seq_sub)->first();
2788 }
2789 if (seq_sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002790 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002791 if (pchar > String::kMaxAsciiCharCode) {
2792 return -1;
2793 }
2794 Vector<const char> ascii_vector =
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002795 seq_sub->ToAsciiVector().SubVector(start_index, subject_length);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002796 const void* pos = memchr(ascii_vector.start(),
2797 static_cast<const char>(pchar),
2798 static_cast<size_t>(ascii_vector.length()));
2799 if (pos == NULL) {
2800 return -1;
2801 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002802 return static_cast<int>(reinterpret_cast<const char*>(pos)
2803 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002804 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002805 return SingleCharIndexOf(seq_sub->ToUC16Vector(),
2806 pat->Get(0),
2807 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
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002815 // Extract flattened substrings of cons strings before determining asciiness.
2816 String* seq_sub = *sub;
2817 if (seq_sub->IsConsString()) {
2818 seq_sub = ConsString::cast(seq_sub)->first();
2819 }
2820 String* seq_pat = *pat;
2821 if (seq_pat->IsConsString()) {
2822 seq_pat = ConsString::cast(seq_pat)->first();
2823 }
2824
ager@chromium.org7c537e22008-10-16 08:43:32 +00002825 // dispatch on type of strings
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002826 if (seq_pat->IsAsciiRepresentation()) {
2827 Vector<const char> pat_vector = seq_pat->ToAsciiVector();
2828 if (seq_sub->IsAsciiRepresentation()) {
2829 return StringSearch(seq_sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002830 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002831 return StringSearch(seq_sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002832 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002833 Vector<const uc16> pat_vector = seq_pat->ToUC16Vector();
2834 if (seq_sub->IsAsciiRepresentation()) {
2835 return StringSearch(seq_sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002836 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002837 return StringSearch(seq_sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002838}
2839
2840
2841static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002842 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002843 ASSERT(args.length() == 3);
2844
ager@chromium.org7c537e22008-10-16 08:43:32 +00002845 CONVERT_ARG_CHECKED(String, sub, 0);
2846 CONVERT_ARG_CHECKED(String, pat, 1);
2847
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002848 Object* index = args[2];
2849 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002850 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002851
ager@chromium.org870a0b62008-11-04 11:43:05 +00002852 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002853 int position = Runtime::StringMatch(sub, pat, start_index);
2854 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002855}
2856
2857
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002858template <typename schar, typename pchar>
2859static int StringMatchBackwards(Vector<const schar> sub,
2860 Vector<const pchar> pat,
2861 int idx) {
2862 ASSERT(pat.length() >= 1);
2863 ASSERT(idx + pat.length() <= sub.length());
2864
2865 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
2866 for (int i = 0; i < pat.length(); i++) {
2867 uc16 c = pat[i];
2868 if (c > String::kMaxAsciiCharCode) {
2869 return -1;
2870 }
2871 }
2872 }
2873
2874 pchar pattern_first_char = pat[0];
2875 for (int i = idx; i >= 0; i--) {
2876 if (sub[i] != pattern_first_char) continue;
2877 int j = 1;
2878 while (j < pat.length()) {
2879 if (pat[j] != sub[i+j]) {
2880 break;
2881 }
2882 j++;
2883 }
2884 if (j == pat.length()) {
2885 return i;
2886 }
2887 }
2888 return -1;
2889}
2890
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002891static Object* Runtime_StringLastIndexOf(Arguments args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002892 HandleScope scope; // create a new handle scope
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002893 ASSERT(args.length() == 3);
2894
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002895 CONVERT_ARG_CHECKED(String, sub, 0);
2896 CONVERT_ARG_CHECKED(String, pat, 1);
2897
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002898 Object* index = args[2];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002899 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002900 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002901
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002902 uint32_t pat_length = pat->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002903 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002904
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002905 if (start_index + pat_length > sub_length) {
2906 start_index = sub_length - pat_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002907 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002908
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002909 if (pat_length == 0) {
2910 return Smi::FromInt(start_index);
2911 }
2912
2913 if (!sub->IsFlat()) {
2914 FlattenString(sub);
2915 }
2916
2917 if (pat_length == 1) {
2918 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2919 if (sub->IsAsciiRepresentation()) {
2920 uc16 pchar = pat->Get(0);
2921 if (pchar > String::kMaxAsciiCharCode) {
2922 return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002923 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002924 return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
2925 static_cast<char>(pat->Get(0)),
2926 start_index));
2927 } else {
2928 return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
2929 pat->Get(0),
2930 start_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002931 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002932 }
2933
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002934 if (!pat->IsFlat()) {
2935 FlattenString(pat);
2936 }
2937
2938 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2939
2940 int position = -1;
2941
2942 if (pat->IsAsciiRepresentation()) {
2943 Vector<const char> pat_vector = pat->ToAsciiVector();
2944 if (sub->IsAsciiRepresentation()) {
2945 position = StringMatchBackwards(sub->ToAsciiVector(),
2946 pat_vector,
2947 start_index);
2948 } else {
2949 position = StringMatchBackwards(sub->ToUC16Vector(),
2950 pat_vector,
2951 start_index);
2952 }
2953 } else {
2954 Vector<const uc16> pat_vector = pat->ToUC16Vector();
2955 if (sub->IsAsciiRepresentation()) {
2956 position = StringMatchBackwards(sub->ToAsciiVector(),
2957 pat_vector,
2958 start_index);
2959 } else {
2960 position = StringMatchBackwards(sub->ToUC16Vector(),
2961 pat_vector,
2962 start_index);
2963 }
2964 }
2965
2966 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002967}
2968
2969
2970static Object* Runtime_StringLocaleCompare(Arguments args) {
2971 NoHandleAllocation ha;
2972 ASSERT(args.length() == 2);
2973
2974 CONVERT_CHECKED(String, str1, args[0]);
2975 CONVERT_CHECKED(String, str2, args[1]);
2976
2977 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002978 int str1_length = str1->length();
2979 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002980
2981 // Decide trivial cases without flattening.
2982 if (str1_length == 0) {
2983 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2984 return Smi::FromInt(-str2_length);
2985 } else {
2986 if (str2_length == 0) return Smi::FromInt(str1_length);
2987 }
2988
2989 int end = str1_length < str2_length ? str1_length : str2_length;
2990
2991 // No need to flatten if we are going to find the answer on the first
2992 // character. At this point we know there is at least one character
2993 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002994 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002995 if (d != 0) return Smi::FromInt(d);
2996
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002997 str1->TryFlatten();
2998 str2->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002999
3000 static StringInputBuffer buf1;
3001 static StringInputBuffer buf2;
3002
3003 buf1.Reset(str1);
3004 buf2.Reset(str2);
3005
3006 for (int i = 0; i < end; i++) {
3007 uint16_t char1 = buf1.GetNext();
3008 uint16_t char2 = buf2.GetNext();
3009 if (char1 != char2) return Smi::FromInt(char1 - char2);
3010 }
3011
3012 return Smi::FromInt(str1_length - str2_length);
3013}
3014
3015
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003016static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003017 NoHandleAllocation ha;
3018 ASSERT(args.length() == 3);
3019
3020 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00003021 Object* from = args[1];
3022 Object* to = args[2];
3023 int start, end;
3024 // We have a fast integer-only case here to avoid a conversion to double in
3025 // the common case where from and to are Smis.
3026 if (from->IsSmi() && to->IsSmi()) {
3027 start = Smi::cast(from)->value();
3028 end = Smi::cast(to)->value();
3029 } else {
3030 CONVERT_DOUBLE_CHECKED(from_number, from);
3031 CONVERT_DOUBLE_CHECKED(to_number, to);
3032 start = FastD2I(from_number);
3033 end = FastD2I(to_number);
3034 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003035 RUNTIME_ASSERT(end >= start);
3036 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003037 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003038 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003039 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003040}
3041
3042
ager@chromium.org41826e72009-03-30 13:30:57 +00003043static Object* Runtime_StringMatch(Arguments args) {
3044 ASSERT_EQ(3, args.length());
3045
3046 CONVERT_ARG_CHECKED(String, subject, 0);
3047 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
3048 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
3049 HandleScope handles;
3050
3051 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
3052
3053 if (match.is_null()) {
3054 return Failure::Exception();
3055 }
3056 if (match->IsNull()) {
3057 return Heap::null_value();
3058 }
3059 int length = subject->length();
3060
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00003061 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00003062 ZoneList<int> offsets(8);
3063 do {
3064 int start;
3065 int end;
3066 {
3067 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003068 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00003069 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
3070 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
3071 }
3072 offsets.Add(start);
3073 offsets.Add(end);
3074 int index = start < end ? end : end + 1;
3075 if (index > length) break;
3076 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
3077 if (match.is_null()) {
3078 return Failure::Exception();
3079 }
3080 } while (!match->IsNull());
3081 int matches = offsets.length() / 2;
3082 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
3083 for (int i = 0; i < matches ; i++) {
3084 int from = offsets.at(i * 2);
3085 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003086 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00003087 }
3088 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
3089 result->set_length(Smi::FromInt(matches));
3090 return *result;
3091}
3092
3093
lrn@chromium.org25156de2010-04-06 13:10:27 +00003094// Two smis before and after the match, for very long strings.
3095const int kMaxBuilderEntriesPerRegExpMatch = 5;
3096
3097
3098static void SetLastMatchInfoNoCaptures(Handle<String> subject,
3099 Handle<JSArray> last_match_info,
3100 int match_start,
3101 int match_end) {
3102 // Fill last_match_info with a single capture.
3103 last_match_info->EnsureSize(2 + RegExpImpl::kLastMatchOverhead);
3104 AssertNoAllocation no_gc;
3105 FixedArray* elements = FixedArray::cast(last_match_info->elements());
3106 RegExpImpl::SetLastCaptureCount(elements, 2);
3107 RegExpImpl::SetLastInput(elements, *subject);
3108 RegExpImpl::SetLastSubject(elements, *subject);
3109 RegExpImpl::SetCapture(elements, 0, match_start);
3110 RegExpImpl::SetCapture(elements, 1, match_end);
3111}
3112
3113
3114template <typename schar>
3115static bool SearchCharMultiple(Vector<schar> subject,
3116 String* pattern,
3117 schar pattern_char,
3118 FixedArrayBuilder* builder,
3119 int* match_pos) {
3120 // Position of last match.
3121 int pos = *match_pos;
3122 int subject_length = subject.length();
3123 while (pos < subject_length) {
3124 int match_end = pos + 1;
3125 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3126 *match_pos = pos;
3127 return false;
3128 }
3129 int new_pos = SingleCharIndexOf(subject, pattern_char, match_end);
3130 if (new_pos >= 0) {
3131 // Match has been found.
3132 if (new_pos > match_end) {
3133 ReplacementStringBuilder::AddSubjectSlice(builder, match_end, new_pos);
3134 }
3135 pos = new_pos;
3136 builder->Add(pattern);
3137 } else {
3138 break;
3139 }
3140 }
3141 if (pos + 1 < subject_length) {
3142 ReplacementStringBuilder::AddSubjectSlice(builder, pos + 1, subject_length);
3143 }
3144 *match_pos = pos;
3145 return true;
3146}
3147
3148
3149static bool SearchCharMultiple(Handle<String> subject,
3150 Handle<String> pattern,
3151 Handle<JSArray> last_match_info,
3152 FixedArrayBuilder* builder) {
3153 ASSERT(subject->IsFlat());
3154 ASSERT_EQ(1, pattern->length());
3155 uc16 pattern_char = pattern->Get(0);
3156 // Treating position before first as initial "previous match position".
3157 int match_pos = -1;
3158
3159 for (;;) { // Break when search complete.
3160 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3161 AssertNoAllocation no_gc;
3162 if (subject->IsAsciiRepresentation()) {
3163 if (pattern_char > String::kMaxAsciiCharCode) {
3164 break;
3165 }
3166 Vector<const char> subject_vector = subject->ToAsciiVector();
3167 char pattern_ascii_char = static_cast<char>(pattern_char);
3168 bool complete = SearchCharMultiple<const char>(subject_vector,
3169 *pattern,
3170 pattern_ascii_char,
3171 builder,
3172 &match_pos);
3173 if (complete) break;
3174 } else {
3175 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3176 bool complete = SearchCharMultiple<const uc16>(subject_vector,
3177 *pattern,
3178 pattern_char,
3179 builder,
3180 &match_pos);
3181 if (complete) break;
3182 }
3183 }
3184
3185 if (match_pos >= 0) {
3186 SetLastMatchInfoNoCaptures(subject,
3187 last_match_info,
3188 match_pos,
3189 match_pos + 1);
3190 return true;
3191 }
3192 return false; // No matches at all.
3193}
3194
3195
3196template <typename schar, typename pchar>
3197static bool SearchStringMultiple(Vector<schar> subject,
3198 String* pattern,
3199 Vector<pchar> pattern_string,
3200 FixedArrayBuilder* builder,
3201 int* match_pos) {
3202 int pos = *match_pos;
3203 int subject_length = subject.length();
3204 int pattern_length = pattern_string.length();
3205 int max_search_start = subject_length - pattern_length;
3206 bool is_ascii = (sizeof(schar) == 1);
3207 StringSearchStrategy strategy =
3208 InitializeStringSearch(pattern_string, is_ascii);
3209 switch (strategy) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003210 case SEARCH_FAIL: break;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003211 case SEARCH_SHORT:
3212 while (pos <= max_search_start) {
3213 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3214 *match_pos = pos;
3215 return false;
3216 }
3217 // Position of end of previous match.
3218 int match_end = pos + pattern_length;
3219 int new_pos = SimpleIndexOf(subject, pattern_string, match_end);
3220 if (new_pos >= 0) {
3221 // A match.
3222 if (new_pos > match_end) {
3223 ReplacementStringBuilder::AddSubjectSlice(builder,
3224 match_end,
3225 new_pos);
3226 }
3227 pos = new_pos;
3228 builder->Add(pattern);
3229 } else {
3230 break;
3231 }
3232 }
3233 break;
3234 case SEARCH_LONG:
3235 while (pos <= max_search_start) {
3236 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003237 *match_pos = pos;
3238 return false;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003239 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003240 int match_end = pos + pattern_length;
3241 int new_pos = ComplexIndexOf(subject, pattern_string, match_end);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003242 if (new_pos >= 0) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003243 // A match has been found.
3244 if (new_pos > match_end) {
3245 ReplacementStringBuilder::AddSubjectSlice(builder,
3246 match_end,
3247 new_pos);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003248 }
3249 pos = new_pos;
3250 builder->Add(pattern);
3251 } else {
3252 break;
3253 }
3254 }
3255 break;
3256 }
3257 if (pos < max_search_start) {
3258 ReplacementStringBuilder::AddSubjectSlice(builder,
3259 pos + pattern_length,
3260 subject_length);
3261 }
3262 *match_pos = pos;
3263 return true;
3264}
3265
3266
3267static bool SearchStringMultiple(Handle<String> subject,
3268 Handle<String> pattern,
3269 Handle<JSArray> last_match_info,
3270 FixedArrayBuilder* builder) {
3271 ASSERT(subject->IsFlat());
3272 ASSERT(pattern->IsFlat());
3273 ASSERT(pattern->length() > 1);
3274
3275 // Treating as if a previous match was before first character.
3276 int match_pos = -pattern->length();
3277
3278 for (;;) { // Break when search complete.
3279 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3280 AssertNoAllocation no_gc;
3281 if (subject->IsAsciiRepresentation()) {
3282 Vector<const char> subject_vector = subject->ToAsciiVector();
3283 if (pattern->IsAsciiRepresentation()) {
3284 if (SearchStringMultiple(subject_vector,
3285 *pattern,
3286 pattern->ToAsciiVector(),
3287 builder,
3288 &match_pos)) break;
3289 } else {
3290 if (SearchStringMultiple(subject_vector,
3291 *pattern,
3292 pattern->ToUC16Vector(),
3293 builder,
3294 &match_pos)) break;
3295 }
3296 } else {
3297 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3298 if (pattern->IsAsciiRepresentation()) {
3299 if (SearchStringMultiple(subject_vector,
3300 *pattern,
3301 pattern->ToAsciiVector(),
3302 builder,
3303 &match_pos)) break;
3304 } else {
3305 if (SearchStringMultiple(subject_vector,
3306 *pattern,
3307 pattern->ToUC16Vector(),
3308 builder,
3309 &match_pos)) break;
3310 }
3311 }
3312 }
3313
3314 if (match_pos >= 0) {
3315 SetLastMatchInfoNoCaptures(subject,
3316 last_match_info,
3317 match_pos,
3318 match_pos + pattern->length());
3319 return true;
3320 }
3321 return false; // No matches at all.
3322}
3323
3324
3325static RegExpImpl::IrregexpResult SearchRegExpNoCaptureMultiple(
3326 Handle<String> subject,
3327 Handle<JSRegExp> regexp,
3328 Handle<JSArray> last_match_array,
3329 FixedArrayBuilder* builder) {
3330 ASSERT(subject->IsFlat());
3331 int match_start = -1;
3332 int match_end = 0;
3333 int pos = 0;
3334 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3335 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3336
3337 OffsetsVector registers(required_registers);
3338 Vector<int> register_vector(registers.vector(), registers.length());
3339 int subject_length = subject->length();
3340
3341 for (;;) { // Break on failure, return on exception.
3342 RegExpImpl::IrregexpResult result =
3343 RegExpImpl::IrregexpExecOnce(regexp,
3344 subject,
3345 pos,
3346 register_vector);
3347 if (result == RegExpImpl::RE_SUCCESS) {
3348 match_start = register_vector[0];
3349 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3350 if (match_end < match_start) {
3351 ReplacementStringBuilder::AddSubjectSlice(builder,
3352 match_end,
3353 match_start);
3354 }
3355 match_end = register_vector[1];
3356 HandleScope loop_scope;
3357 builder->Add(*Factory::NewSubString(subject, match_start, match_end));
3358 if (match_start != match_end) {
3359 pos = match_end;
3360 } else {
3361 pos = match_end + 1;
3362 if (pos > subject_length) break;
3363 }
3364 } else if (result == RegExpImpl::RE_FAILURE) {
3365 break;
3366 } else {
3367 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3368 return result;
3369 }
3370 }
3371
3372 if (match_start >= 0) {
3373 if (match_end < subject_length) {
3374 ReplacementStringBuilder::AddSubjectSlice(builder,
3375 match_end,
3376 subject_length);
3377 }
3378 SetLastMatchInfoNoCaptures(subject,
3379 last_match_array,
3380 match_start,
3381 match_end);
3382 return RegExpImpl::RE_SUCCESS;
3383 } else {
3384 return RegExpImpl::RE_FAILURE; // No matches at all.
3385 }
3386}
3387
3388
3389static RegExpImpl::IrregexpResult SearchRegExpMultiple(
3390 Handle<String> subject,
3391 Handle<JSRegExp> regexp,
3392 Handle<JSArray> last_match_array,
3393 FixedArrayBuilder* builder) {
3394
3395 ASSERT(subject->IsFlat());
3396 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3397 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3398
3399 OffsetsVector registers(required_registers);
3400 Vector<int> register_vector(registers.vector(), registers.length());
3401
3402 RegExpImpl::IrregexpResult result =
3403 RegExpImpl::IrregexpExecOnce(regexp,
3404 subject,
3405 0,
3406 register_vector);
3407
3408 int capture_count = regexp->CaptureCount();
3409 int subject_length = subject->length();
3410
3411 // Position to search from.
3412 int pos = 0;
3413 // End of previous match. Differs from pos if match was empty.
3414 int match_end = 0;
3415 if (result == RegExpImpl::RE_SUCCESS) {
3416 // Need to keep a copy of the previous match for creating last_match_info
3417 // at the end, so we have two vectors that we swap between.
3418 OffsetsVector registers2(required_registers);
3419 Vector<int> prev_register_vector(registers2.vector(), registers2.length());
3420
3421 do {
3422 int match_start = register_vector[0];
3423 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3424 if (match_end < match_start) {
3425 ReplacementStringBuilder::AddSubjectSlice(builder,
3426 match_end,
3427 match_start);
3428 }
3429 match_end = register_vector[1];
3430
3431 {
3432 // Avoid accumulating new handles inside loop.
3433 HandleScope temp_scope;
3434 // Arguments array to replace function is match, captures, index and
3435 // subject, i.e., 3 + capture count in total.
3436 Handle<FixedArray> elements = Factory::NewFixedArray(3 + capture_count);
3437 elements->set(0, *Factory::NewSubString(subject,
3438 match_start,
3439 match_end));
3440 for (int i = 1; i <= capture_count; i++) {
3441 int start = register_vector[i * 2];
3442 if (start >= 0) {
3443 int end = register_vector[i * 2 + 1];
3444 ASSERT(start <= end);
3445 Handle<String> substring = Factory::NewSubString(subject,
3446 start,
3447 end);
3448 elements->set(i, *substring);
3449 } else {
3450 ASSERT(register_vector[i * 2 + 1] < 0);
3451 elements->set(i, Heap::undefined_value());
3452 }
3453 }
3454 elements->set(capture_count + 1, Smi::FromInt(match_start));
3455 elements->set(capture_count + 2, *subject);
3456 builder->Add(*Factory::NewJSArrayWithElements(elements));
3457 }
3458 // Swap register vectors, so the last successful match is in
3459 // prev_register_vector.
3460 Vector<int> tmp = prev_register_vector;
3461 prev_register_vector = register_vector;
3462 register_vector = tmp;
3463
3464 if (match_end > match_start) {
3465 pos = match_end;
3466 } else {
3467 pos = match_end + 1;
3468 if (pos > subject_length) {
3469 break;
3470 }
3471 }
3472
3473 result = RegExpImpl::IrregexpExecOnce(regexp,
3474 subject,
3475 pos,
3476 register_vector);
3477 } while (result == RegExpImpl::RE_SUCCESS);
3478
3479 if (result != RegExpImpl::RE_EXCEPTION) {
3480 // Finished matching, with at least one match.
3481 if (match_end < subject_length) {
3482 ReplacementStringBuilder::AddSubjectSlice(builder,
3483 match_end,
3484 subject_length);
3485 }
3486
3487 int last_match_capture_count = (capture_count + 1) * 2;
3488 int last_match_array_size =
3489 last_match_capture_count + RegExpImpl::kLastMatchOverhead;
3490 last_match_array->EnsureSize(last_match_array_size);
3491 AssertNoAllocation no_gc;
3492 FixedArray* elements = FixedArray::cast(last_match_array->elements());
3493 RegExpImpl::SetLastCaptureCount(elements, last_match_capture_count);
3494 RegExpImpl::SetLastSubject(elements, *subject);
3495 RegExpImpl::SetLastInput(elements, *subject);
3496 for (int i = 0; i < last_match_capture_count; i++) {
3497 RegExpImpl::SetCapture(elements, i, prev_register_vector[i]);
3498 }
3499 return RegExpImpl::RE_SUCCESS;
3500 }
3501 }
3502 // No matches at all, return failure or exception result directly.
3503 return result;
3504}
3505
3506
3507static Object* Runtime_RegExpExecMultiple(Arguments args) {
3508 ASSERT(args.length() == 4);
3509 HandleScope handles;
3510
3511 CONVERT_ARG_CHECKED(String, subject, 1);
3512 if (!subject->IsFlat()) { FlattenString(subject); }
3513 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
3514 CONVERT_ARG_CHECKED(JSArray, last_match_info, 2);
3515 CONVERT_ARG_CHECKED(JSArray, result_array, 3);
3516
3517 ASSERT(last_match_info->HasFastElements());
3518 ASSERT(regexp->GetFlags().is_global());
3519 Handle<FixedArray> result_elements;
3520 if (result_array->HasFastElements()) {
3521 result_elements =
3522 Handle<FixedArray>(FixedArray::cast(result_array->elements()));
3523 } else {
3524 result_elements = Factory::NewFixedArrayWithHoles(16);
3525 }
3526 FixedArrayBuilder builder(result_elements);
3527
3528 if (regexp->TypeTag() == JSRegExp::ATOM) {
3529 Handle<String> pattern(
3530 String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex)));
3531 int pattern_length = pattern->length();
3532 if (pattern_length == 1) {
3533 if (SearchCharMultiple(subject, pattern, last_match_info, &builder)) {
3534 return *builder.ToJSArray(result_array);
3535 }
3536 return Heap::null_value();
3537 }
3538
3539 if (!pattern->IsFlat()) FlattenString(pattern);
3540 if (SearchStringMultiple(subject, pattern, last_match_info, &builder)) {
3541 return *builder.ToJSArray(result_array);
3542 }
3543 return Heap::null_value();
3544 }
3545
3546 ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
3547
3548 RegExpImpl::IrregexpResult result;
3549 if (regexp->CaptureCount() == 0) {
3550 result = SearchRegExpNoCaptureMultiple(subject,
3551 regexp,
3552 last_match_info,
3553 &builder);
3554 } else {
3555 result = SearchRegExpMultiple(subject, regexp, last_match_info, &builder);
3556 }
3557 if (result == RegExpImpl::RE_SUCCESS) return *builder.ToJSArray(result_array);
3558 if (result == RegExpImpl::RE_FAILURE) return Heap::null_value();
3559 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3560 return Failure::Exception();
3561}
3562
3563
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003564static Object* Runtime_NumberToRadixString(Arguments args) {
3565 NoHandleAllocation ha;
3566 ASSERT(args.length() == 2);
3567
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003568 // Fast case where the result is a one character string.
3569 if (args[0]->IsSmi() && args[1]->IsSmi()) {
3570 int value = Smi::cast(args[0])->value();
3571 int radix = Smi::cast(args[1])->value();
3572 if (value >= 0 && value < radix) {
3573 RUNTIME_ASSERT(radix <= 36);
3574 // Character array used for conversion.
3575 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
3576 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
3577 }
3578 }
3579
3580 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003581 CONVERT_DOUBLE_CHECKED(value, args[0]);
3582 if (isnan(value)) {
3583 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3584 }
3585 if (isinf(value)) {
3586 if (value < 0) {
3587 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3588 }
3589 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3590 }
3591 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
3592 int radix = FastD2I(radix_number);
3593 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3594 char* str = DoubleToRadixCString(value, radix);
3595 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3596 DeleteArray(str);
3597 return result;
3598}
3599
3600
3601static Object* Runtime_NumberToFixed(Arguments args) {
3602 NoHandleAllocation ha;
3603 ASSERT(args.length() == 2);
3604
3605 CONVERT_DOUBLE_CHECKED(value, args[0]);
3606 if (isnan(value)) {
3607 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3608 }
3609 if (isinf(value)) {
3610 if (value < 0) {
3611 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3612 }
3613 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3614 }
3615 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3616 int f = FastD2I(f_number);
3617 RUNTIME_ASSERT(f >= 0);
3618 char* str = DoubleToFixedCString(value, f);
3619 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3620 DeleteArray(str);
3621 return res;
3622}
3623
3624
3625static Object* Runtime_NumberToExponential(Arguments args) {
3626 NoHandleAllocation ha;
3627 ASSERT(args.length() == 2);
3628
3629 CONVERT_DOUBLE_CHECKED(value, args[0]);
3630 if (isnan(value)) {
3631 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3632 }
3633 if (isinf(value)) {
3634 if (value < 0) {
3635 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3636 }
3637 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3638 }
3639 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3640 int f = FastD2I(f_number);
3641 RUNTIME_ASSERT(f >= -1 && f <= 20);
3642 char* str = DoubleToExponentialCString(value, f);
3643 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3644 DeleteArray(str);
3645 return res;
3646}
3647
3648
3649static Object* Runtime_NumberToPrecision(Arguments args) {
3650 NoHandleAllocation ha;
3651 ASSERT(args.length() == 2);
3652
3653 CONVERT_DOUBLE_CHECKED(value, args[0]);
3654 if (isnan(value)) {
3655 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3656 }
3657 if (isinf(value)) {
3658 if (value < 0) {
3659 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3660 }
3661 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3662 }
3663 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3664 int f = FastD2I(f_number);
3665 RUNTIME_ASSERT(f >= 1 && f <= 21);
3666 char* str = DoubleToPrecisionCString(value, f);
3667 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3668 DeleteArray(str);
3669 return res;
3670}
3671
3672
3673// Returns a single character string where first character equals
3674// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003675static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003676 if (index < static_cast<uint32_t>(string->length())) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003677 string->TryFlatten();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003678 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003679 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003680 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003681 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003682}
3683
3684
3685Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
3686 // Handle [] indexing on Strings
3687 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003688 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
3689 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003690 }
3691
3692 // Handle [] indexing on String objects
3693 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003694 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
3695 Handle<Object> result =
3696 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
3697 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003698 }
3699
3700 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003701 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003702 return prototype->GetElement(index);
3703 }
3704
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003705 return GetElement(object, index);
3706}
3707
3708
3709Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003710 return object->GetElement(index);
3711}
3712
3713
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003714Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
3715 HandleScope scope;
3716
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003717 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003718 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003719 Handle<Object> error =
3720 Factory::NewTypeError("non_object_property_load",
3721 HandleVector(args, 2));
3722 return Top::Throw(*error);
3723 }
3724
3725 // Check if the given key is an array index.
3726 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003727 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003728 return GetElementOrCharAt(object, index);
3729 }
3730
3731 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003732 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003733 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003734 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003735 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003736 bool has_pending_exception = false;
3737 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003738 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003739 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003740 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003741 }
3742
ager@chromium.org32912102009-01-16 10:38:43 +00003743 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003744 // the element if so.
3745 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003746 return GetElementOrCharAt(object, index);
3747 } else {
3748 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003749 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003750 }
3751}
3752
3753
3754static Object* Runtime_GetProperty(Arguments args) {
3755 NoHandleAllocation ha;
3756 ASSERT(args.length() == 2);
3757
3758 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003759 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003760
3761 return Runtime::GetObjectProperty(object, key);
3762}
3763
3764
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003765// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003766static Object* Runtime_KeyedGetProperty(Arguments args) {
3767 NoHandleAllocation ha;
3768 ASSERT(args.length() == 2);
3769
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003770 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00003771 // itself.
3772 //
3773 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00003774 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00003775 // global proxy object never has properties. This is the case
3776 // because the global proxy object forwards everything to its hidden
3777 // prototype including local lookups.
3778 //
3779 // Additionally, we need to make sure that we do not cache results
3780 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003781 if (args[0]->IsJSObject() &&
3782 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00003783 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003784 args[1]->IsString()) {
3785 JSObject* receiver = JSObject::cast(args[0]);
3786 String* key = String::cast(args[1]);
3787 if (receiver->HasFastProperties()) {
3788 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003789 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003790 int offset = KeyedLookupCache::Lookup(receiver_map, key);
3791 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003792 Object* value = receiver->FastPropertyAt(offset);
3793 return value->IsTheHole() ? Heap::undefined_value() : value;
3794 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003795 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003796 LookupResult result;
3797 receiver->LocalLookup(key, &result);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00003798 if (result.IsProperty() && result.type() == FIELD) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003799 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003800 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003801 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003802 }
3803 } else {
3804 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003805 StringDictionary* dictionary = receiver->property_dictionary();
3806 int entry = dictionary->FindEntry(key);
3807 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003808 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003809 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003810 if (!receiver->IsGlobalObject()) return value;
3811 value = JSGlobalPropertyCell::cast(value)->value();
3812 if (!value->IsTheHole()) return value;
3813 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003814 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003815 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003816 } else if (args[0]->IsString() && args[1]->IsSmi()) {
3817 // Fast case for string indexing using [] with a smi index.
3818 HandleScope scope;
3819 Handle<String> str = args.at<String>(0);
3820 int index = Smi::cast(args[1])->value();
3821 Handle<Object> result = GetCharAt(str, index);
3822 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003823 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003824
3825 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003826 return Runtime::GetObjectProperty(args.at<Object>(0),
3827 args.at<Object>(1));
3828}
3829
3830
ager@chromium.org5c838252010-02-19 08:53:10 +00003831static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
3832 ASSERT(args.length() == 5);
3833 HandleScope scope;
3834 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3835 CONVERT_CHECKED(String, name, args[1]);
3836 CONVERT_CHECKED(Smi, flag_setter, args[2]);
3837 CONVERT_CHECKED(JSFunction, fun, args[3]);
3838 CONVERT_CHECKED(Smi, flag_attr, args[4]);
3839 int unchecked = flag_attr->value();
3840 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3841 RUNTIME_ASSERT(!obj->IsNull());
3842 LookupResult result;
3843 obj->LocalLookupRealNamedProperty(name, &result);
3844
3845 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3846 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
3847 // delete it to avoid running into trouble in DefineAccessor, which
3848 // handles this incorrectly if the property is readonly (does nothing)
3849 if (result.IsProperty() &&
3850 (result.type() == FIELD || result.type() == NORMAL
3851 || result.type() == CONSTANT_FUNCTION)) {
3852 obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
3853 }
3854 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
3855}
3856
3857static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
3858 ASSERT(args.length() == 4);
3859 HandleScope scope;
3860 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
3861 CONVERT_ARG_CHECKED(String, name, 1);
3862 Handle<Object> obj_value = args.at<Object>(2);
3863
3864 CONVERT_CHECKED(Smi, flag, args[3]);
3865 int unchecked = flag->value();
3866 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3867
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00003868 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3869
3870 // Check if this is an element.
3871 uint32_t index;
3872 bool is_element = name->AsArrayIndex(&index);
3873
3874 // Special case for elements if any of the flags are true.
3875 // If elements are in fast case we always implicitly assume that:
3876 // DONT_DELETE: false, DONT_ENUM: false, READ_ONLY: false.
3877 if (((unchecked & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) &&
3878 is_element) {
3879 // Normalize the elements to enable attributes on the property.
3880 js_object->NormalizeElements();
3881 NumberDictionary* dictionary = js_object->element_dictionary();
3882 // Make sure that we never go back to fast case.
3883 dictionary->set_requires_slow_elements();
3884 PropertyDetails details = PropertyDetails(attr, NORMAL);
3885 dictionary->Set(index, *obj_value, details);
3886 }
3887
ager@chromium.org5c838252010-02-19 08:53:10 +00003888 LookupResult result;
3889 js_object->LocalLookupRealNamedProperty(*name, &result);
3890
ager@chromium.org5c838252010-02-19 08:53:10 +00003891 // Take special care when attributes are different and there is already
3892 // a property. For simplicity we normalize the property which enables us
3893 // to not worry about changing the instance_descriptor and creating a new
3894 // map. The current version of SetObjectProperty does not handle attributes
3895 // correctly in the case where a property is a field and is reset with
3896 // new attributes.
3897 if (result.IsProperty() && attr != result.GetAttributes()) {
3898 // New attributes - normalize to avoid writing to instance descriptor
3899 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3900 // Use IgnoreAttributes version since a readonly property may be
3901 // overridden and SetProperty does not allow this.
3902 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
3903 *obj_value,
3904 attr);
3905 }
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00003906
ager@chromium.org5c838252010-02-19 08:53:10 +00003907 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
3908}
3909
3910
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003911Object* Runtime::SetObjectProperty(Handle<Object> object,
3912 Handle<Object> key,
3913 Handle<Object> value,
3914 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003915 HandleScope scope;
3916
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003917 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003918 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003919 Handle<Object> error =
3920 Factory::NewTypeError("non_object_property_store",
3921 HandleVector(args, 2));
3922 return Top::Throw(*error);
3923 }
3924
3925 // If the object isn't a JavaScript object, we ignore the store.
3926 if (!object->IsJSObject()) return *value;
3927
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003928 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3929
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003930 // Check if the given key is an array index.
3931 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003932 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003933 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3934 // of a string using [] notation. We need to support this too in
3935 // JavaScript.
3936 // In the case of a String object we just need to redirect the assignment to
3937 // the underlying string if the index is in range. Since the underlying
3938 // string does nothing with the assignment then we can ignore such
3939 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003940 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003941 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003942 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003943
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003944 Handle<Object> result = SetElement(js_object, index, value);
3945 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003946 return *value;
3947 }
3948
3949 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003950 Handle<Object> result;
3951 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003952 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003953 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003954 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003955 key_string->TryFlatten();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003956 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003957 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003958 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003959 return *value;
3960 }
3961
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003962 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003963 bool has_pending_exception = false;
3964 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3965 if (has_pending_exception) return Failure::Exception();
3966 Handle<String> name = Handle<String>::cast(converted);
3967
3968 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003969 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003970 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003971 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003972 }
3973}
3974
3975
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003976Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
3977 Handle<Object> key,
3978 Handle<Object> value,
3979 PropertyAttributes attr) {
3980 HandleScope scope;
3981
3982 // Check if the given key is an array index.
3983 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003984 if (key->ToArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003985 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3986 // of a string using [] notation. We need to support this too in
3987 // JavaScript.
3988 // In the case of a String object we just need to redirect the assignment to
3989 // the underlying string if the index is in range. Since the underlying
3990 // string does nothing with the assignment then we can ignore such
3991 // assignments.
3992 if (js_object->IsStringObjectWithCharacterAt(index)) {
3993 return *value;
3994 }
3995
3996 return js_object->SetElement(index, *value);
3997 }
3998
3999 if (key->IsString()) {
4000 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004001 return js_object->SetElement(index, *value);
4002 } else {
4003 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004004 key_string->TryFlatten();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004005 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
4006 *value,
4007 attr);
4008 }
4009 }
4010
4011 // Call-back into JavaScript to convert the key to a string.
4012 bool has_pending_exception = false;
4013 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4014 if (has_pending_exception) return Failure::Exception();
4015 Handle<String> name = Handle<String>::cast(converted);
4016
4017 if (name->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004018 return js_object->SetElement(index, *value);
4019 } else {
4020 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
4021 }
4022}
4023
4024
ager@chromium.orge2902be2009-06-08 12:21:35 +00004025Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
4026 Handle<Object> key) {
4027 HandleScope scope;
4028
4029 // Check if the given key is an array index.
4030 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004031 if (key->ToArrayIndex(&index)) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00004032 // In Firefox/SpiderMonkey, Safari and Opera you can access the
4033 // characters of a string using [] notation. In the case of a
4034 // String object we just need to redirect the deletion to the
4035 // underlying string if the index is in range. Since the
4036 // underlying string does nothing with the deletion, we can ignore
4037 // such deletions.
4038 if (js_object->IsStringObjectWithCharacterAt(index)) {
4039 return Heap::true_value();
4040 }
4041
4042 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
4043 }
4044
4045 Handle<String> key_string;
4046 if (key->IsString()) {
4047 key_string = Handle<String>::cast(key);
4048 } else {
4049 // Call-back into JavaScript to convert the key to a string.
4050 bool has_pending_exception = false;
4051 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4052 if (has_pending_exception) return Failure::Exception();
4053 key_string = Handle<String>::cast(converted);
4054 }
4055
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004056 key_string->TryFlatten();
ager@chromium.orge2902be2009-06-08 12:21:35 +00004057 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
4058}
4059
4060
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004061static Object* Runtime_SetProperty(Arguments args) {
4062 NoHandleAllocation ha;
4063 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
4064
4065 Handle<Object> object = args.at<Object>(0);
4066 Handle<Object> key = args.at<Object>(1);
4067 Handle<Object> value = args.at<Object>(2);
4068
4069 // Compute attributes.
4070 PropertyAttributes attributes = NONE;
4071 if (args.length() == 4) {
4072 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004073 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004074 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004075 RUNTIME_ASSERT(
4076 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4077 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004078 }
4079 return Runtime::SetObjectProperty(object, key, value, attributes);
4080}
4081
4082
4083// Set a local property, even if it is READ_ONLY. If the property does not
4084// exist, it will be added with attributes NONE.
4085static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
4086 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004087 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004088 CONVERT_CHECKED(JSObject, object, args[0]);
4089 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004090 // Compute attributes.
4091 PropertyAttributes attributes = NONE;
4092 if (args.length() == 4) {
4093 CONVERT_CHECKED(Smi, value_obj, args[3]);
4094 int unchecked_value = value_obj->value();
4095 // Only attribute bits should be set.
4096 RUNTIME_ASSERT(
4097 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4098 attributes = static_cast<PropertyAttributes>(unchecked_value);
4099 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004100
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004101 return object->
4102 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004103}
4104
4105
4106static Object* Runtime_DeleteProperty(Arguments args) {
4107 NoHandleAllocation ha;
4108 ASSERT(args.length() == 2);
4109
4110 CONVERT_CHECKED(JSObject, object, args[0]);
4111 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00004112 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004113}
4114
4115
ager@chromium.org9085a012009-05-11 19:22:57 +00004116static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
4117 Handle<String> key) {
4118 if (object->HasLocalProperty(*key)) return Heap::true_value();
4119 // Handle hidden prototypes. If there's a hidden prototype above this thing
4120 // then we have to check it for properties, because they are supposed to
4121 // look like they are on this object.
4122 Handle<Object> proto(object->GetPrototype());
4123 if (proto->IsJSObject() &&
4124 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
4125 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
4126 }
4127 return Heap::false_value();
4128}
4129
4130
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004131static Object* Runtime_HasLocalProperty(Arguments args) {
4132 NoHandleAllocation ha;
4133 ASSERT(args.length() == 2);
4134 CONVERT_CHECKED(String, key, args[1]);
4135
ager@chromium.org9085a012009-05-11 19:22:57 +00004136 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004137 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00004138 if (obj->IsJSObject()) {
4139 JSObject* object = JSObject::cast(obj);
4140 // Fast case - no interceptors.
4141 if (object->HasRealNamedProperty(key)) return Heap::true_value();
4142 // Slow case. Either it's not there or we have an interceptor. We should
4143 // have handles for this kind of deal.
4144 HandleScope scope;
4145 return HasLocalPropertyImplementation(Handle<JSObject>(object),
4146 Handle<String>(key));
4147 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004148 // Well, there is one exception: Handle [] on strings.
4149 uint32_t index;
4150 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00004151 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004152 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004153 return Heap::true_value();
4154 }
4155 }
4156 return Heap::false_value();
4157}
4158
4159
4160static Object* Runtime_HasProperty(Arguments args) {
4161 NoHandleAllocation na;
4162 ASSERT(args.length() == 2);
4163
4164 // Only JS objects can have properties.
4165 if (args[0]->IsJSObject()) {
4166 JSObject* object = JSObject::cast(args[0]);
4167 CONVERT_CHECKED(String, key, args[1]);
4168 if (object->HasProperty(key)) return Heap::true_value();
4169 }
4170 return Heap::false_value();
4171}
4172
4173
4174static Object* Runtime_HasElement(Arguments args) {
4175 NoHandleAllocation na;
4176 ASSERT(args.length() == 2);
4177
4178 // Only JS objects can have elements.
4179 if (args[0]->IsJSObject()) {
4180 JSObject* object = JSObject::cast(args[0]);
4181 CONVERT_CHECKED(Smi, index_obj, args[1]);
4182 uint32_t index = index_obj->value();
4183 if (object->HasElement(index)) return Heap::true_value();
4184 }
4185 return Heap::false_value();
4186}
4187
4188
4189static Object* Runtime_IsPropertyEnumerable(Arguments args) {
4190 NoHandleAllocation ha;
4191 ASSERT(args.length() == 2);
4192
4193 CONVERT_CHECKED(JSObject, object, args[0]);
4194 CONVERT_CHECKED(String, key, args[1]);
4195
4196 uint32_t index;
4197 if (key->AsArrayIndex(&index)) {
4198 return Heap::ToBoolean(object->HasElement(index));
4199 }
4200
ager@chromium.org870a0b62008-11-04 11:43:05 +00004201 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
4202 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004203}
4204
4205
4206static Object* Runtime_GetPropertyNames(Arguments args) {
4207 HandleScope scope;
4208 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004209 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004210 return *GetKeysFor(object);
4211}
4212
4213
4214// Returns either a FixedArray as Runtime_GetPropertyNames,
4215// or, if the given object has an enum cache that contains
4216// all enumerable properties of the object and its prototypes
4217// have none, the map of the object. This is used to speed up
4218// the check for deletions during a for-in.
4219static Object* Runtime_GetPropertyNamesFast(Arguments args) {
4220 ASSERT(args.length() == 1);
4221
4222 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4223
4224 if (raw_object->IsSimpleEnum()) return raw_object->map();
4225
4226 HandleScope scope;
4227 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004228 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
4229 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004230
4231 // Test again, since cache may have been built by preceding call.
4232 if (object->IsSimpleEnum()) return object->map();
4233
4234 return *content;
4235}
4236
4237
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004238// Find the length of the prototype chain that is to to handled as one. If a
4239// prototype object is hidden it is to be viewed as part of the the object it
4240// is prototype for.
4241static int LocalPrototypeChainLength(JSObject* obj) {
4242 int count = 1;
4243 Object* proto = obj->GetPrototype();
4244 while (proto->IsJSObject() &&
4245 JSObject::cast(proto)->map()->is_hidden_prototype()) {
4246 count++;
4247 proto = JSObject::cast(proto)->GetPrototype();
4248 }
4249 return count;
4250}
4251
4252
4253// Return the names of the local named properties.
4254// args[0]: object
4255static Object* Runtime_GetLocalPropertyNames(Arguments args) {
4256 HandleScope scope;
4257 ASSERT(args.length() == 1);
4258 if (!args[0]->IsJSObject()) {
4259 return Heap::undefined_value();
4260 }
4261 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4262
4263 // Skip the global proxy as it has no properties and always delegates to the
4264 // real global object.
4265 if (obj->IsJSGlobalProxy()) {
4266 // Only collect names if access is permitted.
4267 if (obj->IsAccessCheckNeeded() &&
4268 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
4269 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
4270 return *Factory::NewJSArray(0);
4271 }
4272 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
4273 }
4274
4275 // Find the number of objects making up this.
4276 int length = LocalPrototypeChainLength(*obj);
4277
4278 // Find the number of local properties for each of the objects.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004279 ScopedVector<int> local_property_count(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004280 int total_property_count = 0;
4281 Handle<JSObject> jsproto = obj;
4282 for (int i = 0; i < length; i++) {
4283 // Only collect names if access is permitted.
4284 if (jsproto->IsAccessCheckNeeded() &&
4285 !Top::MayNamedAccess(*jsproto,
4286 Heap::undefined_value(),
4287 v8::ACCESS_KEYS)) {
4288 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
4289 return *Factory::NewJSArray(0);
4290 }
4291 int n;
4292 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4293 local_property_count[i] = n;
4294 total_property_count += n;
4295 if (i < length - 1) {
4296 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4297 }
4298 }
4299
4300 // Allocate an array with storage for all the property names.
4301 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
4302
4303 // Get the property names.
4304 jsproto = obj;
4305 int proto_with_hidden_properties = 0;
4306 for (int i = 0; i < length; i++) {
4307 jsproto->GetLocalPropertyNames(*names,
4308 i == 0 ? 0 : local_property_count[i - 1]);
4309 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
4310 proto_with_hidden_properties++;
4311 }
4312 if (i < length - 1) {
4313 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4314 }
4315 }
4316
4317 // Filter out name of hidden propeties object.
4318 if (proto_with_hidden_properties > 0) {
4319 Handle<FixedArray> old_names = names;
4320 names = Factory::NewFixedArray(
4321 names->length() - proto_with_hidden_properties);
4322 int dest_pos = 0;
4323 for (int i = 0; i < total_property_count; i++) {
4324 Object* name = old_names->get(i);
4325 if (name == Heap::hidden_symbol()) {
4326 continue;
4327 }
4328 names->set(dest_pos++, name);
4329 }
4330 }
4331
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004332 return *Factory::NewJSArrayWithElements(names);
4333}
4334
4335
4336// Return the names of the local indexed properties.
4337// args[0]: object
4338static Object* Runtime_GetLocalElementNames(Arguments args) {
4339 HandleScope scope;
4340 ASSERT(args.length() == 1);
4341 if (!args[0]->IsJSObject()) {
4342 return Heap::undefined_value();
4343 }
4344 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4345
4346 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4347 Handle<FixedArray> names = Factory::NewFixedArray(n);
4348 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4349 return *Factory::NewJSArrayWithElements(names);
4350}
4351
4352
4353// Return information on whether an object has a named or indexed interceptor.
4354// args[0]: object
4355static Object* Runtime_GetInterceptorInfo(Arguments args) {
4356 HandleScope scope;
4357 ASSERT(args.length() == 1);
4358 if (!args[0]->IsJSObject()) {
4359 return Smi::FromInt(0);
4360 }
4361 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4362
4363 int result = 0;
4364 if (obj->HasNamedInterceptor()) result |= 2;
4365 if (obj->HasIndexedInterceptor()) result |= 1;
4366
4367 return Smi::FromInt(result);
4368}
4369
4370
4371// Return property names from named interceptor.
4372// args[0]: object
4373static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
4374 HandleScope scope;
4375 ASSERT(args.length() == 1);
4376 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4377
4378 if (obj->HasNamedInterceptor()) {
4379 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4380 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4381 }
4382 return Heap::undefined_value();
4383}
4384
4385
4386// Return element names from indexed interceptor.
4387// args[0]: object
4388static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
4389 HandleScope scope;
4390 ASSERT(args.length() == 1);
4391 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4392
4393 if (obj->HasIndexedInterceptor()) {
4394 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4395 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4396 }
4397 return Heap::undefined_value();
4398}
4399
4400
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004401static Object* Runtime_LocalKeys(Arguments args) {
4402 ASSERT_EQ(args.length(), 1);
4403 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4404 HandleScope scope;
4405 Handle<JSObject> object(raw_object);
4406 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
4407 LOCAL_ONLY);
4408 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
4409 // property array and since the result is mutable we have to create
4410 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004411 int length = contents->length();
4412 Handle<FixedArray> copy = Factory::NewFixedArray(length);
4413 for (int i = 0; i < length; i++) {
4414 Object* entry = contents->get(i);
4415 if (entry->IsString()) {
4416 copy->set(i, entry);
4417 } else {
4418 ASSERT(entry->IsNumber());
4419 HandleScope scope;
4420 Handle<Object> entry_handle(entry);
4421 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
4422 copy->set(i, *entry_str);
4423 }
4424 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004425 return *Factory::NewJSArrayWithElements(copy);
4426}
4427
4428
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004429static Object* Runtime_GetArgumentsProperty(Arguments args) {
4430 NoHandleAllocation ha;
4431 ASSERT(args.length() == 1);
4432
4433 // Compute the frame holding the arguments.
4434 JavaScriptFrameIterator it;
4435 it.AdvanceToArgumentsFrame();
4436 JavaScriptFrame* frame = it.frame();
4437
4438 // Get the actual number of provided arguments.
4439 const uint32_t n = frame->GetProvidedParametersCount();
4440
4441 // Try to convert the key to an index. If successful and within
4442 // index return the the argument from the frame.
4443 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004444 if (args[0]->ToArrayIndex(&index) && index < n) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004445 return frame->GetParameter(index);
4446 }
4447
4448 // Convert the key to a string.
4449 HandleScope scope;
4450 bool exception = false;
4451 Handle<Object> converted =
4452 Execution::ToString(args.at<Object>(0), &exception);
4453 if (exception) return Failure::Exception();
4454 Handle<String> key = Handle<String>::cast(converted);
4455
4456 // Try to convert the string key into an array index.
4457 if (key->AsArrayIndex(&index)) {
4458 if (index < n) {
4459 return frame->GetParameter(index);
4460 } else {
4461 return Top::initial_object_prototype()->GetElement(index);
4462 }
4463 }
4464
4465 // Handle special arguments properties.
4466 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
4467 if (key->Equals(Heap::callee_symbol())) return frame->function();
4468
4469 // Lookup in the initial Object.prototype object.
4470 return Top::initial_object_prototype()->GetProperty(*key);
4471}
4472
4473
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004474static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004475 HandleScope scope;
4476
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004477 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004478 Handle<Object> object = args.at<Object>(0);
4479 if (object->IsJSObject()) {
4480 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00004481 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
4482 js_object->TransformToFastProperties(0);
4483 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004484 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004485 return *object;
4486}
4487
4488
4489static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004490 HandleScope scope;
4491
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004492 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004493 Handle<Object> object = args.at<Object>(0);
4494 if (object->IsJSObject()) {
4495 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004496 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004497 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004498 return *object;
4499}
4500
4501
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004502static Object* Runtime_ToBool(Arguments args) {
4503 NoHandleAllocation ha;
4504 ASSERT(args.length() == 1);
4505
4506 return args[0]->ToBoolean();
4507}
4508
4509
4510// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
4511// Possible optimizations: put the type string into the oddballs.
4512static Object* Runtime_Typeof(Arguments args) {
4513 NoHandleAllocation ha;
4514
4515 Object* obj = args[0];
4516 if (obj->IsNumber()) return Heap::number_symbol();
4517 HeapObject* heap_obj = HeapObject::cast(obj);
4518
4519 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004520 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004521
4522 InstanceType instance_type = heap_obj->map()->instance_type();
4523 if (instance_type < FIRST_NONSTRING_TYPE) {
4524 return Heap::string_symbol();
4525 }
4526
4527 switch (instance_type) {
4528 case ODDBALL_TYPE:
4529 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
4530 return Heap::boolean_symbol();
4531 }
4532 if (heap_obj->IsNull()) {
4533 return Heap::object_symbol();
4534 }
4535 ASSERT(heap_obj->IsUndefined());
4536 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004537 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004538 return Heap::function_symbol();
4539 default:
4540 // For any kind of object not handled above, the spec rule for
4541 // host objects gives that it is okay to return "object"
4542 return Heap::object_symbol();
4543 }
4544}
4545
4546
lrn@chromium.org25156de2010-04-06 13:10:27 +00004547static bool AreDigits(const char*s, int from, int to) {
4548 for (int i = from; i < to; i++) {
4549 if (s[i] < '0' || s[i] > '9') return false;
4550 }
4551
4552 return true;
4553}
4554
4555
4556static int ParseDecimalInteger(const char*s, int from, int to) {
4557 ASSERT(to - from < 10); // Overflow is not possible.
4558 ASSERT(from < to);
4559 int d = s[from] - '0';
4560
4561 for (int i = from + 1; i < to; i++) {
4562 d = 10 * d + (s[i] - '0');
4563 }
4564
4565 return d;
4566}
4567
4568
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004569static Object* Runtime_StringToNumber(Arguments args) {
4570 NoHandleAllocation ha;
4571 ASSERT(args.length() == 1);
4572 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004573 subject->TryFlatten();
lrn@chromium.org25156de2010-04-06 13:10:27 +00004574
4575 // Fast case: short integer or some sorts of junk values.
4576 int len = subject->length();
4577 if (subject->IsSeqAsciiString()) {
4578 if (len == 0) return Smi::FromInt(0);
4579
4580 char const* data = SeqAsciiString::cast(subject)->GetChars();
4581 bool minus = (data[0] == '-');
4582 int start_pos = (minus ? 1 : 0);
4583
4584 if (start_pos == len) {
4585 return Heap::nan_value();
4586 } else if (data[start_pos] > '9') {
4587 // Fast check for a junk value. A valid string may start from a
4588 // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit or
4589 // the 'I' character ('Infinity'). All of that have codes not greater than
4590 // '9' except 'I'.
4591 if (data[start_pos] != 'I') {
4592 return Heap::nan_value();
4593 }
4594 } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
4595 // The maximal/minimal smi has 10 digits. If the string has less digits we
4596 // know it will fit into the smi-data type.
4597 int d = ParseDecimalInteger(data, start_pos, len);
4598 if (minus) {
4599 if (d == 0) return Heap::minus_zero_value();
4600 d = -d;
4601 }
4602 return Smi::FromInt(d);
4603 }
4604 }
4605
4606 // Slower case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004607 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
4608}
4609
4610
4611static Object* Runtime_StringFromCharCodeArray(Arguments args) {
4612 NoHandleAllocation ha;
4613 ASSERT(args.length() == 1);
4614
4615 CONVERT_CHECKED(JSArray, codes, args[0]);
4616 int length = Smi::cast(codes->length())->value();
4617
4618 // Check if the string can be ASCII.
4619 int i;
4620 for (i = 0; i < length; i++) {
4621 Object* element = codes->GetElement(i);
4622 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
4623 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
4624 break;
4625 }
4626
4627 Object* object = NULL;
4628 if (i == length) { // The string is ASCII.
4629 object = Heap::AllocateRawAsciiString(length);
4630 } else { // The string is not ASCII.
4631 object = Heap::AllocateRawTwoByteString(length);
4632 }
4633
4634 if (object->IsFailure()) return object;
4635 String* result = String::cast(object);
4636 for (int i = 0; i < length; i++) {
4637 Object* element = codes->GetElement(i);
4638 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004639 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004640 }
4641 return result;
4642}
4643
4644
4645// kNotEscaped is generated by the following:
4646//
4647// #!/bin/perl
4648// for (my $i = 0; $i < 256; $i++) {
4649// print "\n" if $i % 16 == 0;
4650// my $c = chr($i);
4651// my $escaped = 1;
4652// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
4653// print $escaped ? "0, " : "1, ";
4654// }
4655
4656
4657static bool IsNotEscaped(uint16_t character) {
4658 // Only for 8 bit characters, the rest are always escaped (in a different way)
4659 ASSERT(character < 256);
4660 static const char kNotEscaped[256] = {
4661 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4662 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4663 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
4664 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
4665 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4666 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
4667 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4668 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
4669 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4670 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4671 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4672 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4673 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4674 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4675 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4676 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4677 };
4678 return kNotEscaped[character] != 0;
4679}
4680
4681
4682static Object* Runtime_URIEscape(Arguments args) {
4683 const char hex_chars[] = "0123456789ABCDEF";
4684 NoHandleAllocation ha;
4685 ASSERT(args.length() == 1);
4686 CONVERT_CHECKED(String, source, args[0]);
4687
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004688 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004689
4690 int escaped_length = 0;
4691 int length = source->length();
4692 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004693 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004694 buffer->Reset(source);
4695 while (buffer->has_more()) {
4696 uint16_t character = buffer->GetNext();
4697 if (character >= 256) {
4698 escaped_length += 6;
4699 } else if (IsNotEscaped(character)) {
4700 escaped_length++;
4701 } else {
4702 escaped_length += 3;
4703 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004704 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004705 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004706 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004707 Top::context()->mark_out_of_memory();
4708 return Failure::OutOfMemoryException();
4709 }
4710 }
4711 }
4712 // No length change implies no change. Return original string if no change.
4713 if (escaped_length == length) {
4714 return source;
4715 }
4716 Object* o = Heap::AllocateRawAsciiString(escaped_length);
4717 if (o->IsFailure()) return o;
4718 String* destination = String::cast(o);
4719 int dest_position = 0;
4720
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004721 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004722 buffer->Rewind();
4723 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00004724 uint16_t chr = buffer->GetNext();
4725 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004726 destination->Set(dest_position, '%');
4727 destination->Set(dest_position+1, 'u');
4728 destination->Set(dest_position+2, hex_chars[chr >> 12]);
4729 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
4730 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
4731 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004732 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00004733 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004734 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004735 dest_position++;
4736 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004737 destination->Set(dest_position, '%');
4738 destination->Set(dest_position+1, hex_chars[chr >> 4]);
4739 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004740 dest_position += 3;
4741 }
4742 }
4743 return destination;
4744}
4745
4746
4747static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
4748 static const signed char kHexValue['g'] = {
4749 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4750 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4751 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4752 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
4753 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4754 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4755 -1, 10, 11, 12, 13, 14, 15 };
4756
4757 if (character1 > 'f') return -1;
4758 int hi = kHexValue[character1];
4759 if (hi == -1) return -1;
4760 if (character2 > 'f') return -1;
4761 int lo = kHexValue[character2];
4762 if (lo == -1) return -1;
4763 return (hi << 4) + lo;
4764}
4765
4766
ager@chromium.org870a0b62008-11-04 11:43:05 +00004767static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004768 int i,
4769 int length,
4770 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004771 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00004772 int32_t hi = 0;
4773 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004774 if (character == '%' &&
4775 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004776 source->Get(i + 1) == 'u' &&
4777 (hi = TwoDigitHex(source->Get(i + 2),
4778 source->Get(i + 3))) != -1 &&
4779 (lo = TwoDigitHex(source->Get(i + 4),
4780 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004781 *step = 6;
4782 return (hi << 8) + lo;
4783 } else if (character == '%' &&
4784 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004785 (lo = TwoDigitHex(source->Get(i + 1),
4786 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004787 *step = 3;
4788 return lo;
4789 } else {
4790 *step = 1;
4791 return character;
4792 }
4793}
4794
4795
4796static Object* Runtime_URIUnescape(Arguments args) {
4797 NoHandleAllocation ha;
4798 ASSERT(args.length() == 1);
4799 CONVERT_CHECKED(String, source, args[0]);
4800
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004801 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004802
4803 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004804 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004805
4806 int unescaped_length = 0;
4807 for (int i = 0; i < length; unescaped_length++) {
4808 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004809 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004810 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004811 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004812 i += step;
4813 }
4814
4815 // No length change implies no change. Return original string if no change.
4816 if (unescaped_length == length)
4817 return source;
4818
4819 Object* o = ascii ?
4820 Heap::AllocateRawAsciiString(unescaped_length) :
4821 Heap::AllocateRawTwoByteString(unescaped_length);
4822 if (o->IsFailure()) return o;
4823 String* destination = String::cast(o);
4824
4825 int dest_position = 0;
4826 for (int i = 0; i < length; dest_position++) {
4827 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004828 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004829 i += step;
4830 }
4831 return destination;
4832}
4833
4834
4835static Object* Runtime_StringParseInt(Arguments args) {
4836 NoHandleAllocation ha;
4837
4838 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004839 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004840
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004841 s->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004842
lrn@chromium.org25156de2010-04-06 13:10:27 +00004843 RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
4844 double value = StringToInt(s, radix);
4845 return Heap::NumberFromDouble(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004846 return Heap::nan_value();
4847}
4848
4849
4850static Object* Runtime_StringParseFloat(Arguments args) {
4851 NoHandleAllocation ha;
4852 CONVERT_CHECKED(String, str, args[0]);
4853
4854 // ECMA-262 section 15.1.2.3, empty string is NaN
4855 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
4856
4857 // Create a number object from the value.
4858 return Heap::NumberFromDouble(value);
4859}
4860
4861
4862static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
4863static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
4864
4865
4866template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004867static Object* ConvertCaseHelper(String* s,
4868 int length,
4869 int input_string_length,
4870 unibrow::Mapping<Converter, 128>* mapping) {
4871 // We try this twice, once with the assumption that the result is no longer
4872 // than the input and, if that assumption breaks, again with the exact
4873 // length. This may not be pretty, but it is nicer than what was here before
4874 // and I hereby claim my vaffel-is.
4875 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004876 // Allocate the resulting string.
4877 //
4878 // NOTE: This assumes that the upper/lower case of an ascii
4879 // character is also ascii. This is currently the case, but it
4880 // might break in the future if we implement more context and locale
4881 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00004882 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004883 ? Heap::AllocateRawAsciiString(length)
4884 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004885 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004886 String* result = String::cast(o);
4887 bool has_changed_character = false;
4888
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004889 // Convert all characters to upper case, assuming that they will fit
4890 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004891 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004892 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004893 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004894 // We can assume that the string is not empty
4895 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004896 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00004897 bool has_next = buffer->has_more();
4898 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004899 int char_length = mapping->get(current, next, chars);
4900 if (char_length == 0) {
4901 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004902 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004903 i++;
4904 } else if (char_length == 1) {
4905 // Common case: converting the letter resulted in one character.
4906 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004907 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004908 has_changed_character = true;
4909 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004910 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004911 // We've assumed that the result would be as long as the
4912 // input but here is a character that converts to several
4913 // characters. No matter, we calculate the exact length
4914 // of the result and try the whole thing again.
4915 //
4916 // Note that this leaves room for optimization. We could just
4917 // memcpy what we already have to the result string. Also,
4918 // the result string is the last object allocated we could
4919 // "realloc" it and probably, in the vast majority of cases,
4920 // extend the existing string to be able to hold the full
4921 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00004922 int next_length = 0;
4923 if (has_next) {
4924 next_length = mapping->get(next, 0, chars);
4925 if (next_length == 0) next_length = 1;
4926 }
4927 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004928 while (buffer->has_more()) {
4929 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00004930 // NOTE: we use 0 as the next character here because, while
4931 // the next character may affect what a character converts to,
4932 // it does not in any case affect the length of what it convert
4933 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004934 int char_length = mapping->get(current, 0, chars);
4935 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00004936 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004937 if (current_length > Smi::kMaxValue) {
4938 Top::context()->mark_out_of_memory();
4939 return Failure::OutOfMemoryException();
4940 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004941 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004942 // Try again with the real length.
4943 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004944 } else {
4945 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004946 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004947 i++;
4948 }
4949 has_changed_character = true;
4950 }
4951 current = next;
4952 }
4953 if (has_changed_character) {
4954 return result;
4955 } else {
4956 // If we didn't actually change anything in doing the conversion
4957 // we simple return the result and let the converted string
4958 // become garbage; there is no reason to keep two identical strings
4959 // alive.
4960 return s;
4961 }
4962}
4963
4964
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004965namespace {
4966
4967struct ToLowerTraits {
4968 typedef unibrow::ToLowercase UnibrowConverter;
4969
4970 static bool ConvertAscii(char* dst, char* src, int length) {
4971 bool changed = false;
4972 for (int i = 0; i < length; ++i) {
4973 char c = src[i];
4974 if ('A' <= c && c <= 'Z') {
4975 c += ('a' - 'A');
4976 changed = true;
4977 }
4978 dst[i] = c;
4979 }
4980 return changed;
4981 }
4982};
4983
4984
4985struct ToUpperTraits {
4986 typedef unibrow::ToUppercase UnibrowConverter;
4987
4988 static bool ConvertAscii(char* dst, char* src, int length) {
4989 bool changed = false;
4990 for (int i = 0; i < length; ++i) {
4991 char c = src[i];
4992 if ('a' <= c && c <= 'z') {
4993 c -= ('a' - 'A');
4994 changed = true;
4995 }
4996 dst[i] = c;
4997 }
4998 return changed;
4999 }
5000};
5001
5002} // namespace
5003
5004
5005template <typename ConvertTraits>
5006static Object* ConvertCase(
5007 Arguments args,
5008 unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005009 NoHandleAllocation ha;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005010 CONVERT_CHECKED(String, s, args[0]);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005011 s = s->TryFlattenGetString();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005012
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005013 const int length = s->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005014 // Assume that the string is not empty; we need this assumption later
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005015 if (length == 0) return s;
5016
5017 // Simpler handling of ascii strings.
5018 //
5019 // NOTE: This assumes that the upper/lower case of an ascii
5020 // character is also ascii. This is currently the case, but it
5021 // might break in the future if we implement more context and locale
5022 // dependent upper/lower conversions.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005023 if (s->IsSeqAsciiString()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005024 Object* o = Heap::AllocateRawAsciiString(length);
5025 if (o->IsFailure()) return o;
5026 SeqAsciiString* result = SeqAsciiString::cast(o);
5027 bool has_changed_character = ConvertTraits::ConvertAscii(
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005028 result->GetChars(), SeqAsciiString::cast(s)->GetChars(), length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005029 return has_changed_character ? result : s;
5030 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005031
5032 Object* answer = ConvertCaseHelper(s, length, length, mapping);
5033 if (answer->IsSmi()) {
5034 // Retry with correct length.
5035 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
5036 }
5037 return answer; // This may be a failure.
5038}
5039
5040
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005041static Object* Runtime_StringToLowerCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005042 return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005043}
5044
5045
5046static Object* Runtime_StringToUpperCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005047 return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005048}
5049
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005050
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005051static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
5052 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
5053}
5054
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005055
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005056static Object* Runtime_StringTrim(Arguments args) {
5057 NoHandleAllocation ha;
5058 ASSERT(args.length() == 3);
5059
5060 CONVERT_CHECKED(String, s, args[0]);
5061 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
5062 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
5063
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005064 s->TryFlatten();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005065 int length = s->length();
5066
5067 int left = 0;
5068 if (trimLeft) {
5069 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
5070 left++;
5071 }
5072 }
5073
5074 int right = length;
5075 if (trimRight) {
5076 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
5077 right--;
5078 }
5079 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005080 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005081}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005082
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005083
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005084template <typename schar, typename pchar>
5085void FindStringIndices(Vector<const schar> subject,
5086 Vector<const pchar> pattern,
5087 ZoneList<int>* indices,
5088 unsigned int limit) {
5089 ASSERT(limit > 0);
5090 // Collect indices of pattern in subject, and the end-of-string index.
5091 // Stop after finding at most limit values.
5092 StringSearchStrategy strategy =
5093 InitializeStringSearch(pattern, sizeof(schar) == 1);
5094 switch (strategy) {
5095 case SEARCH_FAIL: return;
5096 case SEARCH_SHORT: {
5097 int pattern_length = pattern.length();
5098 int index = 0;
5099 while (limit > 0) {
5100 index = SimpleIndexOf(subject, pattern, index);
5101 if (index < 0) return;
5102 indices->Add(index);
5103 index += pattern_length;
5104 limit--;
5105 }
5106 return;
5107 }
5108 case SEARCH_LONG: {
5109 int pattern_length = pattern.length();
5110 int index = 0;
5111 while (limit > 0) {
5112 index = ComplexIndexOf(subject, pattern, index);
5113 if (index < 0) return;
5114 indices->Add(index);
5115 index += pattern_length;
5116 limit--;
5117 }
5118 return;
5119 }
5120 default:
5121 UNREACHABLE();
5122 return;
5123 }
5124}
5125
5126template <typename schar>
5127inline void FindCharIndices(Vector<const schar> subject,
5128 const schar pattern_char,
5129 ZoneList<int>* indices,
5130 unsigned int limit) {
5131 // Collect indices of pattern_char in subject, and the end-of-string index.
5132 // Stop after finding at most limit values.
5133 int index = 0;
5134 while (limit > 0) {
5135 index = SingleCharIndexOf(subject, pattern_char, index);
5136 if (index < 0) return;
5137 indices->Add(index);
5138 index++;
5139 limit--;
5140 }
5141}
5142
5143
5144static Object* Runtime_StringSplit(Arguments args) {
5145 ASSERT(args.length() == 3);
5146 HandleScope handle_scope;
5147 CONVERT_ARG_CHECKED(String, subject, 0);
5148 CONVERT_ARG_CHECKED(String, pattern, 1);
5149 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
5150
5151 int subject_length = subject->length();
5152 int pattern_length = pattern->length();
5153 RUNTIME_ASSERT(pattern_length > 0);
5154
5155 // The limit can be very large (0xffffffffu), but since the pattern
5156 // isn't empty, we can never create more parts than ~half the length
5157 // of the subject.
5158
5159 if (!subject->IsFlat()) FlattenString(subject);
5160
5161 static const int kMaxInitialListCapacity = 16;
5162
5163 ZoneScope scope(DELETE_ON_EXIT);
5164
5165 // Find (up to limit) indices of separator and end-of-string in subject
5166 int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
5167 ZoneList<int> indices(initial_capacity);
5168 if (pattern_length == 1) {
5169 // Special case, go directly to fast single-character split.
5170 AssertNoAllocation nogc;
5171 uc16 pattern_char = pattern->Get(0);
5172 if (subject->IsTwoByteRepresentation()) {
5173 FindCharIndices(subject->ToUC16Vector(), pattern_char,
5174 &indices,
5175 limit);
5176 } else if (pattern_char <= String::kMaxAsciiCharCode) {
5177 FindCharIndices(subject->ToAsciiVector(),
5178 static_cast<char>(pattern_char),
5179 &indices,
5180 limit);
5181 }
5182 } else {
5183 if (!pattern->IsFlat()) FlattenString(pattern);
5184 AssertNoAllocation nogc;
5185 if (subject->IsAsciiRepresentation()) {
5186 Vector<const char> subject_vector = subject->ToAsciiVector();
5187 if (pattern->IsAsciiRepresentation()) {
5188 FindStringIndices(subject_vector,
5189 pattern->ToAsciiVector(),
5190 &indices,
5191 limit);
5192 } else {
5193 FindStringIndices(subject_vector,
5194 pattern->ToUC16Vector(),
5195 &indices,
5196 limit);
5197 }
5198 } else {
5199 Vector<const uc16> subject_vector = subject->ToUC16Vector();
5200 if (pattern->IsAsciiRepresentation()) {
5201 FindStringIndices(subject_vector,
5202 pattern->ToAsciiVector(),
5203 &indices,
5204 limit);
5205 } else {
5206 FindStringIndices(subject_vector,
5207 pattern->ToUC16Vector(),
5208 &indices,
5209 limit);
5210 }
5211 }
5212 }
5213 if (static_cast<uint32_t>(indices.length()) < limit) {
5214 indices.Add(subject_length);
5215 }
5216 // The list indices now contains the end of each part to create.
5217
5218
5219 // Create JSArray of substrings separated by separator.
5220 int part_count = indices.length();
5221
5222 Handle<JSArray> result = Factory::NewJSArray(part_count);
5223 result->set_length(Smi::FromInt(part_count));
5224
5225 ASSERT(result->HasFastElements());
5226
5227 if (part_count == 1 && indices.at(0) == subject_length) {
5228 FixedArray::cast(result->elements())->set(0, *subject);
5229 return *result;
5230 }
5231
5232 Handle<FixedArray> elements(FixedArray::cast(result->elements()));
5233 int part_start = 0;
5234 for (int i = 0; i < part_count; i++) {
5235 HandleScope local_loop_handle;
5236 int part_end = indices.at(i);
5237 Handle<String> substring =
5238 Factory::NewSubString(subject, part_start, part_end);
5239 elements->set(i, *substring);
5240 part_start = part_end + pattern_length;
5241 }
5242
5243 return *result;
5244}
5245
5246
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005247// Copies ascii characters to the given fixed array looking up
5248// one-char strings in the cache. Gives up on the first char that is
5249// not in the cache and fills the remainder with smi zeros. Returns
5250// the length of the successfully copied prefix.
5251static int CopyCachedAsciiCharsToArray(const char* chars,
5252 FixedArray* elements,
5253 int length) {
5254 AssertNoAllocation nogc;
5255 FixedArray* ascii_cache = Heap::single_character_string_cache();
5256 Object* undefined = Heap::undefined_value();
5257 int i;
5258 for (i = 0; i < length; ++i) {
5259 Object* value = ascii_cache->get(chars[i]);
5260 if (value == undefined) break;
5261 ASSERT(!Heap::InNewSpace(value));
5262 elements->set(i, value, SKIP_WRITE_BARRIER);
5263 }
5264 if (i < length) {
5265 ASSERT(Smi::FromInt(0) == 0);
5266 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
5267 }
5268#ifdef DEBUG
5269 for (int j = 0; j < length; ++j) {
5270 Object* element = elements->get(j);
5271 ASSERT(element == Smi::FromInt(0) ||
5272 (element->IsString() && String::cast(element)->LooksValid()));
5273 }
5274#endif
5275 return i;
5276}
5277
5278
5279// Converts a String to JSArray.
5280// For example, "foo" => ["f", "o", "o"].
5281static Object* Runtime_StringToArray(Arguments args) {
5282 HandleScope scope;
5283 ASSERT(args.length() == 1);
5284 CONVERT_ARG_CHECKED(String, s, 0);
5285
5286 s->TryFlatten();
5287 const int length = s->length();
5288
5289 Handle<FixedArray> elements;
5290 if (s->IsFlat() && s->IsAsciiRepresentation()) {
5291 Object* obj = Heap::AllocateUninitializedFixedArray(length);
5292 if (obj->IsFailure()) return obj;
5293 elements = Handle<FixedArray>(FixedArray::cast(obj));
5294
5295 Vector<const char> chars = s->ToAsciiVector();
5296 // Note, this will initialize all elements (not only the prefix)
5297 // to prevent GC from seeing partially initialized array.
5298 int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
5299 *elements,
5300 length);
5301
5302 for (int i = num_copied_from_cache; i < length; ++i) {
5303 elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
5304 }
5305 } else {
5306 elements = Factory::NewFixedArray(length);
5307 for (int i = 0; i < length; ++i) {
5308 elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
5309 }
5310 }
5311
5312#ifdef DEBUG
5313 for (int i = 0; i < length; ++i) {
5314 ASSERT(String::cast(elements->get(i))->length() == 1);
5315 }
5316#endif
5317
5318 return *Factory::NewJSArrayWithElements(elements);
5319}
5320
5321
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00005322bool Runtime::IsUpperCaseChar(uint16_t ch) {
5323 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
5324 int char_length = to_upper_mapping.get(ch, 0, chars);
5325 return char_length == 0;
5326}
5327
5328
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005329static Object* Runtime_NumberToString(Arguments args) {
5330 NoHandleAllocation ha;
5331 ASSERT(args.length() == 1);
5332
5333 Object* number = args[0];
5334 RUNTIME_ASSERT(number->IsNumber());
5335
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005336 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005337}
5338
5339
ager@chromium.org357bf652010-04-12 11:30:10 +00005340static Object* Runtime_NumberToStringSkipCache(Arguments args) {
5341 NoHandleAllocation ha;
5342 ASSERT(args.length() == 1);
5343
5344 Object* number = args[0];
5345 RUNTIME_ASSERT(number->IsNumber());
5346
5347 return Heap::NumberToString(number, false);
5348}
5349
5350
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005351static Object* Runtime_NumberToInteger(Arguments args) {
5352 NoHandleAllocation ha;
5353 ASSERT(args.length() == 1);
5354
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005355 CONVERT_DOUBLE_CHECKED(number, args[0]);
5356
5357 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5358 if (number > 0 && number <= Smi::kMaxValue) {
5359 return Smi::FromInt(static_cast<int>(number));
5360 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005361 return Heap::NumberFromDouble(DoubleToInteger(number));
5362}
5363
5364
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005365
5366
5367
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005368static Object* Runtime_NumberToIntegerMapMinusZero(Arguments args) {
5369 NoHandleAllocation ha;
5370 ASSERT(args.length() == 1);
5371
5372 CONVERT_DOUBLE_CHECKED(number, args[0]);
5373
5374 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5375 if (number > 0 && number <= Smi::kMaxValue) {
5376 return Smi::FromInt(static_cast<int>(number));
5377 }
5378
5379 double double_value = DoubleToInteger(number);
5380 // Map both -0 and +0 to +0.
5381 if (double_value == 0) double_value = 0;
5382
5383 return Heap::NumberFromDouble(double_value);
5384}
5385
5386
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005387static Object* Runtime_NumberToJSUint32(Arguments args) {
5388 NoHandleAllocation ha;
5389 ASSERT(args.length() == 1);
5390
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005391 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005392 return Heap::NumberFromUint32(number);
5393}
5394
5395
5396static Object* Runtime_NumberToJSInt32(Arguments args) {
5397 NoHandleAllocation ha;
5398 ASSERT(args.length() == 1);
5399
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005400 CONVERT_DOUBLE_CHECKED(number, args[0]);
5401
5402 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5403 if (number > 0 && number <= Smi::kMaxValue) {
5404 return Smi::FromInt(static_cast<int>(number));
5405 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005406 return Heap::NumberFromInt32(DoubleToInt32(number));
5407}
5408
5409
ager@chromium.org870a0b62008-11-04 11:43:05 +00005410// Converts a Number to a Smi, if possible. Returns NaN if the number is not
5411// a small integer.
5412static Object* Runtime_NumberToSmi(Arguments args) {
5413 NoHandleAllocation ha;
5414 ASSERT(args.length() == 1);
5415
5416 Object* obj = args[0];
5417 if (obj->IsSmi()) {
5418 return obj;
5419 }
5420 if (obj->IsHeapNumber()) {
5421 double value = HeapNumber::cast(obj)->value();
5422 int int_value = FastD2I(value);
5423 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
5424 return Smi::FromInt(int_value);
5425 }
5426 }
5427 return Heap::nan_value();
5428}
5429
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005430
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005431static Object* Runtime_NumberAdd(Arguments args) {
5432 NoHandleAllocation ha;
5433 ASSERT(args.length() == 2);
5434
5435 CONVERT_DOUBLE_CHECKED(x, args[0]);
5436 CONVERT_DOUBLE_CHECKED(y, args[1]);
5437 return Heap::AllocateHeapNumber(x + y);
5438}
5439
5440
5441static Object* Runtime_NumberSub(Arguments args) {
5442 NoHandleAllocation ha;
5443 ASSERT(args.length() == 2);
5444
5445 CONVERT_DOUBLE_CHECKED(x, args[0]);
5446 CONVERT_DOUBLE_CHECKED(y, args[1]);
5447 return Heap::AllocateHeapNumber(x - y);
5448}
5449
5450
5451static Object* Runtime_NumberMul(Arguments args) {
5452 NoHandleAllocation ha;
5453 ASSERT(args.length() == 2);
5454
5455 CONVERT_DOUBLE_CHECKED(x, args[0]);
5456 CONVERT_DOUBLE_CHECKED(y, args[1]);
5457 return Heap::AllocateHeapNumber(x * y);
5458}
5459
5460
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005461static Object* Runtime_NumberUnaryMinus(Arguments args) {
5462 NoHandleAllocation ha;
5463 ASSERT(args.length() == 1);
5464
5465 CONVERT_DOUBLE_CHECKED(x, args[0]);
5466 return Heap::AllocateHeapNumber(-x);
5467}
5468
5469
5470static Object* Runtime_NumberDiv(Arguments args) {
5471 NoHandleAllocation ha;
5472 ASSERT(args.length() == 2);
5473
5474 CONVERT_DOUBLE_CHECKED(x, args[0]);
5475 CONVERT_DOUBLE_CHECKED(y, args[1]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005476 return Heap::NumberFromDouble(x / y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005477}
5478
5479
5480static Object* Runtime_NumberMod(Arguments args) {
5481 NoHandleAllocation ha;
5482 ASSERT(args.length() == 2);
5483
5484 CONVERT_DOUBLE_CHECKED(x, args[0]);
5485 CONVERT_DOUBLE_CHECKED(y, args[1]);
5486
ager@chromium.org3811b432009-10-28 14:53:37 +00005487 x = modulo(x, y);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005488 // NumberFromDouble may return a Smi instead of a Number object
5489 return Heap::NumberFromDouble(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005490}
5491
5492
5493static Object* Runtime_StringAdd(Arguments args) {
5494 NoHandleAllocation ha;
5495 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005496 CONVERT_CHECKED(String, str1, args[0]);
5497 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005498 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00005499 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005500}
5501
5502
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005503template <typename sinkchar>
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005504static inline void StringBuilderConcatHelper(String* special,
5505 sinkchar* sink,
5506 FixedArray* fixed_array,
5507 int array_length) {
5508 int position = 0;
5509 for (int i = 0; i < array_length; i++) {
5510 Object* element = fixed_array->get(i);
5511 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005512 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005513 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005514 int pos;
5515 int len;
5516 if (encoded_slice > 0) {
5517 // Position and length encoded in one smi.
5518 pos = StringBuilderSubstringPosition::decode(encoded_slice);
5519 len = StringBuilderSubstringLength::decode(encoded_slice);
5520 } else {
5521 // Position and length encoded in two smis.
5522 Object* obj = fixed_array->get(++i);
5523 ASSERT(obj->IsSmi());
5524 pos = Smi::cast(obj)->value();
5525 len = -encoded_slice;
5526 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00005527 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00005528 sink + position,
5529 pos,
5530 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005531 position += len;
5532 } else {
5533 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005534 int element_length = string->length();
5535 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005536 position += element_length;
5537 }
5538 }
5539}
5540
5541
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005542static Object* Runtime_StringBuilderConcat(Arguments args) {
5543 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005544 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005545 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005546 if (!args[1]->IsSmi()) {
5547 Top::context()->mark_out_of_memory();
5548 return Failure::OutOfMemoryException();
5549 }
5550 int array_length = Smi::cast(args[1])->value();
5551 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005552
5553 // This assumption is used by the slice encoding in one or two smis.
5554 ASSERT(Smi::kMaxValue >= String::kMaxLength);
5555
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005556 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005557 if (!array->HasFastElements()) {
5558 return Top::Throw(Heap::illegal_argument_symbol());
5559 }
5560 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005561 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005562 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005563 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005564
5565 if (array_length == 0) {
5566 return Heap::empty_string();
5567 } else if (array_length == 1) {
5568 Object* first = fixed_array->get(0);
5569 if (first->IsString()) return first;
5570 }
5571
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005572 bool ascii = special->HasOnlyAsciiChars();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005573 int position = 0;
5574 for (int i = 0; i < array_length; i++) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005575 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005576 Object* elt = fixed_array->get(i);
5577 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005578 // Smi encoding of position and length.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005579 int smi_value = Smi::cast(elt)->value();
5580 int pos;
5581 int len;
5582 if (smi_value > 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005583 // Position and length encoded in one smi.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005584 pos = StringBuilderSubstringPosition::decode(smi_value);
5585 len = StringBuilderSubstringLength::decode(smi_value);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005586 } else {
5587 // Position and length encoded in two smis.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005588 len = -smi_value;
5589 // Get the position and check that it is a positive smi.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005590 i++;
5591 if (i >= array_length) {
5592 return Top::Throw(Heap::illegal_argument_symbol());
5593 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005594 Object* next_smi = fixed_array->get(i);
5595 if (!next_smi->IsSmi()) {
5596 return Top::Throw(Heap::illegal_argument_symbol());
5597 }
5598 pos = Smi::cast(next_smi)->value();
5599 if (pos < 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005600 return Top::Throw(Heap::illegal_argument_symbol());
5601 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005602 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005603 ASSERT(pos >= 0);
5604 ASSERT(len >= 0);
5605 if (pos > special_length || len > special_length - pos) {
5606 return Top::Throw(Heap::illegal_argument_symbol());
5607 }
5608 increment = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005609 } else if (elt->IsString()) {
5610 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005611 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005612 increment = element_length;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005613 if (ascii && !element->HasOnlyAsciiChars()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005614 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005615 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005616 } else {
5617 return Top::Throw(Heap::illegal_argument_symbol());
5618 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005619 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005620 Top::context()->mark_out_of_memory();
5621 return Failure::OutOfMemoryException();
5622 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005623 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005624 }
5625
5626 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005627 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005628
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005629 if (ascii) {
5630 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005631 if (object->IsFailure()) return object;
5632 SeqAsciiString* answer = SeqAsciiString::cast(object);
5633 StringBuilderConcatHelper(special,
5634 answer->GetChars(),
5635 fixed_array,
5636 array_length);
5637 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005638 } else {
5639 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005640 if (object->IsFailure()) return object;
5641 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
5642 StringBuilderConcatHelper(special,
5643 answer->GetChars(),
5644 fixed_array,
5645 array_length);
5646 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005647 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005648}
5649
5650
5651static Object* Runtime_NumberOr(Arguments args) {
5652 NoHandleAllocation ha;
5653 ASSERT(args.length() == 2);
5654
5655 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5656 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5657 return Heap::NumberFromInt32(x | y);
5658}
5659
5660
5661static Object* Runtime_NumberAnd(Arguments args) {
5662 NoHandleAllocation ha;
5663 ASSERT(args.length() == 2);
5664
5665 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5666 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5667 return Heap::NumberFromInt32(x & y);
5668}
5669
5670
5671static Object* Runtime_NumberXor(Arguments args) {
5672 NoHandleAllocation ha;
5673 ASSERT(args.length() == 2);
5674
5675 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5676 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5677 return Heap::NumberFromInt32(x ^ y);
5678}
5679
5680
5681static Object* Runtime_NumberNot(Arguments args) {
5682 NoHandleAllocation ha;
5683 ASSERT(args.length() == 1);
5684
5685 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5686 return Heap::NumberFromInt32(~x);
5687}
5688
5689
5690static Object* Runtime_NumberShl(Arguments args) {
5691 NoHandleAllocation ha;
5692 ASSERT(args.length() == 2);
5693
5694 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5695 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5696 return Heap::NumberFromInt32(x << (y & 0x1f));
5697}
5698
5699
5700static Object* Runtime_NumberShr(Arguments args) {
5701 NoHandleAllocation ha;
5702 ASSERT(args.length() == 2);
5703
5704 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
5705 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5706 return Heap::NumberFromUint32(x >> (y & 0x1f));
5707}
5708
5709
5710static Object* Runtime_NumberSar(Arguments args) {
5711 NoHandleAllocation ha;
5712 ASSERT(args.length() == 2);
5713
5714 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5715 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5716 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
5717}
5718
5719
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005720static Object* Runtime_NumberEquals(Arguments args) {
5721 NoHandleAllocation ha;
5722 ASSERT(args.length() == 2);
5723
5724 CONVERT_DOUBLE_CHECKED(x, args[0]);
5725 CONVERT_DOUBLE_CHECKED(y, args[1]);
5726 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
5727 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
5728 if (x == y) return Smi::FromInt(EQUAL);
5729 Object* result;
5730 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
5731 result = Smi::FromInt(EQUAL);
5732 } else {
5733 result = Smi::FromInt(NOT_EQUAL);
5734 }
5735 return result;
5736}
5737
5738
5739static Object* Runtime_StringEquals(Arguments args) {
5740 NoHandleAllocation ha;
5741 ASSERT(args.length() == 2);
5742
5743 CONVERT_CHECKED(String, x, args[0]);
5744 CONVERT_CHECKED(String, y, args[1]);
5745
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005746 bool not_equal = !x->Equals(y);
5747 // This is slightly convoluted because the value that signifies
5748 // equality is 0 and inequality is 1 so we have to negate the result
5749 // from String::Equals.
5750 ASSERT(not_equal == 0 || not_equal == 1);
5751 STATIC_CHECK(EQUAL == 0);
5752 STATIC_CHECK(NOT_EQUAL == 1);
5753 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005754}
5755
5756
5757static Object* Runtime_NumberCompare(Arguments args) {
5758 NoHandleAllocation ha;
5759 ASSERT(args.length() == 3);
5760
5761 CONVERT_DOUBLE_CHECKED(x, args[0]);
5762 CONVERT_DOUBLE_CHECKED(y, args[1]);
5763 if (isnan(x) || isnan(y)) return args[2];
5764 if (x == y) return Smi::FromInt(EQUAL);
5765 if (isless(x, y)) return Smi::FromInt(LESS);
5766 return Smi::FromInt(GREATER);
5767}
5768
5769
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005770// Compare two Smis as if they were converted to strings and then
5771// compared lexicographically.
5772static Object* Runtime_SmiLexicographicCompare(Arguments args) {
5773 NoHandleAllocation ha;
5774 ASSERT(args.length() == 2);
5775
5776 // Arrays for the individual characters of the two Smis. Smis are
5777 // 31 bit integers and 10 decimal digits are therefore enough.
5778 static int x_elms[10];
5779 static int y_elms[10];
5780
5781 // Extract the integer values from the Smis.
5782 CONVERT_CHECKED(Smi, x, args[0]);
5783 CONVERT_CHECKED(Smi, y, args[1]);
5784 int x_value = x->value();
5785 int y_value = y->value();
5786
5787 // If the integers are equal so are the string representations.
5788 if (x_value == y_value) return Smi::FromInt(EQUAL);
5789
5790 // If one of the integers are zero the normal integer order is the
5791 // same as the lexicographic order of the string representations.
5792 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
5793
ager@chromium.org32912102009-01-16 10:38:43 +00005794 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005795 // smallest because the char code of '-' is less than the char code
5796 // of any digit. Otherwise, we make both values positive.
5797 if (x_value < 0 || y_value < 0) {
5798 if (y_value >= 0) return Smi::FromInt(LESS);
5799 if (x_value >= 0) return Smi::FromInt(GREATER);
5800 x_value = -x_value;
5801 y_value = -y_value;
5802 }
5803
5804 // Convert the integers to arrays of their decimal digits.
5805 int x_index = 0;
5806 int y_index = 0;
5807 while (x_value > 0) {
5808 x_elms[x_index++] = x_value % 10;
5809 x_value /= 10;
5810 }
5811 while (y_value > 0) {
5812 y_elms[y_index++] = y_value % 10;
5813 y_value /= 10;
5814 }
5815
5816 // Loop through the arrays of decimal digits finding the first place
5817 // where they differ.
5818 while (--x_index >= 0 && --y_index >= 0) {
5819 int diff = x_elms[x_index] - y_elms[y_index];
5820 if (diff != 0) return Smi::FromInt(diff);
5821 }
5822
5823 // If one array is a suffix of the other array, the longest array is
5824 // the representation of the largest of the Smis in the
5825 // lexicographic ordering.
5826 return Smi::FromInt(x_index - y_index);
5827}
5828
5829
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005830static Object* StringInputBufferCompare(String* x, String* y) {
5831 static StringInputBuffer bufx;
5832 static StringInputBuffer bufy;
5833 bufx.Reset(x);
5834 bufy.Reset(y);
5835 while (bufx.has_more() && bufy.has_more()) {
5836 int d = bufx.GetNext() - bufy.GetNext();
5837 if (d < 0) return Smi::FromInt(LESS);
5838 else if (d > 0) return Smi::FromInt(GREATER);
5839 }
5840
5841 // x is (non-trivial) prefix of y:
5842 if (bufy.has_more()) return Smi::FromInt(LESS);
5843 // y is prefix of x:
5844 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
5845}
5846
5847
5848static Object* FlatStringCompare(String* x, String* y) {
5849 ASSERT(x->IsFlat());
5850 ASSERT(y->IsFlat());
5851 Object* equal_prefix_result = Smi::FromInt(EQUAL);
5852 int prefix_length = x->length();
5853 if (y->length() < prefix_length) {
5854 prefix_length = y->length();
5855 equal_prefix_result = Smi::FromInt(GREATER);
5856 } else if (y->length() > prefix_length) {
5857 equal_prefix_result = Smi::FromInt(LESS);
5858 }
5859 int r;
5860 if (x->IsAsciiRepresentation()) {
5861 Vector<const char> x_chars = x->ToAsciiVector();
5862 if (y->IsAsciiRepresentation()) {
5863 Vector<const char> y_chars = y->ToAsciiVector();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005864 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005865 } else {
5866 Vector<const uc16> y_chars = y->ToUC16Vector();
5867 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5868 }
5869 } else {
5870 Vector<const uc16> x_chars = x->ToUC16Vector();
5871 if (y->IsAsciiRepresentation()) {
5872 Vector<const char> y_chars = y->ToAsciiVector();
5873 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5874 } else {
5875 Vector<const uc16> y_chars = y->ToUC16Vector();
5876 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5877 }
5878 }
5879 Object* result;
5880 if (r == 0) {
5881 result = equal_prefix_result;
5882 } else {
5883 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
5884 }
5885 ASSERT(result == StringInputBufferCompare(x, y));
5886 return result;
5887}
5888
5889
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005890static Object* Runtime_StringCompare(Arguments args) {
5891 NoHandleAllocation ha;
5892 ASSERT(args.length() == 2);
5893
5894 CONVERT_CHECKED(String, x, args[0]);
5895 CONVERT_CHECKED(String, y, args[1]);
5896
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005897 Counters::string_compare_runtime.Increment();
5898
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005899 // A few fast case tests before we flatten.
5900 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005901 if (y->length() == 0) {
5902 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005903 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005904 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005905 return Smi::FromInt(LESS);
5906 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005907
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005908 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005909 if (d < 0) return Smi::FromInt(LESS);
5910 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005911
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005912 Object* obj = Heap::PrepareForCompare(x);
5913 if (obj->IsFailure()) return obj;
5914 obj = Heap::PrepareForCompare(y);
5915 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005916
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005917 return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
5918 : StringInputBufferCompare(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005919}
5920
5921
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005922static Object* Runtime_Math_acos(Arguments args) {
5923 NoHandleAllocation ha;
5924 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005925 Counters::math_acos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005926
5927 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005928 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005929}
5930
5931
5932static Object* Runtime_Math_asin(Arguments args) {
5933 NoHandleAllocation ha;
5934 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005935 Counters::math_asin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005936
5937 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005938 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005939}
5940
5941
5942static Object* Runtime_Math_atan(Arguments args) {
5943 NoHandleAllocation ha;
5944 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005945 Counters::math_atan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005946
5947 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005948 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005949}
5950
5951
5952static Object* Runtime_Math_atan2(Arguments args) {
5953 NoHandleAllocation ha;
5954 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005955 Counters::math_atan2.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005956
5957 CONVERT_DOUBLE_CHECKED(x, args[0]);
5958 CONVERT_DOUBLE_CHECKED(y, args[1]);
5959 double result;
5960 if (isinf(x) && isinf(y)) {
5961 // Make sure that the result in case of two infinite arguments
5962 // is a multiple of Pi / 4. The sign of the result is determined
5963 // by the first argument (x) and the sign of the second argument
5964 // determines the multiplier: one or three.
5965 static double kPiDividedBy4 = 0.78539816339744830962;
5966 int multiplier = (x < 0) ? -1 : 1;
5967 if (y < 0) multiplier *= 3;
5968 result = multiplier * kPiDividedBy4;
5969 } else {
5970 result = atan2(x, y);
5971 }
5972 return Heap::AllocateHeapNumber(result);
5973}
5974
5975
5976static Object* Runtime_Math_ceil(Arguments args) {
5977 NoHandleAllocation ha;
5978 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005979 Counters::math_ceil.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005980
5981 CONVERT_DOUBLE_CHECKED(x, args[0]);
5982 return Heap::NumberFromDouble(ceiling(x));
5983}
5984
5985
5986static Object* Runtime_Math_cos(Arguments args) {
5987 NoHandleAllocation ha;
5988 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005989 Counters::math_cos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005990
5991 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005992 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005993}
5994
5995
5996static Object* Runtime_Math_exp(Arguments args) {
5997 NoHandleAllocation ha;
5998 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005999 Counters::math_exp.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006000
6001 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006002 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006003}
6004
6005
6006static Object* Runtime_Math_floor(Arguments args) {
6007 NoHandleAllocation ha;
6008 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006009 Counters::math_floor.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006010
6011 CONVERT_DOUBLE_CHECKED(x, args[0]);
6012 return Heap::NumberFromDouble(floor(x));
6013}
6014
6015
6016static Object* Runtime_Math_log(Arguments args) {
6017 NoHandleAllocation ha;
6018 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006019 Counters::math_log.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006020
6021 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006022 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006023}
6024
6025
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006026// Helper function to compute x^y, where y is known to be an
6027// integer. Uses binary decomposition to limit the number of
6028// multiplications; see the discussion in "Hacker's Delight" by Henry
6029// S. Warren, Jr., figure 11-6, page 213.
6030static double powi(double x, int y) {
6031 ASSERT(y != kMinInt);
6032 unsigned n = (y < 0) ? -y : y;
6033 double m = x;
6034 double p = 1;
6035 while (true) {
6036 if ((n & 1) != 0) p *= m;
6037 n >>= 1;
6038 if (n == 0) {
6039 if (y < 0) {
6040 // Unfortunately, we have to be careful when p has reached
6041 // infinity in the computation, because sometimes the higher
6042 // internal precision in the pow() implementation would have
6043 // given us a finite p. This happens very rarely.
6044 double result = 1.0 / p;
6045 return (result == 0 && isinf(p))
6046 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
6047 : result;
6048 } else {
6049 return p;
6050 }
6051 }
6052 m *= m;
6053 }
6054}
6055
6056
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006057static Object* Runtime_Math_pow(Arguments args) {
6058 NoHandleAllocation ha;
6059 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006060 Counters::math_pow.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006061
6062 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006063
6064 // If the second argument is a smi, it is much faster to call the
6065 // custom powi() function than the generic pow().
6066 if (args[1]->IsSmi()) {
6067 int y = Smi::cast(args[1])->value();
6068 return Heap::AllocateHeapNumber(powi(x, y));
6069 }
6070
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006071 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00006072
6073 if (!isinf(x)) {
6074 if (y == 0.5) {
6075 // It's not uncommon to use Math.pow(x, 0.5) to compute the
6076 // square root of a number. To speed up such computations, we
6077 // explictly check for this case and use the sqrt() function
6078 // which is faster than pow().
6079 return Heap::AllocateHeapNumber(sqrt(x));
6080 } else if (y == -0.5) {
6081 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
6082 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
6083 }
6084 }
6085
6086 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006087 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006088 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6089 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006090 } else {
6091 return Heap::AllocateHeapNumber(pow(x, y));
6092 }
6093}
6094
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006095// Fast version of Math.pow if we know that y is not an integer and
6096// y is not -0.5 or 0.5. Used as slowcase from codegen.
6097static Object* Runtime_Math_pow_cfunction(Arguments args) {
6098 NoHandleAllocation ha;
6099 ASSERT(args.length() == 2);
6100 CONVERT_DOUBLE_CHECKED(x, args[0]);
6101 CONVERT_DOUBLE_CHECKED(y, args[1]);
6102 if (y == 0) {
6103 return Smi::FromInt(1);
6104 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6105 return Heap::nan_value();
6106 } else {
6107 return Heap::AllocateHeapNumber(pow(x, y));
6108 }
6109}
6110
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006111
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006112static Object* Runtime_RoundNumber(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006113 NoHandleAllocation ha;
6114 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006115 Counters::math_round.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006116
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006117 if (!args[0]->IsHeapNumber()) {
6118 // Must be smi. Return the argument unchanged for all the other types
6119 // to make fuzz-natives test happy.
6120 return args[0];
6121 }
6122
6123 HeapNumber* number = reinterpret_cast<HeapNumber*>(args[0]);
6124
6125 double value = number->value();
6126 int exponent = number->get_exponent();
6127 int sign = number->get_sign();
6128
6129 // We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
6130 // should be rounded to 2^30, which is not smi.
6131 if (!sign && exponent <= kSmiValueSize - 3) {
6132 return Smi::FromInt(static_cast<int>(value + 0.5));
6133 }
6134
6135 // If the magnitude is big enough, there's no place for fraction part. If we
6136 // try to add 0.5 to this number, 1.0 will be added instead.
6137 if (exponent >= 52) {
6138 return number;
6139 }
6140
6141 if (sign && value >= -0.5) return Heap::minus_zero_value();
6142
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006143 // Do not call NumberFromDouble() to avoid extra checks.
6144 return Heap::AllocateHeapNumber(floor(value + 0.5));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006145}
6146
6147
6148static Object* Runtime_Math_sin(Arguments args) {
6149 NoHandleAllocation ha;
6150 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006151 Counters::math_sin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006152
6153 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006154 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006155}
6156
6157
6158static Object* Runtime_Math_sqrt(Arguments args) {
6159 NoHandleAllocation ha;
6160 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006161 Counters::math_sqrt.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006162
6163 CONVERT_DOUBLE_CHECKED(x, args[0]);
6164 return Heap::AllocateHeapNumber(sqrt(x));
6165}
6166
6167
6168static Object* Runtime_Math_tan(Arguments args) {
6169 NoHandleAllocation ha;
6170 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006171 Counters::math_tan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006172
6173 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006174 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006175}
6176
6177
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006178static int MakeDay(int year, int month, int day) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006179 static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
6180 181, 212, 243, 273, 304, 334};
6181 static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
6182 182, 213, 244, 274, 305, 335};
6183
6184 year += month / 12;
6185 month %= 12;
6186 if (month < 0) {
6187 year--;
6188 month += 12;
6189 }
6190
6191 ASSERT(month >= 0);
6192 ASSERT(month < 12);
6193
6194 // year_delta is an arbitrary number such that:
6195 // a) year_delta = -1 (mod 400)
6196 // b) year + year_delta > 0 for years in the range defined by
6197 // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
6198 // Jan 1 1970. This is required so that we don't run into integer
6199 // division of negative numbers.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006200 // c) there shouldn't be an overflow for 32-bit integers in the following
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006201 // operations.
6202 static const int year_delta = 399999;
6203 static const int base_day = 365 * (1970 + year_delta) +
6204 (1970 + year_delta) / 4 -
6205 (1970 + year_delta) / 100 +
6206 (1970 + year_delta) / 400;
6207
6208 int year1 = year + year_delta;
6209 int day_from_year = 365 * year1 +
6210 year1 / 4 -
6211 year1 / 100 +
6212 year1 / 400 -
6213 base_day;
6214
6215 if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006216 return day_from_year + day_from_month[month] + day - 1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006217 }
6218
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006219 return day_from_year + day_from_month_leap[month] + day - 1;
6220}
6221
6222
6223static Object* Runtime_DateMakeDay(Arguments args) {
6224 NoHandleAllocation ha;
6225 ASSERT(args.length() == 3);
6226
6227 CONVERT_SMI_CHECKED(year, args[0]);
6228 CONVERT_SMI_CHECKED(month, args[1]);
6229 CONVERT_SMI_CHECKED(date, args[2]);
6230
6231 return Smi::FromInt(MakeDay(year, month, date));
6232}
6233
6234
6235static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
6236static const int kDaysIn4Years = 4 * 365 + 1;
6237static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
6238static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
6239static const int kDays1970to2000 = 30 * 365 + 7;
6240static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
6241 kDays1970to2000;
6242static const int kYearsOffset = 400000;
6243
6244static const char kDayInYear[] = {
6245 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6246 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6247 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6248 22, 23, 24, 25, 26, 27, 28,
6249 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6250 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6251 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6252 22, 23, 24, 25, 26, 27, 28, 29, 30,
6253 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6254 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6255 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6256 22, 23, 24, 25, 26, 27, 28, 29, 30,
6257 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6258 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6259 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6260 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6261 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6262 22, 23, 24, 25, 26, 27, 28, 29, 30,
6263 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6264 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6265 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6266 22, 23, 24, 25, 26, 27, 28, 29, 30,
6267 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6268 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6269
6270 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6271 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6272 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6273 22, 23, 24, 25, 26, 27, 28,
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, 30,
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, 31,
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,
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
6295 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6296 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6297 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6298 22, 23, 24, 25, 26, 27, 28, 29,
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, 29, 30,
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, 31,
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,
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
6320 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6321 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6322 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6323 22, 23, 24, 25, 26, 27, 28,
6324 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6325 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6326 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6327 22, 23, 24, 25, 26, 27, 28, 29, 30,
6328 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6329 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6330 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6331 22, 23, 24, 25, 26, 27, 28, 29, 30,
6332 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6333 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6334 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6335 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6336 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6337 22, 23, 24, 25, 26, 27, 28, 29, 30,
6338 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6339 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6340 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6341 22, 23, 24, 25, 26, 27, 28, 29, 30,
6342 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6343 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
6344
6345static const char kMonthInYear[] = {
6346 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
6347 0, 0, 0, 0, 0, 0,
6348 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
6349 1, 1, 1,
6350 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
6351 2, 2, 2, 2, 2, 2,
6352 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
6353 3, 3, 3, 3, 3,
6354 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
6355 4, 4, 4, 4, 4, 4,
6356 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6357 5, 5, 5, 5, 5,
6358 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6359 6, 6, 6, 6, 6, 6,
6360 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
6361 7, 7, 7, 7, 7, 7,
6362 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
6363 8, 8, 8, 8, 8,
6364 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
6365 9, 9, 9, 9, 9, 9,
6366 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6367 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6368 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6369 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6370
6371 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,
6372 0, 0, 0, 0, 0, 0,
6373 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,
6374 1, 1, 1,
6375 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,
6376 2, 2, 2, 2, 2, 2,
6377 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,
6378 3, 3, 3, 3, 3,
6379 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,
6380 4, 4, 4, 4, 4, 4,
6381 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,
6382 5, 5, 5, 5, 5,
6383 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,
6384 6, 6, 6, 6, 6, 6,
6385 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,
6386 7, 7, 7, 7, 7, 7,
6387 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,
6388 8, 8, 8, 8, 8,
6389 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,
6390 9, 9, 9, 9, 9, 9,
6391 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6392 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6393 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6394 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6395
6396 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,
6397 0, 0, 0, 0, 0, 0,
6398 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,
6399 1, 1, 1, 1,
6400 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,
6401 2, 2, 2, 2, 2, 2,
6402 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,
6403 3, 3, 3, 3, 3,
6404 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,
6405 4, 4, 4, 4, 4, 4,
6406 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,
6407 5, 5, 5, 5, 5,
6408 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,
6409 6, 6, 6, 6, 6, 6,
6410 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,
6411 7, 7, 7, 7, 7, 7,
6412 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,
6413 8, 8, 8, 8, 8,
6414 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,
6415 9, 9, 9, 9, 9, 9,
6416 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6417 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6418 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6419 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6420
6421 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,
6422 0, 0, 0, 0, 0, 0,
6423 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,
6424 1, 1, 1,
6425 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,
6426 2, 2, 2, 2, 2, 2,
6427 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,
6428 3, 3, 3, 3, 3,
6429 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,
6430 4, 4, 4, 4, 4, 4,
6431 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,
6432 5, 5, 5, 5, 5,
6433 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,
6434 6, 6, 6, 6, 6, 6,
6435 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,
6436 7, 7, 7, 7, 7, 7,
6437 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,
6438 8, 8, 8, 8, 8,
6439 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,
6440 9, 9, 9, 9, 9, 9,
6441 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6442 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6443 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6444 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
6445
6446
6447// This function works for dates from 1970 to 2099.
6448static inline void DateYMDFromTimeAfter1970(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006449 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006450#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006451 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006452#endif
6453
6454 year = 1970 + (4 * date + 2) / kDaysIn4Years;
6455 date %= kDaysIn4Years;
6456
6457 month = kMonthInYear[date];
6458 day = kDayInYear[date];
6459
6460 ASSERT(MakeDay(year, month, day) == save_date);
6461}
6462
6463
6464static inline void DateYMDFromTimeSlow(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006465 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006466#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006467 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006468#endif
6469
6470 date += kDaysOffset;
6471 year = 400 * (date / kDaysIn400Years) - kYearsOffset;
6472 date %= kDaysIn400Years;
6473
6474 ASSERT(MakeDay(year, 0, 1) + date == save_date);
6475
6476 date--;
6477 int yd1 = date / kDaysIn100Years;
6478 date %= kDaysIn100Years;
6479 year += 100 * yd1;
6480
6481 date++;
6482 int yd2 = date / kDaysIn4Years;
6483 date %= kDaysIn4Years;
6484 year += 4 * yd2;
6485
6486 date--;
6487 int yd3 = date / 365;
6488 date %= 365;
6489 year += yd3;
6490
6491 bool is_leap = (!yd1 || yd2) && !yd3;
6492
6493 ASSERT(date >= -1);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006494 ASSERT(is_leap || (date >= 0));
6495 ASSERT((date < 365) || (is_leap && (date < 366)));
6496 ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0))));
6497 ASSERT(is_leap || ((MakeDay(year, 0, 1) + date) == save_date));
6498 ASSERT(!is_leap || ((MakeDay(year, 0, 1) + date + 1) == save_date));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006499
6500 if (is_leap) {
6501 day = kDayInYear[2*365 + 1 + date];
6502 month = kMonthInYear[2*365 + 1 + date];
6503 } else {
6504 day = kDayInYear[date];
6505 month = kMonthInYear[date];
6506 }
6507
6508 ASSERT(MakeDay(year, month, day) == save_date);
6509}
6510
6511
6512static inline void DateYMDFromTime(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006513 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006514 if (date >= 0 && date < 32 * kDaysIn4Years) {
6515 DateYMDFromTimeAfter1970(date, year, month, day);
6516 } else {
6517 DateYMDFromTimeSlow(date, year, month, day);
6518 }
6519}
6520
6521
6522static Object* Runtime_DateYMDFromTime(Arguments args) {
6523 NoHandleAllocation ha;
6524 ASSERT(args.length() == 2);
6525
6526 CONVERT_DOUBLE_CHECKED(t, args[0]);
6527 CONVERT_CHECKED(JSArray, res_array, args[1]);
6528
6529 int year, month, day;
6530 DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
6531
6532 res_array->SetElement(0, Smi::FromInt(year));
6533 res_array->SetElement(1, Smi::FromInt(month));
6534 res_array->SetElement(2, Smi::FromInt(day));
6535
6536 return Heap::undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006537}
6538
6539
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006540static Object* Runtime_NewArgumentsFast(Arguments args) {
6541 NoHandleAllocation ha;
6542 ASSERT(args.length() == 3);
6543
6544 JSFunction* callee = JSFunction::cast(args[0]);
6545 Object** parameters = reinterpret_cast<Object**>(args[1]);
6546 const int length = Smi::cast(args[2])->value();
6547
6548 Object* result = Heap::AllocateArgumentsObject(callee, length);
6549 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006550 // Allocate the elements if needed.
6551 if (length > 0) {
6552 // Allocate the fixed array.
6553 Object* obj = Heap::AllocateRawFixedArray(length);
6554 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006555
6556 AssertNoAllocation no_gc;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006557 FixedArray* array = reinterpret_cast<FixedArray*>(obj);
6558 array->set_map(Heap::fixed_array_map());
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006559 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006560
6561 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006562 for (int i = 0; i < length; i++) {
6563 array->set(i, *--parameters, mode);
6564 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006565 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006566 }
6567 return result;
6568}
6569
6570
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006571static Object* Runtime_NewClosure(Arguments args) {
6572 HandleScope scope;
6573 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00006574 CONVERT_ARG_CHECKED(Context, context, 0);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006575 CONVERT_ARG_CHECKED(SharedFunctionInfo, shared, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006576
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00006577 PretenureFlag pretenure = (context->global_context() == *context)
6578 ? TENURED // Allocate global closures in old space.
6579 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006580 Handle<JSFunction> result =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006581 Factory::NewFunctionFromSharedFunctionInfo(shared, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006582 return *result;
6583}
6584
6585
ager@chromium.org5c838252010-02-19 08:53:10 +00006586static Code* ComputeConstructStub(Handle<JSFunction> function) {
6587 Handle<Object> prototype = Factory::null_value();
6588 if (function->has_instance_prototype()) {
6589 prototype = Handle<Object>(function->instance_prototype());
6590 }
6591 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006592 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00006593 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006594 if (code->IsFailure()) {
6595 return Builtins::builtin(Builtins::JSConstructStubGeneric);
6596 }
6597 return Code::cast(code);
6598 }
6599
ager@chromium.org5c838252010-02-19 08:53:10 +00006600 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006601}
6602
6603
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006604static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006605 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006606 ASSERT(args.length() == 1);
6607
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006608 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006609
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006610 // If the constructor isn't a proper function we throw a type error.
6611 if (!constructor->IsJSFunction()) {
6612 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6613 Handle<Object> type_error =
6614 Factory::NewTypeError("not_constructor", arguments);
6615 return Top::Throw(*type_error);
6616 }
6617
6618 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006619
6620 // If function should not have prototype, construction is not allowed. In this
6621 // case generated code bailouts here, since function has no initial_map.
6622 if (!function->should_have_prototype()) {
6623 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6624 Handle<Object> type_error =
6625 Factory::NewTypeError("not_constructor", arguments);
6626 return Top::Throw(*type_error);
6627 }
6628
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006629#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006630 // Handle stepping into constructors if step into is active.
6631 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006632 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006633 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006634#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006635
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006636 if (function->has_initial_map()) {
6637 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006638 // The 'Function' function ignores the receiver object when
6639 // called using 'new' and creates a new JSFunction object that
6640 // is returned. The receiver object is only used for error
6641 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006642 // JSFunction. Factory::NewJSObject() should not be used to
6643 // allocate JSFunctions since it does not properly initialize
6644 // the shared part of the function. Since the receiver is
6645 // ignored anyway, we use the global object as the receiver
6646 // instead of a new JSFunction object. This way, errors are
6647 // reported the same way whether or not 'Function' is called
6648 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006649 return Top::context()->global();
6650 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006651 }
6652
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006653 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006654 Handle<SharedFunctionInfo> shared(function->shared());
6655 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006656
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006657 bool first_allocation = !function->has_initial_map();
6658 Handle<JSObject> result = Factory::NewJSObject(function);
6659 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006660 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00006661 ComputeConstructStub(Handle<JSFunction>(function)));
6662 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006663 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006664
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006665 Counters::constructed_objects.Increment();
6666 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006667
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006668 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006669}
6670
6671
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006672static Object* Runtime_LazyCompile(Arguments args) {
6673 HandleScope scope;
6674 ASSERT(args.length() == 1);
6675
6676 Handle<JSFunction> function = args.at<JSFunction>(0);
6677#ifdef DEBUG
6678 if (FLAG_trace_lazy) {
6679 PrintF("[lazy: ");
6680 function->shared()->name()->Print();
6681 PrintF("]\n");
6682 }
6683#endif
6684
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006685 // Compile the target function. Here we compile using CompileLazyInLoop in
6686 // order to get the optimized version. This helps code like delta-blue
6687 // that calls performance-critical routines through constructors. A
6688 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
6689 // direct call. Since the in-loop tracking takes place through CallICs
6690 // this means that things called through constructors are never known to
6691 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006692 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006693 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006694 return Failure::Exception();
6695 }
6696
6697 return function->code();
6698}
6699
6700
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006701static Object* Runtime_GetFunctionDelegate(Arguments args) {
6702 HandleScope scope;
6703 ASSERT(args.length() == 1);
6704 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6705 return *Execution::GetFunctionDelegate(args.at<Object>(0));
6706}
6707
6708
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00006709static Object* Runtime_GetConstructorDelegate(Arguments args) {
6710 HandleScope scope;
6711 ASSERT(args.length() == 1);
6712 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6713 return *Execution::GetConstructorDelegate(args.at<Object>(0));
6714}
6715
6716
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006717static Object* Runtime_NewContext(Arguments args) {
6718 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00006719 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006720
kasper.lund7276f142008-07-30 08:49:36 +00006721 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006722 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
6723 Object* result = Heap::AllocateFunctionContext(length, function);
6724 if (result->IsFailure()) return result;
6725
6726 Top::set_context(Context::cast(result));
6727
kasper.lund7276f142008-07-30 08:49:36 +00006728 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006729}
6730
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006731static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006732 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006733 Object* js_object = object;
6734 if (!js_object->IsJSObject()) {
6735 js_object = js_object->ToObject();
6736 if (js_object->IsFailure()) {
6737 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006738 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006739 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006740 Handle<Object> result =
6741 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
6742 return Top::Throw(*result);
6743 }
6744 }
6745
6746 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006747 Heap::AllocateWithContext(Top::context(),
6748 JSObject::cast(js_object),
6749 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006750 if (result->IsFailure()) return result;
6751
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006752 Context* context = Context::cast(result);
6753 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006754
kasper.lund7276f142008-07-30 08:49:36 +00006755 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006756}
6757
6758
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006759static Object* Runtime_PushContext(Arguments args) {
6760 NoHandleAllocation ha;
6761 ASSERT(args.length() == 1);
6762 return PushContextHelper(args[0], false);
6763}
6764
6765
6766static Object* Runtime_PushCatchContext(Arguments args) {
6767 NoHandleAllocation ha;
6768 ASSERT(args.length() == 1);
6769 return PushContextHelper(args[0], true);
6770}
6771
6772
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006773static Object* Runtime_LookupContext(Arguments args) {
6774 HandleScope scope;
6775 ASSERT(args.length() == 2);
6776
6777 CONVERT_ARG_CHECKED(Context, context, 0);
6778 CONVERT_ARG_CHECKED(String, name, 1);
6779
6780 int index;
6781 PropertyAttributes attributes;
6782 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006783 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006784 context->Lookup(name, flags, &index, &attributes);
6785
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006786 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006787 ASSERT(holder->IsJSObject());
6788 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006789 }
6790
6791 // No intermediate context found. Use global object by default.
6792 return Top::context()->global();
6793}
6794
6795
ager@chromium.orga1645e22009-09-09 19:27:10 +00006796// A mechanism to return a pair of Object pointers in registers (if possible).
6797// How this is achieved is calling convention-dependent.
6798// All currently supported x86 compiles uses calling conventions that are cdecl
6799// variants where a 64-bit value is returned in two 32-bit registers
6800// (edx:eax on ia32, r1:r0 on ARM).
6801// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
6802// In Win64 calling convention, a struct of two pointers is returned in memory,
6803// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006804#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006805struct ObjectPair {
6806 Object* x;
6807 Object* y;
6808};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006809
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006810static inline ObjectPair MakePair(Object* x, Object* y) {
6811 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006812 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
6813 // In Win64 they are assigned to a hidden first argument.
6814 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006815}
6816#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006817typedef uint64_t ObjectPair;
6818static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006819 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006820 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006821}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006822#endif
6823
6824
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006825static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006826 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
6827 USE(attributes);
6828 return x->IsTheHole() ? Heap::undefined_value() : x;
6829}
6830
6831
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006832static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
6833 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006834 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006835 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006836 JSFunction* context_extension_function =
6837 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006838 // If the holder isn't a context extension object, we just return it
6839 // as the receiver. This allows arguments objects to be used as
6840 // receivers, but only if they are put in the context scope chain
6841 // explicitly via a with-statement.
6842 Object* constructor = holder->map()->constructor();
6843 if (constructor != context_extension_function) return holder;
6844 // Fall back to using the global object as the receiver if the
6845 // property turns out to be a local variable allocated in a context
6846 // extension object - introduced via eval.
6847 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006848}
6849
6850
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006851static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006852 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00006853 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006854
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006855 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00006856 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006857 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006858 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006859 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006860
6861 int index;
6862 PropertyAttributes attributes;
6863 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006864 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006865 context->Lookup(name, flags, &index, &attributes);
6866
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006867 // If the index is non-negative, the slot has been found in a local
6868 // variable or a parameter. Read it from the context object or the
6869 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006870 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006871 // If the "property" we were looking for is a local variable or an
6872 // argument in a context, the receiver is the global object; see
6873 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
6874 JSObject* receiver = Top::context()->global()->global_receiver();
6875 Object* value = (holder->IsContext())
6876 ? Context::cast(*holder)->get(index)
6877 : JSObject::cast(*holder)->GetElement(index);
6878 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006879 }
6880
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006881 // If the holder is found, we read the property from it.
6882 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006883 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006884 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006885 JSObject* receiver;
6886 if (object->IsGlobalObject()) {
6887 receiver = GlobalObject::cast(object)->global_receiver();
6888 } else if (context->is_exception_holder(*holder)) {
6889 receiver = Top::context()->global()->global_receiver();
6890 } else {
6891 receiver = ComputeReceiverForNonGlobal(object);
6892 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006893 // No need to unhole the value here. This is taken care of by the
6894 // GetProperty function.
6895 Object* value = object->GetProperty(*name);
6896 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006897 }
6898
6899 if (throw_error) {
6900 // The property doesn't exist - throw exception.
6901 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006902 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006903 return MakePair(Top::Throw(*reference_error), NULL);
6904 } else {
6905 // The property doesn't exist - return undefined
6906 return MakePair(Heap::undefined_value(), Heap::undefined_value());
6907 }
6908}
6909
6910
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006911static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006912 return LoadContextSlotHelper(args, true);
6913}
6914
6915
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006916static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006917 return LoadContextSlotHelper(args, false);
6918}
6919
6920
6921static Object* Runtime_StoreContextSlot(Arguments args) {
6922 HandleScope scope;
6923 ASSERT(args.length() == 3);
6924
6925 Handle<Object> value(args[0]);
6926 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006927 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006928
6929 int index;
6930 PropertyAttributes attributes;
6931 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006932 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006933 context->Lookup(name, flags, &index, &attributes);
6934
6935 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006936 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006937 // Ignore if read_only variable.
6938 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006939 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006940 }
6941 } else {
6942 ASSERT((attributes & READ_ONLY) == 0);
6943 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006944 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006945 USE(result);
6946 ASSERT(!result->IsFailure());
6947 }
6948 return *value;
6949 }
6950
6951 // Slow case: The property is not in a FixedArray context.
6952 // It is either in an JSObject extension context or it was not found.
6953 Handle<JSObject> context_ext;
6954
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006955 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006956 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006957 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006958 } else {
6959 // The property was not found. It needs to be stored in the global context.
6960 ASSERT(attributes == ABSENT);
6961 attributes = NONE;
6962 context_ext = Handle<JSObject>(Top::context()->global());
6963 }
6964
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006965 // Set the property, but ignore if read_only variable on the context
6966 // extension object itself.
6967 if ((attributes & READ_ONLY) == 0 ||
6968 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006969 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
6970 if (set.is_null()) {
6971 // Failure::Exception is converted to a null handle in the
6972 // handle-based methods such as SetProperty. We therefore need
6973 // to convert null handles back to exceptions.
6974 ASSERT(Top::has_pending_exception());
6975 return Failure::Exception();
6976 }
6977 }
6978 return *value;
6979}
6980
6981
6982static Object* Runtime_Throw(Arguments args) {
6983 HandleScope scope;
6984 ASSERT(args.length() == 1);
6985
6986 return Top::Throw(args[0]);
6987}
6988
6989
6990static Object* Runtime_ReThrow(Arguments args) {
6991 HandleScope scope;
6992 ASSERT(args.length() == 1);
6993
6994 return Top::ReThrow(args[0]);
6995}
6996
6997
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006998static Object* Runtime_PromoteScheduledException(Arguments args) {
6999 ASSERT_EQ(0, args.length());
7000 return Top::PromoteScheduledException();
7001}
7002
7003
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007004static Object* Runtime_ThrowReferenceError(Arguments args) {
7005 HandleScope scope;
7006 ASSERT(args.length() == 1);
7007
7008 Handle<Object> name(args[0]);
7009 Handle<Object> reference_error =
7010 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
7011 return Top::Throw(*reference_error);
7012}
7013
7014
7015static Object* Runtime_StackOverflow(Arguments args) {
7016 NoHandleAllocation na;
7017 return Top::StackOverflow();
7018}
7019
7020
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007021static Object* Runtime_StackGuard(Arguments args) {
7022 ASSERT(args.length() == 1);
7023
7024 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007025 if (StackGuard::IsStackOverflow()) {
7026 return Runtime_StackOverflow(args);
7027 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007028
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007029 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007030}
7031
7032
7033// NOTE: These PrintXXX functions are defined for all builds (not just
7034// DEBUG builds) because we may want to be able to trace function
7035// calls in all modes.
7036static void PrintString(String* str) {
7037 // not uncommon to have empty strings
7038 if (str->length() > 0) {
7039 SmartPointer<char> s =
7040 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
7041 PrintF("%s", *s);
7042 }
7043}
7044
7045
7046static void PrintObject(Object* obj) {
7047 if (obj->IsSmi()) {
7048 PrintF("%d", Smi::cast(obj)->value());
7049 } else if (obj->IsString() || obj->IsSymbol()) {
7050 PrintString(String::cast(obj));
7051 } else if (obj->IsNumber()) {
7052 PrintF("%g", obj->Number());
7053 } else if (obj->IsFailure()) {
7054 PrintF("<failure>");
7055 } else if (obj->IsUndefined()) {
7056 PrintF("<undefined>");
7057 } else if (obj->IsNull()) {
7058 PrintF("<null>");
7059 } else if (obj->IsTrue()) {
7060 PrintF("<true>");
7061 } else if (obj->IsFalse()) {
7062 PrintF("<false>");
7063 } else {
7064 PrintF("%p", obj);
7065 }
7066}
7067
7068
7069static int StackSize() {
7070 int n = 0;
7071 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
7072 return n;
7073}
7074
7075
7076static void PrintTransition(Object* result) {
7077 // indentation
7078 { const int nmax = 80;
7079 int n = StackSize();
7080 if (n <= nmax)
7081 PrintF("%4d:%*s", n, n, "");
7082 else
7083 PrintF("%4d:%*s", n, nmax, "...");
7084 }
7085
7086 if (result == NULL) {
7087 // constructor calls
7088 JavaScriptFrameIterator it;
7089 JavaScriptFrame* frame = it.frame();
7090 if (frame->IsConstructor()) PrintF("new ");
7091 // function name
7092 Object* fun = frame->function();
7093 if (fun->IsJSFunction()) {
7094 PrintObject(JSFunction::cast(fun)->shared()->name());
7095 } else {
7096 PrintObject(fun);
7097 }
7098 // function arguments
7099 // (we are intentionally only printing the actually
7100 // supplied parameters, not all parameters required)
7101 PrintF("(this=");
7102 PrintObject(frame->receiver());
7103 const int length = frame->GetProvidedParametersCount();
7104 for (int i = 0; i < length; i++) {
7105 PrintF(", ");
7106 PrintObject(frame->GetParameter(i));
7107 }
7108 PrintF(") {\n");
7109
7110 } else {
7111 // function result
7112 PrintF("} -> ");
7113 PrintObject(result);
7114 PrintF("\n");
7115 }
7116}
7117
7118
7119static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007120 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007121 NoHandleAllocation ha;
7122 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007123 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007124}
7125
7126
7127static Object* Runtime_TraceExit(Arguments args) {
7128 NoHandleAllocation ha;
7129 PrintTransition(args[0]);
7130 return args[0]; // return TOS
7131}
7132
7133
7134static Object* Runtime_DebugPrint(Arguments args) {
7135 NoHandleAllocation ha;
7136 ASSERT(args.length() == 1);
7137
7138#ifdef DEBUG
7139 if (args[0]->IsString()) {
7140 // If we have a string, assume it's a code "marker"
7141 // and print some interesting cpu debugging info.
7142 JavaScriptFrameIterator it;
7143 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007144 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
7145 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007146 } else {
7147 PrintF("DebugPrint: ");
7148 }
7149 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007150 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007151 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007152 HeapObject::cast(args[0])->map()->Print();
7153 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007154#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007155 // ShortPrint is available in release mode. Print is not.
7156 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007157#endif
7158 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00007159 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007160
7161 return args[0]; // return TOS
7162}
7163
7164
7165static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007166 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007167 NoHandleAllocation ha;
7168 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007169 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007170}
7171
7172
mads.s.ager31e71382008-08-13 09:32:07 +00007173static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007174 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007175 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007176
7177 // According to ECMA-262, section 15.9.1, page 117, the precision of
7178 // the number in a Date object representing a particular instant in
7179 // time is milliseconds. Therefore, we floor the result of getting
7180 // the OS time.
7181 double millis = floor(OS::TimeCurrentMillis());
7182 return Heap::NumberFromDouble(millis);
7183}
7184
7185
7186static Object* Runtime_DateParseString(Arguments args) {
7187 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007188 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007189
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007190 CONVERT_ARG_CHECKED(String, str, 0);
7191 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007192
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007193 CONVERT_ARG_CHECKED(JSArray, output, 1);
7194 RUNTIME_ASSERT(output->HasFastElements());
7195
7196 AssertNoAllocation no_allocation;
7197
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007198 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007199 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
7200 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007201 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007202 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007203 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007204 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007205 result = DateParser::Parse(str->ToUC16Vector(), output_array);
7206 }
7207
7208 if (result) {
7209 return *output;
7210 } else {
7211 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007212 }
7213}
7214
7215
7216static Object* Runtime_DateLocalTimezone(Arguments args) {
7217 NoHandleAllocation ha;
7218 ASSERT(args.length() == 1);
7219
7220 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007221 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007222 return Heap::AllocateStringFromUtf8(CStrVector(zone));
7223}
7224
7225
7226static Object* Runtime_DateLocalTimeOffset(Arguments args) {
7227 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007228 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007229
7230 return Heap::NumberFromDouble(OS::LocalTimeOffset());
7231}
7232
7233
7234static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
7235 NoHandleAllocation ha;
7236 ASSERT(args.length() == 1);
7237
7238 CONVERT_DOUBLE_CHECKED(x, args[0]);
7239 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
7240}
7241
7242
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007243static Object* Runtime_GlobalReceiver(Arguments args) {
7244 ASSERT(args.length() == 1);
7245 Object* global = args[0];
7246 if (!global->IsJSGlobalObject()) return Heap::null_value();
7247 return JSGlobalObject::cast(global)->global_receiver();
7248}
7249
7250
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007251static Object* Runtime_CompileString(Arguments args) {
7252 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007253 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007254 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007255 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007256
ager@chromium.org381abbb2009-02-25 13:23:22 +00007257 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007258 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007259 Compiler::ValidationState validate = (is_json->IsTrue())
7260 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007261 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
7262 context,
7263 true,
7264 validate);
7265 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007266 Handle<JSFunction> fun =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007267 Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007268 return *fun;
7269}
7270
7271
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007272static ObjectPair CompileGlobalEval(Handle<String> source,
7273 Handle<Object> receiver) {
7274 // Deal with a normal eval call with a string argument. Compile it
7275 // and return the compiled function bound in the local context.
7276 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
7277 source,
7278 Handle<Context>(Top::context()),
7279 Top::context()->IsGlobalContext(),
7280 Compiler::DONT_VALIDATE_JSON);
7281 if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
7282 Handle<JSFunction> compiled = Factory::NewFunctionFromSharedFunctionInfo(
7283 shared,
7284 Handle<Context>(Top::context()),
7285 NOT_TENURED);
7286 return MakePair(*compiled, *receiver);
7287}
7288
7289
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007290static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
7291 ASSERT(args.length() == 3);
7292 if (!args[0]->IsJSFunction()) {
7293 return MakePair(Top::ThrowIllegalOperation(), NULL);
7294 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007295
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007296 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007297 Handle<JSFunction> callee = args.at<JSFunction>(0);
7298 Handle<Object> receiver; // Will be overwritten.
7299
7300 // Compute the calling context.
7301 Handle<Context> context = Handle<Context>(Top::context());
7302#ifdef DEBUG
7303 // Make sure Top::context() agrees with the old code that traversed
7304 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007305 StackFrameLocator locator;
7306 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007307 ASSERT(Context::cast(frame->context()) == *context);
7308#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007309
7310 // Find where the 'eval' symbol is bound. It is unaliased only if
7311 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007312 int index = -1;
7313 PropertyAttributes attributes = ABSENT;
7314 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007315 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
7316 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007317 // Stop search when eval is found or when the global context is
7318 // reached.
7319 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007320 if (context->is_function_context()) {
7321 context = Handle<Context>(Context::cast(context->closure()->context()));
7322 } else {
7323 context = Handle<Context>(context->previous());
7324 }
7325 }
7326
iposva@chromium.org245aa852009-02-10 00:49:54 +00007327 // If eval could not be resolved, it has been deleted and we need to
7328 // throw a reference error.
7329 if (attributes == ABSENT) {
7330 Handle<Object> name = Factory::eval_symbol();
7331 Handle<Object> reference_error =
7332 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007333 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007334 }
7335
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007336 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007337 // 'eval' is not bound in the global context. Just call the function
7338 // with the given arguments. This is not necessarily the global eval.
7339 if (receiver->IsContext()) {
7340 context = Handle<Context>::cast(receiver);
7341 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007342 } else if (receiver->IsJSContextExtensionObject()) {
7343 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007344 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007345 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007346 }
7347
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007348 // 'eval' is bound in the global context, but it may have been overwritten.
7349 // Compare it to the builtin 'GlobalEval' function to make sure.
7350 if (*callee != Top::global_context()->global_eval_fun() ||
7351 !args[1]->IsString()) {
7352 return MakePair(*callee, Top::context()->global()->global_receiver());
7353 }
7354
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007355 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
7356}
7357
7358
7359static ObjectPair Runtime_ResolvePossiblyDirectEvalNoLookup(Arguments args) {
7360 ASSERT(args.length() == 3);
7361 if (!args[0]->IsJSFunction()) {
7362 return MakePair(Top::ThrowIllegalOperation(), NULL);
7363 }
7364
7365 HandleScope scope;
7366 Handle<JSFunction> callee = args.at<JSFunction>(0);
7367
7368 // 'eval' is bound in the global context, but it may have been overwritten.
7369 // Compare it to the builtin 'GlobalEval' function to make sure.
7370 if (*callee != Top::global_context()->global_eval_fun() ||
7371 !args[1]->IsString()) {
7372 return MakePair(*callee, Top::context()->global()->global_receiver());
7373 }
7374
7375 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007376}
7377
7378
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007379static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
7380 // This utility adjusts the property attributes for newly created Function
7381 // object ("new Function(...)") by changing the map.
7382 // All it does is changing the prototype property to enumerable
7383 // as specified in ECMA262, 15.3.5.2.
7384 HandleScope scope;
7385 ASSERT(args.length() == 1);
7386 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7387 ASSERT(func->map()->instance_type() ==
7388 Top::function_instance_map()->instance_type());
7389 ASSERT(func->map()->instance_size() ==
7390 Top::function_instance_map()->instance_size());
7391 func->set_map(*Top::function_instance_map());
7392 return *func;
7393}
7394
7395
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007396// Push an array unto an array of arrays if it is not already in the
7397// array. Returns true if the element was pushed on the stack and
7398// false otherwise.
7399static Object* Runtime_PushIfAbsent(Arguments args) {
7400 ASSERT(args.length() == 2);
7401 CONVERT_CHECKED(JSArray, array, args[0]);
7402 CONVERT_CHECKED(JSArray, element, args[1]);
7403 RUNTIME_ASSERT(array->HasFastElements());
7404 int length = Smi::cast(array->length())->value();
7405 FixedArray* elements = FixedArray::cast(array->elements());
7406 for (int i = 0; i < length; i++) {
7407 if (elements->get(i) == element) return Heap::false_value();
7408 }
7409 Object* obj = array->SetFastElement(length, element);
7410 if (obj->IsFailure()) return obj;
7411 return Heap::true_value();
7412}
7413
7414
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007415/**
7416 * A simple visitor visits every element of Array's.
7417 * The backend storage can be a fixed array for fast elements case,
7418 * or a dictionary for sparse array. Since Dictionary is a subtype
7419 * of FixedArray, the class can be used by both fast and slow cases.
7420 * The second parameter of the constructor, fast_elements, specifies
7421 * whether the storage is a FixedArray or Dictionary.
7422 *
7423 * An index limit is used to deal with the situation that a result array
7424 * length overflows 32-bit non-negative integer.
7425 */
7426class ArrayConcatVisitor {
7427 public:
7428 ArrayConcatVisitor(Handle<FixedArray> storage,
7429 uint32_t index_limit,
7430 bool fast_elements) :
7431 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007432 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007433
7434 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007435 if (i >= index_limit_ - index_offset_) return;
7436 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007437
7438 if (fast_elements_) {
7439 ASSERT(index < static_cast<uint32_t>(storage_->length()));
7440 storage_->set(index, *elm);
7441
7442 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007443 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
7444 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007445 Factory::DictionaryAtNumberPut(dict, index, elm);
7446 if (!result.is_identical_to(dict))
7447 storage_ = result;
7448 }
7449 }
7450
7451 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007452 if (index_limit_ - index_offset_ < delta) {
7453 index_offset_ = index_limit_;
7454 } else {
7455 index_offset_ += delta;
7456 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007457 }
7458
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007459 Handle<FixedArray> storage() { return storage_; }
7460
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007461 private:
7462 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007463 // Limit on the accepted indices. Elements with indices larger than the
7464 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007465 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007466 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007467 uint32_t index_offset_;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007468 const bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007469};
7470
7471
ager@chromium.org3811b432009-10-28 14:53:37 +00007472template<class ExternalArrayClass, class ElementType>
7473static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
7474 bool elements_are_ints,
7475 bool elements_are_guaranteed_smis,
7476 uint32_t range,
7477 ArrayConcatVisitor* visitor) {
7478 Handle<ExternalArrayClass> array(
7479 ExternalArrayClass::cast(receiver->elements()));
7480 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
7481
7482 if (visitor != NULL) {
7483 if (elements_are_ints) {
7484 if (elements_are_guaranteed_smis) {
7485 for (uint32_t j = 0; j < len; j++) {
7486 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
7487 visitor->visit(j, e);
7488 }
7489 } else {
7490 for (uint32_t j = 0; j < len; j++) {
7491 int64_t val = static_cast<int64_t>(array->get(j));
7492 if (Smi::IsValid(static_cast<intptr_t>(val))) {
7493 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
7494 visitor->visit(j, e);
7495 } else {
7496 Handle<Object> e(
7497 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
7498 visitor->visit(j, e);
7499 }
7500 }
7501 }
7502 } else {
7503 for (uint32_t j = 0; j < len; j++) {
7504 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
7505 visitor->visit(j, e);
7506 }
7507 }
7508 }
7509
7510 return len;
7511}
7512
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007513/**
7514 * A helper function that visits elements of a JSObject. Only elements
7515 * whose index between 0 and range (exclusive) are visited.
7516 *
7517 * If the third parameter, visitor, is not NULL, the visitor is called
7518 * with parameters, 'visitor_index_offset + element index' and the element.
7519 *
7520 * It returns the number of visisted elements.
7521 */
7522static uint32_t IterateElements(Handle<JSObject> receiver,
7523 uint32_t range,
7524 ArrayConcatVisitor* visitor) {
7525 uint32_t num_of_elements = 0;
7526
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007527 switch (receiver->GetElementsKind()) {
7528 case JSObject::FAST_ELEMENTS: {
7529 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
7530 uint32_t len = elements->length();
7531 if (range < len) {
7532 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007533 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007534
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007535 for (uint32_t j = 0; j < len; j++) {
7536 Handle<Object> e(elements->get(j));
7537 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007538 num_of_elements++;
7539 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007540 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007541 }
7542 }
7543 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007544 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007545 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007546 case JSObject::PIXEL_ELEMENTS: {
7547 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
7548 uint32_t len = pixels->length();
7549 if (range < len) {
7550 len = range;
7551 }
7552
7553 for (uint32_t j = 0; j < len; j++) {
7554 num_of_elements++;
7555 if (visitor != NULL) {
7556 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
7557 visitor->visit(j, e);
7558 }
7559 }
7560 break;
7561 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007562 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
7563 num_of_elements =
7564 IterateExternalArrayElements<ExternalByteArray, int8_t>(
7565 receiver, true, true, range, visitor);
7566 break;
7567 }
7568 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7569 num_of_elements =
7570 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
7571 receiver, true, true, range, visitor);
7572 break;
7573 }
7574 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
7575 num_of_elements =
7576 IterateExternalArrayElements<ExternalShortArray, int16_t>(
7577 receiver, true, true, range, visitor);
7578 break;
7579 }
7580 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7581 num_of_elements =
7582 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
7583 receiver, true, true, range, visitor);
7584 break;
7585 }
7586 case JSObject::EXTERNAL_INT_ELEMENTS: {
7587 num_of_elements =
7588 IterateExternalArrayElements<ExternalIntArray, int32_t>(
7589 receiver, true, false, range, visitor);
7590 break;
7591 }
7592 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7593 num_of_elements =
7594 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
7595 receiver, true, false, range, visitor);
7596 break;
7597 }
7598 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
7599 num_of_elements =
7600 IterateExternalArrayElements<ExternalFloatArray, float>(
7601 receiver, false, false, range, visitor);
7602 break;
7603 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007604 case JSObject::DICTIONARY_ELEMENTS: {
7605 Handle<NumberDictionary> dict(receiver->element_dictionary());
7606 uint32_t capacity = dict->Capacity();
7607 for (uint32_t j = 0; j < capacity; j++) {
7608 Handle<Object> k(dict->KeyAt(j));
7609 if (dict->IsKey(*k)) {
7610 ASSERT(k->IsNumber());
7611 uint32_t index = static_cast<uint32_t>(k->Number());
7612 if (index < range) {
7613 num_of_elements++;
7614 if (visitor) {
7615 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
7616 }
7617 }
7618 }
7619 }
7620 break;
7621 }
7622 default:
7623 UNREACHABLE();
7624 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007625 }
7626
7627 return num_of_elements;
7628}
7629
7630
7631/**
7632 * A helper function that visits elements of an Array object, and elements
7633 * on its prototypes.
7634 *
7635 * Elements on prototypes are visited first, and only elements whose indices
7636 * less than Array length are visited.
7637 *
7638 * If a ArrayConcatVisitor object is given, the visitor is called with
7639 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007640 *
7641 * The returned number of elements is an upper bound on the actual number
7642 * of elements added. If the same element occurs in more than one object
7643 * in the array's prototype chain, it will be counted more than once, but
7644 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007645 */
7646static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
7647 ArrayConcatVisitor* visitor) {
7648 uint32_t range = static_cast<uint32_t>(array->length()->Number());
7649 Handle<Object> obj = array;
7650
7651 static const int kEstimatedPrototypes = 3;
7652 List< Handle<JSObject> > objects(kEstimatedPrototypes);
7653
7654 // Visit prototype first. If an element on the prototype is shadowed by
7655 // the inheritor using the same index, the ArrayConcatVisitor visits
7656 // the prototype element before the shadowing element.
7657 // The visitor can simply overwrite the old value by new value using
7658 // the same index. This follows Array::concat semantics.
7659 while (!obj->IsNull()) {
7660 objects.Add(Handle<JSObject>::cast(obj));
7661 obj = Handle<Object>(obj->GetPrototype());
7662 }
7663
7664 uint32_t nof_elements = 0;
7665 for (int i = objects.length() - 1; i >= 0; i--) {
7666 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007667 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007668 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007669
7670 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
7671 nof_elements = JSObject::kMaxElementCount;
7672 } else {
7673 nof_elements += encountered_elements;
7674 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007675 }
7676
7677 return nof_elements;
7678}
7679
7680
7681/**
7682 * A helper function of Runtime_ArrayConcat.
7683 *
7684 * The first argument is an Array of arrays and objects. It is the
7685 * same as the arguments array of Array::concat JS function.
7686 *
7687 * If an argument is an Array object, the function visits array
7688 * elements. If an argument is not an Array object, the function
7689 * visits the object as if it is an one-element array.
7690 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007691 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007692 * non-negative number is used as new length. For example, if one
7693 * array length is 2^32 - 1, second array length is 1, the
7694 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007695 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
7696 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007697 */
7698static uint32_t IterateArguments(Handle<JSArray> arguments,
7699 ArrayConcatVisitor* visitor) {
7700 uint32_t visited_elements = 0;
7701 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7702
7703 for (uint32_t i = 0; i < num_of_args; i++) {
7704 Handle<Object> obj(arguments->GetElement(i));
7705 if (obj->IsJSArray()) {
7706 Handle<JSArray> array = Handle<JSArray>::cast(obj);
7707 uint32_t len = static_cast<uint32_t>(array->length()->Number());
7708 uint32_t nof_elements =
7709 IterateArrayAndPrototypeElements(array, visitor);
7710 // Total elements of array and its prototype chain can be more than
7711 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007712 // the array length number of elements. We use the length as an estimate
7713 // for the actual number of elements added.
7714 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
7715 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
7716 visited_elements = JSArray::kMaxElementCount;
7717 } else {
7718 visited_elements += added_elements;
7719 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007720 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007721 } else {
7722 if (visitor) {
7723 visitor->visit(0, obj);
7724 visitor->increase_index_offset(1);
7725 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007726 if (visited_elements < JSArray::kMaxElementCount) {
7727 visited_elements++;
7728 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007729 }
7730 }
7731 return visited_elements;
7732}
7733
7734
7735/**
7736 * Array::concat implementation.
7737 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007738 * TODO(lrn): Fix non-compliance for very large concatenations and update to
7739 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007740 */
7741static Object* Runtime_ArrayConcat(Arguments args) {
7742 ASSERT(args.length() == 1);
7743 HandleScope handle_scope;
7744
7745 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
7746 Handle<JSArray> arguments(arg_arrays);
7747
7748 // Pass 1: estimate the number of elements of the result
7749 // (it could be more than real numbers if prototype has elements).
7750 uint32_t result_length = 0;
7751 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7752
7753 { AssertNoAllocation nogc;
7754 for (uint32_t i = 0; i < num_of_args; i++) {
7755 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007756 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007757 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007758 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007759 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
7760 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007761 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007762 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007763 if (JSObject::kMaxElementCount - result_length < length_estimate) {
7764 result_length = JSObject::kMaxElementCount;
7765 break;
7766 }
7767 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007768 }
7769 }
7770
7771 // Allocate an empty array, will set length and content later.
7772 Handle<JSArray> result = Factory::NewJSArray(0);
7773
7774 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
7775 // If estimated number of elements is more than half of length, a
7776 // fixed array (fast case) is more time and space-efficient than a
7777 // dictionary.
7778 bool fast_case = (estimate_nof_elements * 2) >= result_length;
7779
7780 Handle<FixedArray> storage;
7781 if (fast_case) {
7782 // The backing storage array must have non-existing elements to
7783 // preserve holes across concat operations.
7784 storage = Factory::NewFixedArrayWithHoles(result_length);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007785 result->set_map(*Factory::GetFastElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007786 } else {
7787 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
7788 uint32_t at_least_space_for = estimate_nof_elements +
7789 (estimate_nof_elements >> 2);
7790 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007791 Factory::NewNumberDictionary(at_least_space_for));
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007792 result->set_map(*Factory::GetSlowElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007793 }
7794
7795 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
7796
7797 ArrayConcatVisitor visitor(storage, result_length, fast_case);
7798
7799 IterateArguments(arguments, &visitor);
7800
7801 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007802 // Please note the storage might have changed in the visitor.
7803 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007804
7805 return *result;
7806}
7807
7808
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007809// This will not allocate (flatten the string), but it may run
7810// very slowly for very deeply nested ConsStrings. For debugging use only.
7811static Object* Runtime_GlobalPrint(Arguments args) {
7812 NoHandleAllocation ha;
7813 ASSERT(args.length() == 1);
7814
7815 CONVERT_CHECKED(String, string, args[0]);
7816 StringInputBuffer buffer(string);
7817 while (buffer.has_more()) {
7818 uint16_t character = buffer.GetNext();
7819 PrintF("%c", character);
7820 }
7821 return string;
7822}
7823
ager@chromium.org5ec48922009-05-05 07:25:34 +00007824// Moves all own elements of an object, that are below a limit, to positions
7825// starting at zero. All undefined values are placed after non-undefined values,
7826// and are followed by non-existing element. Does not change the length
7827// property.
7828// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007829static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007830 ASSERT(args.length() == 2);
7831 CONVERT_CHECKED(JSObject, object, args[0]);
7832 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
7833 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007834}
7835
7836
7837// Move contents of argument 0 (an array) to argument 1 (an array)
7838static Object* Runtime_MoveArrayContents(Arguments args) {
7839 ASSERT(args.length() == 2);
7840 CONVERT_CHECKED(JSArray, from, args[0]);
7841 CONVERT_CHECKED(JSArray, to, args[1]);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007842 HeapObject* new_elements = from->elements();
7843 Object* new_map;
7844 if (new_elements->map() == Heap::fixed_array_map()) {
7845 new_map = to->map()->GetFastElementsMap();
7846 } else {
7847 new_map = to->map()->GetSlowElementsMap();
7848 }
7849 if (new_map->IsFailure()) return new_map;
7850 to->set_map(Map::cast(new_map));
7851 to->set_elements(new_elements);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007852 to->set_length(from->length());
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007853 Object* obj = from->ResetElements();
7854 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007855 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007856 return to;
7857}
7858
7859
7860// How many elements does this array have?
7861static Object* Runtime_EstimateNumberOfElements(Arguments args) {
7862 ASSERT(args.length() == 1);
7863 CONVERT_CHECKED(JSArray, array, args[0]);
7864 HeapObject* elements = array->elements();
7865 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007866 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007867 } else {
7868 return array->length();
7869 }
7870}
7871
7872
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007873static Object* Runtime_SwapElements(Arguments args) {
7874 HandleScope handle_scope;
7875
7876 ASSERT_EQ(3, args.length());
7877
ager@chromium.orgac091b72010-05-05 07:34:42 +00007878 CONVERT_ARG_CHECKED(JSObject, object, 0);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007879 Handle<Object> key1 = args.at<Object>(1);
7880 Handle<Object> key2 = args.at<Object>(2);
7881
7882 uint32_t index1, index2;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007883 if (!key1->ToArrayIndex(&index1)
7884 || !key2->ToArrayIndex(&index2)) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00007885 return Top::ThrowIllegalOperation();
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007886 }
7887
ager@chromium.orgac091b72010-05-05 07:34:42 +00007888 Handle<JSObject> jsobject = Handle<JSObject>::cast(object);
7889 Handle<Object> tmp1 = GetElement(jsobject, index1);
7890 Handle<Object> tmp2 = GetElement(jsobject, index2);
7891
7892 SetElement(jsobject, index1, tmp2);
7893 SetElement(jsobject, index2, tmp1);
7894
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007895 return Heap::undefined_value();
7896}
7897
7898
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007899// Returns an array that tells you where in the [0, length) interval an array
7900// might have elements. Can either return keys or intervals. Keys can have
7901// gaps in (undefined). Intervals can also span over some undefined keys.
7902static Object* Runtime_GetArrayKeys(Arguments args) {
7903 ASSERT(args.length() == 2);
7904 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007905 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007906 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007907 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007908 // Create an array and get all the keys into it, then remove all the
7909 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007910 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007911 int keys_length = keys->length();
7912 for (int i = 0; i < keys_length; i++) {
7913 Object* key = keys->get(i);
7914 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007915 if (!key->ToArrayIndex(&index) || index >= length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007916 // Zap invalid keys.
7917 keys->set_undefined(i);
7918 }
7919 }
7920 return *Factory::NewJSArrayWithElements(keys);
7921 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007922 ASSERT(array->HasFastElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007923 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
7924 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007925 single_interval->set(0, Smi::FromInt(-1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007926 uint32_t actual_length =
7927 static_cast<uint32_t>(FixedArray::cast(array->elements())->length());
ager@chromium.org5ec48922009-05-05 07:25:34 +00007928 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007929 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00007930 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007931 single_interval->set(1, *length_object);
7932 return *Factory::NewJSArrayWithElements(single_interval);
7933 }
7934}
7935
7936
7937// DefineAccessor takes an optional final argument which is the
7938// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
7939// to the way accessors are implemented, it is set for both the getter
7940// and setter on the first call to DefineAccessor and ignored on
7941// subsequent calls.
7942static Object* Runtime_DefineAccessor(Arguments args) {
7943 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
7944 // Compute attributes.
7945 PropertyAttributes attributes = NONE;
7946 if (args.length() == 5) {
7947 CONVERT_CHECKED(Smi, attrs, args[4]);
7948 int value = attrs->value();
7949 // Only attribute bits should be set.
7950 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
7951 attributes = static_cast<PropertyAttributes>(value);
7952 }
7953
7954 CONVERT_CHECKED(JSObject, obj, args[0]);
7955 CONVERT_CHECKED(String, name, args[1]);
7956 CONVERT_CHECKED(Smi, flag, args[2]);
7957 CONVERT_CHECKED(JSFunction, fun, args[3]);
7958 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
7959}
7960
7961
7962static Object* Runtime_LookupAccessor(Arguments args) {
7963 ASSERT(args.length() == 3);
7964 CONVERT_CHECKED(JSObject, obj, args[0]);
7965 CONVERT_CHECKED(String, name, args[1]);
7966 CONVERT_CHECKED(Smi, flag, args[2]);
7967 return obj->LookupAccessor(name, flag->value() == 0);
7968}
7969
7970
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007971#ifdef ENABLE_DEBUGGER_SUPPORT
7972static Object* Runtime_DebugBreak(Arguments args) {
7973 ASSERT(args.length() == 0);
7974 return Execution::DebugBreakHelper();
7975}
7976
7977
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007978// Helper functions for wrapping and unwrapping stack frame ids.
7979static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007980 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007981 return Smi::FromInt(id >> 2);
7982}
7983
7984
7985static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
7986 return static_cast<StackFrame::Id>(wrapped->value() << 2);
7987}
7988
7989
7990// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00007991// args[0]: debug event listener function to set or null or undefined for
7992// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007993// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00007994static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007995 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007996 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
7997 args[0]->IsUndefined() ||
7998 args[0]->IsNull());
7999 Handle<Object> callback = args.at<Object>(0);
8000 Handle<Object> data = args.at<Object>(1);
8001 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008002
8003 return Heap::undefined_value();
8004}
8005
8006
8007static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00008008 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008009 StackGuard::DebugBreak();
8010 return Heap::undefined_value();
8011}
8012
8013
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008014static Object* DebugLookupResultValue(Object* receiver, String* name,
8015 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00008016 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008017 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008018 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008019 case NORMAL:
8020 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008021 if (value->IsTheHole()) {
8022 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008023 }
8024 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008025 case FIELD:
8026 value =
8027 JSObject::cast(
8028 result->holder())->FastPropertyAt(result->GetFieldIndex());
8029 if (value->IsTheHole()) {
8030 return Heap::undefined_value();
8031 }
8032 return value;
8033 case CONSTANT_FUNCTION:
8034 return result->GetConstantFunction();
8035 case CALLBACKS: {
8036 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008037 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008038 value = receiver->GetPropertyWithCallback(
8039 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00008040 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008041 value = Top::pending_exception();
8042 Top::clear_pending_exception();
8043 if (caught_exception != NULL) {
8044 *caught_exception = true;
8045 }
8046 }
8047 return value;
8048 } else {
8049 return Heap::undefined_value();
8050 }
8051 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008052 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008053 case MAP_TRANSITION:
8054 case CONSTANT_TRANSITION:
8055 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008056 return Heap::undefined_value();
8057 default:
8058 UNREACHABLE();
8059 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008060 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008061 return Heap::undefined_value();
8062}
8063
8064
ager@chromium.org32912102009-01-16 10:38:43 +00008065// Get debugger related details for an object property.
8066// args[0]: object holding property
8067// args[1]: name of the property
8068//
8069// The array returned contains the following information:
8070// 0: Property value
8071// 1: Property details
8072// 2: Property value is exception
8073// 3: Getter function if defined
8074// 4: Setter function if defined
8075// Items 2-4 are only filled if the property has either a getter or a setter
8076// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00008077static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008078 HandleScope scope;
8079
8080 ASSERT(args.length() == 2);
8081
8082 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8083 CONVERT_ARG_CHECKED(String, name, 1);
8084
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008085 // Make sure to set the current context to the context before the debugger was
8086 // entered (if the debugger is entered). The reason for switching context here
8087 // is that for some property lookups (accessors and interceptors) callbacks
8088 // into the embedding application can occour, and the embedding application
8089 // could have the assumption that its own global context is the current
8090 // context and not some internal debugger context.
8091 SaveContext save;
8092 if (Debug::InDebugger()) {
8093 Top::set_context(*Debug::debugger_entry()->GetContext());
8094 }
8095
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008096 // Skip the global proxy as it has no properties and always delegates to the
8097 // real global object.
8098 if (obj->IsJSGlobalProxy()) {
8099 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
8100 }
8101
8102
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008103 // Check if the name is trivially convertible to an index and get the element
8104 // if so.
8105 uint32_t index;
8106 if (name->AsArrayIndex(&index)) {
8107 Handle<FixedArray> details = Factory::NewFixedArray(2);
8108 details->set(0, Runtime::GetElementOrCharAt(obj, index));
8109 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
8110 return *Factory::NewJSArrayWithElements(details);
8111 }
8112
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008113 // Find the number of objects making up this.
8114 int length = LocalPrototypeChainLength(*obj);
8115
8116 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008117 Handle<JSObject> jsproto = obj;
8118 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008119 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008120 jsproto->LocalLookup(*name, &result);
8121 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008122 // LookupResult is not GC safe as it holds raw object pointers.
8123 // GC can happen later in this code so put the required fields into
8124 // local variables using handles when required for later use.
8125 PropertyType result_type = result.type();
8126 Handle<Object> result_callback_obj;
8127 if (result_type == CALLBACKS) {
8128 result_callback_obj = Handle<Object>(result.GetCallbackObject());
8129 }
8130 Smi* property_details = result.GetPropertyDetails().AsSmi();
8131 // DebugLookupResultValue can cause GC so details from LookupResult needs
8132 // to be copied to handles before this.
8133 bool caught_exception = false;
8134 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
8135 &caught_exception);
8136 if (raw_value->IsFailure()) return raw_value;
8137 Handle<Object> value(raw_value);
8138
8139 // If the callback object is a fixed array then it contains JavaScript
8140 // getter and/or setter.
8141 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
8142 result_callback_obj->IsFixedArray();
8143 Handle<FixedArray> details =
8144 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
8145 details->set(0, *value);
8146 details->set(1, property_details);
8147 if (hasJavaScriptAccessors) {
8148 details->set(2,
8149 caught_exception ? Heap::true_value()
8150 : Heap::false_value());
8151 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
8152 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
8153 }
8154
8155 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008156 }
8157 if (i < length - 1) {
8158 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
8159 }
8160 }
8161
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008162 return Heap::undefined_value();
8163}
8164
8165
8166static Object* Runtime_DebugGetProperty(Arguments args) {
8167 HandleScope scope;
8168
8169 ASSERT(args.length() == 2);
8170
8171 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8172 CONVERT_ARG_CHECKED(String, name, 1);
8173
8174 LookupResult result;
8175 obj->Lookup(*name, &result);
8176 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008177 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008178 }
8179 return Heap::undefined_value();
8180}
8181
8182
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008183// Return the property type calculated from the property details.
8184// args[0]: smi with property details.
8185static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
8186 ASSERT(args.length() == 1);
8187 CONVERT_CHECKED(Smi, details, args[0]);
8188 PropertyType type = PropertyDetails(details).type();
8189 return Smi::FromInt(static_cast<int>(type));
8190}
8191
8192
8193// Return the property attribute calculated from the property details.
8194// args[0]: smi with property details.
8195static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
8196 ASSERT(args.length() == 1);
8197 CONVERT_CHECKED(Smi, details, args[0]);
8198 PropertyAttributes attributes = PropertyDetails(details).attributes();
8199 return Smi::FromInt(static_cast<int>(attributes));
8200}
8201
8202
8203// Return the property insertion index calculated from the property details.
8204// args[0]: smi with property details.
8205static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
8206 ASSERT(args.length() == 1);
8207 CONVERT_CHECKED(Smi, details, args[0]);
8208 int index = PropertyDetails(details).index();
8209 return Smi::FromInt(index);
8210}
8211
8212
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008213// Return property value from named interceptor.
8214// args[0]: object
8215// args[1]: property name
8216static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
8217 HandleScope scope;
8218 ASSERT(args.length() == 2);
8219 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8220 RUNTIME_ASSERT(obj->HasNamedInterceptor());
8221 CONVERT_ARG_CHECKED(String, name, 1);
8222
8223 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008224 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008225}
8226
8227
8228// Return element value from indexed interceptor.
8229// args[0]: object
8230// args[1]: index
8231static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
8232 HandleScope scope;
8233 ASSERT(args.length() == 2);
8234 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8235 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
8236 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
8237
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008238 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008239}
8240
8241
8242static Object* Runtime_CheckExecutionState(Arguments args) {
8243 ASSERT(args.length() >= 1);
8244 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00008245 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008246 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008247 return Top::Throw(Heap::illegal_execution_state_symbol());
8248 }
8249
8250 return Heap::true_value();
8251}
8252
8253
8254static Object* Runtime_GetFrameCount(Arguments args) {
8255 HandleScope scope;
8256 ASSERT(args.length() == 1);
8257
8258 // Check arguments.
8259 Object* result = Runtime_CheckExecutionState(args);
8260 if (result->IsFailure()) return result;
8261
8262 // Count all frames which are relevant to debugging stack trace.
8263 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008264 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008265 if (id == StackFrame::NO_ID) {
8266 // If there is no JavaScript stack frame count is 0.
8267 return Smi::FromInt(0);
8268 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008269 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
8270 return Smi::FromInt(n);
8271}
8272
8273
8274static const int kFrameDetailsFrameIdIndex = 0;
8275static const int kFrameDetailsReceiverIndex = 1;
8276static const int kFrameDetailsFunctionIndex = 2;
8277static const int kFrameDetailsArgumentCountIndex = 3;
8278static const int kFrameDetailsLocalCountIndex = 4;
8279static const int kFrameDetailsSourcePositionIndex = 5;
8280static const int kFrameDetailsConstructCallIndex = 6;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008281static const int kFrameDetailsAtReturnIndex = 7;
8282static const int kFrameDetailsDebuggerFrameIndex = 8;
8283static const int kFrameDetailsFirstDynamicIndex = 9;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008284
8285// Return an array with frame details
8286// args[0]: number: break id
8287// args[1]: number: frame index
8288//
8289// The array returned contains the following information:
8290// 0: Frame id
8291// 1: Receiver
8292// 2: Function
8293// 3: Argument count
8294// 4: Local count
8295// 5: Source position
8296// 6: Constructor call
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008297// 7: Is at return
8298// 8: Debugger frame
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008299// Arguments name, value
8300// Locals name, value
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008301// Return value if any
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008302static Object* Runtime_GetFrameDetails(Arguments args) {
8303 HandleScope scope;
8304 ASSERT(args.length() == 2);
8305
8306 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008307 Object* check = Runtime_CheckExecutionState(args);
8308 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008309 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8310
8311 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008312 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008313 if (id == StackFrame::NO_ID) {
8314 // If there are no JavaScript stack frames return undefined.
8315 return Heap::undefined_value();
8316 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008317 int count = 0;
8318 JavaScriptFrameIterator it(id);
8319 for (; !it.done(); it.Advance()) {
8320 if (count == index) break;
8321 count++;
8322 }
8323 if (it.done()) return Heap::undefined_value();
8324
8325 // Traverse the saved contexts chain to find the active context for the
8326 // selected frame.
8327 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008328 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008329 save = save->prev();
8330 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008331 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008332
8333 // Get the frame id.
8334 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
8335
8336 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008337 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008338
8339 // Check for constructor frame.
8340 bool constructor = it.frame()->IsConstructor();
8341
8342 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008343 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008344 ScopeInfo<> info(*code);
8345
8346 // Get the context.
8347 Handle<Context> context(Context::cast(it.frame()->context()));
8348
8349 // Get the locals names and values into a temporary array.
8350 //
8351 // TODO(1240907): Hide compiler-introduced stack variables
8352 // (e.g. .result)? For users of the debugger, they will probably be
8353 // confusing.
8354 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
8355 for (int i = 0; i < info.NumberOfLocals(); i++) {
8356 // Name of the local.
8357 locals->set(i * 2, *info.LocalName(i));
8358
8359 // Fetch the value of the local - either from the stack or from a
8360 // heap-allocated context.
8361 if (i < info.number_of_stack_slots()) {
8362 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
8363 } else {
8364 Handle<String> name = info.LocalName(i);
8365 // Traverse the context chain to the function context as all local
8366 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008367 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008368 context = Handle<Context>(context->previous());
8369 }
8370 ASSERT(context->is_function_context());
8371 locals->set(i * 2 + 1,
8372 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
8373 NULL)));
8374 }
8375 }
8376
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008377 // Check whether this frame is positioned at return.
8378 int at_return = (index == 0) ? Debug::IsBreakAtReturn(it.frame()) : false;
8379
8380 // If positioned just before return find the value to be returned and add it
8381 // to the frame information.
8382 Handle<Object> return_value = Factory::undefined_value();
8383 if (at_return) {
8384 StackFrameIterator it2;
8385 Address internal_frame_sp = NULL;
8386 while (!it2.done()) {
8387 if (it2.frame()->is_internal()) {
8388 internal_frame_sp = it2.frame()->sp();
8389 } else {
8390 if (it2.frame()->is_java_script()) {
8391 if (it2.frame()->id() == it.frame()->id()) {
8392 // The internal frame just before the JavaScript frame contains the
8393 // value to return on top. A debug break at return will create an
8394 // internal frame to store the return value (eax/rax/r0) before
8395 // entering the debug break exit frame.
8396 if (internal_frame_sp != NULL) {
8397 return_value =
8398 Handle<Object>(Memory::Object_at(internal_frame_sp));
8399 break;
8400 }
8401 }
8402 }
8403
8404 // Indicate that the previous frame was not an internal frame.
8405 internal_frame_sp = NULL;
8406 }
8407 it2.Advance();
8408 }
8409 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008410
8411 // Now advance to the arguments adapter frame (if any). It contains all
8412 // the provided parameters whereas the function frame always have the number
8413 // of arguments matching the functions parameters. The rest of the
8414 // information (except for what is collected above) is the same.
8415 it.AdvanceToArgumentsFrame();
8416
8417 // Find the number of arguments to fill. At least fill the number of
8418 // parameters for the function and fill more if more parameters are provided.
8419 int argument_count = info.number_of_parameters();
8420 if (argument_count < it.frame()->GetProvidedParametersCount()) {
8421 argument_count = it.frame()->GetProvidedParametersCount();
8422 }
8423
8424 // Calculate the size of the result.
8425 int details_size = kFrameDetailsFirstDynamicIndex +
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008426 2 * (argument_count + info.NumberOfLocals()) +
8427 (at_return ? 1 : 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008428 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8429
8430 // Add the frame id.
8431 details->set(kFrameDetailsFrameIdIndex, *frame_id);
8432
8433 // Add the function (same as in function frame).
8434 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
8435
8436 // Add the arguments count.
8437 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
8438
8439 // Add the locals count
8440 details->set(kFrameDetailsLocalCountIndex,
8441 Smi::FromInt(info.NumberOfLocals()));
8442
8443 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00008444 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008445 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
8446 } else {
8447 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
8448 }
8449
8450 // Add the constructor information.
8451 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
8452
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008453 // Add the at return information.
8454 details->set(kFrameDetailsAtReturnIndex, Heap::ToBoolean(at_return));
8455
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008456 // Add information on whether this frame is invoked in the debugger context.
8457 details->set(kFrameDetailsDebuggerFrameIndex,
8458 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
8459
8460 // Fill the dynamic part.
8461 int details_index = kFrameDetailsFirstDynamicIndex;
8462
8463 // Add arguments name and value.
8464 for (int i = 0; i < argument_count; i++) {
8465 // Name of the argument.
8466 if (i < info.number_of_parameters()) {
8467 details->set(details_index++, *info.parameter_name(i));
8468 } else {
8469 details->set(details_index++, Heap::undefined_value());
8470 }
8471
8472 // Parameter value.
8473 if (i < it.frame()->GetProvidedParametersCount()) {
8474 details->set(details_index++, it.frame()->GetParameter(i));
8475 } else {
8476 details->set(details_index++, Heap::undefined_value());
8477 }
8478 }
8479
8480 // Add locals name and value from the temporary copy from the function frame.
8481 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
8482 details->set(details_index++, locals->get(i));
8483 }
8484
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008485 // Add the value being returned.
8486 if (at_return) {
8487 details->set(details_index++, *return_value);
8488 }
8489
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008490 // Add the receiver (same as in function frame).
8491 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
8492 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
8493 Handle<Object> receiver(it.frame()->receiver());
8494 if (!receiver->IsJSObject()) {
8495 // If the receiver is NOT a JSObject we have hit an optimization
8496 // where a value object is not converted into a wrapped JS objects.
8497 // To hide this optimization from the debugger, we wrap the receiver
8498 // by creating correct wrapper object based on the calling frame's
8499 // global context.
8500 it.Advance();
8501 Handle<Context> calling_frames_global_context(
8502 Context::cast(Context::cast(it.frame()->context())->global_context()));
8503 receiver = Factory::ToObject(receiver, calling_frames_global_context);
8504 }
8505 details->set(kFrameDetailsReceiverIndex, *receiver);
8506
8507 ASSERT_EQ(details_size, details_index);
8508 return *Factory::NewJSArrayWithElements(details);
8509}
8510
8511
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008512// Copy all the context locals into an object used to materialize a scope.
8513static void CopyContextLocalsToScopeObject(Handle<Code> code,
8514 ScopeInfo<>& scope_info,
8515 Handle<Context> context,
8516 Handle<JSObject> scope_object) {
8517 // Fill all context locals to the context extension.
8518 for (int i = Context::MIN_CONTEXT_SLOTS;
8519 i < scope_info.number_of_context_slots();
8520 i++) {
8521 int context_index =
8522 ScopeInfo<>::ContextSlotIndex(*code,
8523 *scope_info.context_slot_name(i),
8524 NULL);
8525
8526 // Don't include the arguments shadow (.arguments) context variable.
8527 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
8528 SetProperty(scope_object,
8529 scope_info.context_slot_name(i),
8530 Handle<Object>(context->get(context_index)), NONE);
8531 }
8532 }
8533}
8534
8535
8536// Create a plain JSObject which materializes the local scope for the specified
8537// frame.
8538static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
8539 Handle<JSFunction> function(JSFunction::cast(frame->function()));
8540 Handle<Code> code(function->code());
8541 ScopeInfo<> scope_info(*code);
8542
8543 // Allocate and initialize a JSObject with all the arguments, stack locals
8544 // heap locals and extension properties of the debugged function.
8545 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
8546
8547 // First fill all parameters.
8548 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8549 SetProperty(local_scope,
8550 scope_info.parameter_name(i),
8551 Handle<Object>(frame->GetParameter(i)), NONE);
8552 }
8553
8554 // Second fill all stack locals.
8555 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
8556 SetProperty(local_scope,
8557 scope_info.stack_slot_name(i),
8558 Handle<Object>(frame->GetExpression(i)), NONE);
8559 }
8560
8561 // Third fill all context locals.
8562 Handle<Context> frame_context(Context::cast(frame->context()));
8563 Handle<Context> function_context(frame_context->fcontext());
8564 CopyContextLocalsToScopeObject(code, scope_info,
8565 function_context, local_scope);
8566
8567 // Finally copy any properties from the function context extension. This will
8568 // be variables introduced by eval.
8569 if (function_context->closure() == *function) {
8570 if (function_context->has_extension() &&
8571 !function_context->IsGlobalContext()) {
8572 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008573 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008574 for (int i = 0; i < keys->length(); i++) {
8575 // Names of variables introduced by eval are strings.
8576 ASSERT(keys->get(i)->IsString());
8577 Handle<String> key(String::cast(keys->get(i)));
8578 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
8579 }
8580 }
8581 }
8582 return local_scope;
8583}
8584
8585
8586// Create a plain JSObject which materializes the closure content for the
8587// context.
8588static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
8589 ASSERT(context->is_function_context());
8590
8591 Handle<Code> code(context->closure()->code());
8592 ScopeInfo<> scope_info(*code);
8593
8594 // Allocate and initialize a JSObject with all the content of theis function
8595 // closure.
8596 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
8597
8598 // Check whether the arguments shadow object exists.
8599 int arguments_shadow_index =
8600 ScopeInfo<>::ContextSlotIndex(*code,
8601 Heap::arguments_shadow_symbol(),
8602 NULL);
8603 if (arguments_shadow_index >= 0) {
8604 // In this case all the arguments are available in the arguments shadow
8605 // object.
8606 Handle<JSObject> arguments_shadow(
8607 JSObject::cast(context->get(arguments_shadow_index)));
8608 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8609 SetProperty(closure_scope,
8610 scope_info.parameter_name(i),
8611 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
8612 }
8613 }
8614
8615 // Fill all context locals to the context extension.
8616 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
8617
8618 // Finally copy any properties from the function context extension. This will
8619 // be variables introduced by eval.
8620 if (context->has_extension()) {
8621 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008622 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008623 for (int i = 0; i < keys->length(); i++) {
8624 // Names of variables introduced by eval are strings.
8625 ASSERT(keys->get(i)->IsString());
8626 Handle<String> key(String::cast(keys->get(i)));
8627 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
8628 }
8629 }
8630
8631 return closure_scope;
8632}
8633
8634
8635// Iterate over the actual scopes visible from a stack frame. All scopes are
8636// backed by an actual context except the local scope, which is inserted
8637// "artifically" in the context chain.
8638class ScopeIterator {
8639 public:
8640 enum ScopeType {
8641 ScopeTypeGlobal = 0,
8642 ScopeTypeLocal,
8643 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00008644 ScopeTypeClosure,
8645 // Every catch block contains an implicit with block (its parameter is
8646 // a JSContextExtensionObject) that extends current scope with a variable
8647 // holding exception object. Such with blocks are treated as scopes of their
8648 // own type.
8649 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008650 };
8651
8652 explicit ScopeIterator(JavaScriptFrame* frame)
8653 : frame_(frame),
8654 function_(JSFunction::cast(frame->function())),
8655 context_(Context::cast(frame->context())),
8656 local_done_(false),
8657 at_local_(false) {
8658
8659 // Check whether the first scope is actually a local scope.
8660 if (context_->IsGlobalContext()) {
8661 // If there is a stack slot for .result then this local scope has been
8662 // created for evaluating top level code and it is not a real local scope.
8663 // Checking for the existence of .result seems fragile, but the scope info
8664 // saved with the code object does not otherwise have that information.
8665 Handle<Code> code(function_->code());
8666 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
8667 at_local_ = index < 0;
8668 } else if (context_->is_function_context()) {
8669 at_local_ = true;
8670 }
8671 }
8672
8673 // More scopes?
8674 bool Done() { return context_.is_null(); }
8675
8676 // Move to the next scope.
8677 void Next() {
8678 // If at a local scope mark the local scope as passed.
8679 if (at_local_) {
8680 at_local_ = false;
8681 local_done_ = true;
8682
8683 // If the current context is not associated with the local scope the
8684 // current context is the next real scope, so don't move to the next
8685 // context in this case.
8686 if (context_->closure() != *function_) {
8687 return;
8688 }
8689 }
8690
8691 // The global scope is always the last in the chain.
8692 if (context_->IsGlobalContext()) {
8693 context_ = Handle<Context>();
8694 return;
8695 }
8696
8697 // Move to the next context.
8698 if (context_->is_function_context()) {
8699 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
8700 } else {
8701 context_ = Handle<Context>(context_->previous());
8702 }
8703
8704 // If passing the local scope indicate that the current scope is now the
8705 // local scope.
8706 if (!local_done_ &&
8707 (context_->IsGlobalContext() || (context_->is_function_context()))) {
8708 at_local_ = true;
8709 }
8710 }
8711
8712 // Return the type of the current scope.
8713 int Type() {
8714 if (at_local_) {
8715 return ScopeTypeLocal;
8716 }
8717 if (context_->IsGlobalContext()) {
8718 ASSERT(context_->global()->IsGlobalObject());
8719 return ScopeTypeGlobal;
8720 }
8721 if (context_->is_function_context()) {
8722 return ScopeTypeClosure;
8723 }
8724 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00008725 // Current scope is either an explicit with statement or a with statement
8726 // implicitely generated for a catch block.
8727 // If the extension object here is a JSContextExtensionObject then
8728 // current with statement is one frome a catch block otherwise it's a
8729 // regular with statement.
8730 if (context_->extension()->IsJSContextExtensionObject()) {
8731 return ScopeTypeCatch;
8732 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008733 return ScopeTypeWith;
8734 }
8735
8736 // Return the JavaScript object with the content of the current scope.
8737 Handle<JSObject> ScopeObject() {
8738 switch (Type()) {
8739 case ScopeIterator::ScopeTypeGlobal:
8740 return Handle<JSObject>(CurrentContext()->global());
8741 break;
8742 case ScopeIterator::ScopeTypeLocal:
8743 // Materialize the content of the local scope into a JSObject.
8744 return MaterializeLocalScope(frame_);
8745 break;
8746 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00008747 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008748 // Return the with object.
8749 return Handle<JSObject>(CurrentContext()->extension());
8750 break;
8751 case ScopeIterator::ScopeTypeClosure:
8752 // Materialize the content of the closure scope into a JSObject.
8753 return MaterializeClosure(CurrentContext());
8754 break;
8755 }
8756 UNREACHABLE();
8757 return Handle<JSObject>();
8758 }
8759
8760 // Return the context for this scope. For the local context there might not
8761 // be an actual context.
8762 Handle<Context> CurrentContext() {
8763 if (at_local_ && context_->closure() != *function_) {
8764 return Handle<Context>();
8765 }
8766 return context_;
8767 }
8768
8769#ifdef DEBUG
8770 // Debug print of the content of the current scope.
8771 void DebugPrint() {
8772 switch (Type()) {
8773 case ScopeIterator::ScopeTypeGlobal:
8774 PrintF("Global:\n");
8775 CurrentContext()->Print();
8776 break;
8777
8778 case ScopeIterator::ScopeTypeLocal: {
8779 PrintF("Local:\n");
8780 Handle<Code> code(function_->code());
8781 ScopeInfo<> scope_info(*code);
8782 scope_info.Print();
8783 if (!CurrentContext().is_null()) {
8784 CurrentContext()->Print();
8785 if (CurrentContext()->has_extension()) {
8786 Handle<JSObject> extension =
8787 Handle<JSObject>(CurrentContext()->extension());
8788 if (extension->IsJSContextExtensionObject()) {
8789 extension->Print();
8790 }
8791 }
8792 }
8793 break;
8794 }
8795
8796 case ScopeIterator::ScopeTypeWith: {
8797 PrintF("With:\n");
8798 Handle<JSObject> extension =
8799 Handle<JSObject>(CurrentContext()->extension());
8800 extension->Print();
8801 break;
8802 }
8803
ager@chromium.orga1645e22009-09-09 19:27:10 +00008804 case ScopeIterator::ScopeTypeCatch: {
8805 PrintF("Catch:\n");
8806 Handle<JSObject> extension =
8807 Handle<JSObject>(CurrentContext()->extension());
8808 extension->Print();
8809 break;
8810 }
8811
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008812 case ScopeIterator::ScopeTypeClosure: {
8813 PrintF("Closure:\n");
8814 CurrentContext()->Print();
8815 if (CurrentContext()->has_extension()) {
8816 Handle<JSObject> extension =
8817 Handle<JSObject>(CurrentContext()->extension());
8818 if (extension->IsJSContextExtensionObject()) {
8819 extension->Print();
8820 }
8821 }
8822 break;
8823 }
8824
8825 default:
8826 UNREACHABLE();
8827 }
8828 PrintF("\n");
8829 }
8830#endif
8831
8832 private:
8833 JavaScriptFrame* frame_;
8834 Handle<JSFunction> function_;
8835 Handle<Context> context_;
8836 bool local_done_;
8837 bool at_local_;
8838
8839 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
8840};
8841
8842
8843static Object* Runtime_GetScopeCount(Arguments args) {
8844 HandleScope scope;
8845 ASSERT(args.length() == 2);
8846
8847 // Check arguments.
8848 Object* check = Runtime_CheckExecutionState(args);
8849 if (check->IsFailure()) return check;
8850 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8851
8852 // Get the frame where the debugging is performed.
8853 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8854 JavaScriptFrameIterator it(id);
8855 JavaScriptFrame* frame = it.frame();
8856
8857 // Count the visible scopes.
8858 int n = 0;
8859 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8860 n++;
8861 }
8862
8863 return Smi::FromInt(n);
8864}
8865
8866
8867static const int kScopeDetailsTypeIndex = 0;
8868static const int kScopeDetailsObjectIndex = 1;
8869static const int kScopeDetailsSize = 2;
8870
8871// Return an array with scope details
8872// args[0]: number: break id
8873// args[1]: number: frame index
8874// args[2]: number: scope index
8875//
8876// The array returned contains the following information:
8877// 0: Scope type
8878// 1: Scope object
8879static Object* Runtime_GetScopeDetails(Arguments args) {
8880 HandleScope scope;
8881 ASSERT(args.length() == 3);
8882
8883 // Check arguments.
8884 Object* check = Runtime_CheckExecutionState(args);
8885 if (check->IsFailure()) return check;
8886 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8887 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
8888
8889 // Get the frame where the debugging is performed.
8890 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8891 JavaScriptFrameIterator frame_it(id);
8892 JavaScriptFrame* frame = frame_it.frame();
8893
8894 // Find the requested scope.
8895 int n = 0;
8896 ScopeIterator it(frame);
8897 for (; !it.Done() && n < index; it.Next()) {
8898 n++;
8899 }
8900 if (it.Done()) {
8901 return Heap::undefined_value();
8902 }
8903
8904 // Calculate the size of the result.
8905 int details_size = kScopeDetailsSize;
8906 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8907
8908 // Fill in scope details.
8909 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
8910 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
8911
8912 return *Factory::NewJSArrayWithElements(details);
8913}
8914
8915
8916static Object* Runtime_DebugPrintScopes(Arguments args) {
8917 HandleScope scope;
8918 ASSERT(args.length() == 0);
8919
8920#ifdef DEBUG
8921 // Print the scopes for the top frame.
8922 StackFrameLocator locator;
8923 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
8924 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8925 it.DebugPrint();
8926 }
8927#endif
8928 return Heap::undefined_value();
8929}
8930
8931
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008932static Object* Runtime_GetCFrames(Arguments args) {
8933 HandleScope scope;
8934 ASSERT(args.length() == 1);
8935 Object* result = Runtime_CheckExecutionState(args);
8936 if (result->IsFailure()) return result;
8937
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008938#if V8_HOST_ARCH_64_BIT
8939 UNIMPLEMENTED();
8940 return Heap::undefined_value();
8941#else
8942
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008943 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008944 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
8945 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008946 if (frames_count == OS::kStackWalkError) {
8947 return Heap::undefined_value();
8948 }
8949
8950 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
8951 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
8952 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
8953 for (int i = 0; i < frames_count; i++) {
8954 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
8955 frame_value->SetProperty(
8956 *address_str,
8957 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
8958 NONE);
8959
8960 // Get the stack walk text for this frame.
8961 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008962 int frame_text_length = StrLength(frames[i].text);
8963 if (frame_text_length > 0) {
8964 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008965 frame_text = Factory::NewStringFromAscii(str);
8966 }
8967
8968 if (!frame_text.is_null()) {
8969 frame_value->SetProperty(*text_str, *frame_text, NONE);
8970 }
8971
8972 frames_array->set(i, *frame_value);
8973 }
8974 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008975#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008976}
8977
8978
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00008979static Object* Runtime_GetThreadCount(Arguments args) {
8980 HandleScope scope;
8981 ASSERT(args.length() == 1);
8982
8983 // Check arguments.
8984 Object* result = Runtime_CheckExecutionState(args);
8985 if (result->IsFailure()) return result;
8986
8987 // Count all archived V8 threads.
8988 int n = 0;
8989 for (ThreadState* thread = ThreadState::FirstInUse();
8990 thread != NULL;
8991 thread = thread->Next()) {
8992 n++;
8993 }
8994
8995 // Total number of threads is current thread and archived threads.
8996 return Smi::FromInt(n + 1);
8997}
8998
8999
9000static const int kThreadDetailsCurrentThreadIndex = 0;
9001static const int kThreadDetailsThreadIdIndex = 1;
9002static const int kThreadDetailsSize = 2;
9003
9004// Return an array with thread details
9005// args[0]: number: break id
9006// args[1]: number: thread index
9007//
9008// The array returned contains the following information:
9009// 0: Is current thread?
9010// 1: Thread id
9011static Object* Runtime_GetThreadDetails(Arguments args) {
9012 HandleScope scope;
9013 ASSERT(args.length() == 2);
9014
9015 // Check arguments.
9016 Object* check = Runtime_CheckExecutionState(args);
9017 if (check->IsFailure()) return check;
9018 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
9019
9020 // Allocate array for result.
9021 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
9022
9023 // Thread index 0 is current thread.
9024 if (index == 0) {
9025 // Fill the details.
9026 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
9027 details->set(kThreadDetailsThreadIdIndex,
9028 Smi::FromInt(ThreadManager::CurrentId()));
9029 } else {
9030 // Find the thread with the requested index.
9031 int n = 1;
9032 ThreadState* thread = ThreadState::FirstInUse();
9033 while (index != n && thread != NULL) {
9034 thread = thread->Next();
9035 n++;
9036 }
9037 if (thread == NULL) {
9038 return Heap::undefined_value();
9039 }
9040
9041 // Fill the details.
9042 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
9043 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
9044 }
9045
9046 // Convert to JS array and return.
9047 return *Factory::NewJSArrayWithElements(details);
9048}
9049
9050
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009051static Object* Runtime_GetBreakLocations(Arguments args) {
9052 HandleScope scope;
9053 ASSERT(args.length() == 1);
9054
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009055 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9056 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009057 // Find the number of break points
9058 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
9059 if (break_locations->IsUndefined()) return Heap::undefined_value();
9060 // Return array as JS array
9061 return *Factory::NewJSArrayWithElements(
9062 Handle<FixedArray>::cast(break_locations));
9063}
9064
9065
9066// Set a break point in a function
9067// args[0]: function
9068// args[1]: number: break source position (within the function source)
9069// args[2]: number: break point object
9070static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
9071 HandleScope scope;
9072 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009073 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9074 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009075 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9076 RUNTIME_ASSERT(source_position >= 0);
9077 Handle<Object> break_point_object_arg = args.at<Object>(2);
9078
9079 // Set break point.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009080 Debug::SetBreakPoint(shared, break_point_object_arg, &source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009081
lrn@chromium.org32d961d2010-06-30 09:09:34 +00009082 return Smi::FromInt(source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009083}
9084
9085
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009086Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
9087 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009088 // Iterate the heap looking for SharedFunctionInfo generated from the
9089 // script. The inner most SharedFunctionInfo containing the source position
9090 // for the requested break point is found.
9091 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
9092 // which is found is not compiled it is compiled and the heap is iterated
9093 // again as the compilation might create inner functions from the newly
9094 // compiled function and the actual requested break point might be in one of
9095 // these functions.
9096 bool done = false;
9097 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00009098 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009099 Handle<SharedFunctionInfo> target;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009100 while (!done) {
9101 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009102 for (HeapObject* obj = iterator.next();
9103 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009104 if (obj->IsSharedFunctionInfo()) {
9105 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
9106 if (shared->script() == *script) {
9107 // If the SharedFunctionInfo found has the requested script data and
9108 // contains the source position it is a candidate.
9109 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00009110 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009111 start_position = shared->start_position();
9112 }
9113 if (start_position <= position &&
9114 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00009115 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009116 // candidate this is the new candidate.
9117 if (target.is_null()) {
9118 target_start_position = start_position;
9119 target = shared;
9120 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00009121 if (target_start_position == start_position &&
9122 shared->end_position() == target->end_position()) {
9123 // If a top-level function contain only one function
9124 // declartion the source for the top-level and the function is
9125 // the same. In that case prefer the non top-level function.
9126 if (!shared->is_toplevel()) {
9127 target_start_position = start_position;
9128 target = shared;
9129 }
9130 } else if (target_start_position <= start_position &&
9131 shared->end_position() <= target->end_position()) {
9132 // This containment check includes equality as a function inside
9133 // a top-level function can share either start or end position
9134 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009135 target_start_position = start_position;
9136 target = shared;
9137 }
9138 }
9139 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009140 }
9141 }
9142 }
9143
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009144 if (target.is_null()) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009145 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009146 }
9147
9148 // If the candidate found is compiled we are done. NOTE: when lazy
9149 // compilation of inner functions is introduced some additional checking
9150 // needs to be done here to compile inner functions.
9151 done = target->is_compiled();
9152 if (!done) {
9153 // If the candidate is not compiled compile it to reveal any inner
9154 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009155 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009156 }
9157 }
9158
9159 return *target;
9160}
9161
9162
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009163// Changes the state of a break point in a script and returns source position
9164// where break point was set. NOTE: Regarding performance see the NOTE for
9165// GetScriptFromScriptData.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009166// args[0]: script to set break point in
9167// args[1]: number: break source position (within the script source)
9168// args[2]: number: break point object
9169static Object* Runtime_SetScriptBreakPoint(Arguments args) {
9170 HandleScope scope;
9171 ASSERT(args.length() == 3);
9172 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
9173 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9174 RUNTIME_ASSERT(source_position >= 0);
9175 Handle<Object> break_point_object_arg = args.at<Object>(2);
9176
9177 // Get the script from the script wrapper.
9178 RUNTIME_ASSERT(wrapper->value()->IsScript());
9179 Handle<Script> script(Script::cast(wrapper->value()));
9180
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009181 Object* result = Runtime::FindSharedFunctionInfoInScript(
9182 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009183 if (!result->IsUndefined()) {
9184 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
9185 // Find position within function. The script position might be before the
9186 // source position of the first function.
9187 int position;
9188 if (shared->start_position() > source_position) {
9189 position = 0;
9190 } else {
9191 position = source_position - shared->start_position();
9192 }
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009193 Debug::SetBreakPoint(shared, break_point_object_arg, &position);
9194 position += shared->start_position();
9195 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009196 }
9197 return Heap::undefined_value();
9198}
9199
9200
9201// Clear a break point
9202// args[0]: number: break point object
9203static Object* Runtime_ClearBreakPoint(Arguments args) {
9204 HandleScope scope;
9205 ASSERT(args.length() == 1);
9206 Handle<Object> break_point_object_arg = args.at<Object>(0);
9207
9208 // Clear break point.
9209 Debug::ClearBreakPoint(break_point_object_arg);
9210
9211 return Heap::undefined_value();
9212}
9213
9214
9215// Change the state of break on exceptions
9216// args[0]: boolean indicating uncaught exceptions
9217// args[1]: boolean indicating on/off
9218static Object* Runtime_ChangeBreakOnException(Arguments args) {
9219 HandleScope scope;
9220 ASSERT(args.length() == 2);
9221 ASSERT(args[0]->IsNumber());
9222 ASSERT(args[1]->IsBoolean());
9223
9224 // Update break point state
9225 ExceptionBreakType type =
9226 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
9227 bool enable = args[1]->ToBoolean()->IsTrue();
9228 Debug::ChangeBreakOnException(type, enable);
9229 return Heap::undefined_value();
9230}
9231
9232
9233// Prepare for stepping
9234// args[0]: break id for checking execution state
9235// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00009236// args[2]: number of times to perform the step, for step out it is the number
9237// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009238static Object* Runtime_PrepareStep(Arguments args) {
9239 HandleScope scope;
9240 ASSERT(args.length() == 3);
9241 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009242 Object* check = Runtime_CheckExecutionState(args);
9243 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009244 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
9245 return Top::Throw(Heap::illegal_argument_symbol());
9246 }
9247
9248 // Get the step action and check validity.
9249 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
9250 if (step_action != StepIn &&
9251 step_action != StepNext &&
9252 step_action != StepOut &&
9253 step_action != StepInMin &&
9254 step_action != StepMin) {
9255 return Top::Throw(Heap::illegal_argument_symbol());
9256 }
9257
9258 // Get the number of steps.
9259 int step_count = NumberToInt32(args[2]);
9260 if (step_count < 1) {
9261 return Top::Throw(Heap::illegal_argument_symbol());
9262 }
9263
ager@chromium.orga1645e22009-09-09 19:27:10 +00009264 // Clear all current stepping setup.
9265 Debug::ClearStepping();
9266
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009267 // Prepare step.
9268 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
9269 return Heap::undefined_value();
9270}
9271
9272
9273// Clear all stepping set by PrepareStep.
9274static Object* Runtime_ClearStepping(Arguments args) {
9275 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009276 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009277 Debug::ClearStepping();
9278 return Heap::undefined_value();
9279}
9280
9281
9282// Creates a copy of the with context chain. The copy of the context chain is
9283// is linked to the function context supplied.
9284static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
9285 Handle<Context> function_context) {
9286 // At the bottom of the chain. Return the function context to link to.
9287 if (context_chain->is_function_context()) {
9288 return function_context;
9289 }
9290
9291 // Recursively copy the with contexts.
9292 Handle<Context> previous(context_chain->previous());
9293 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
9294 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009295 CopyWithContextChain(function_context, previous),
9296 extension,
9297 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009298}
9299
9300
9301// Helper function to find or create the arguments object for
9302// Runtime_DebugEvaluate.
9303static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
9304 Handle<JSFunction> function,
9305 Handle<Code> code,
9306 const ScopeInfo<>* sinfo,
9307 Handle<Context> function_context) {
9308 // Try to find the value of 'arguments' to pass as parameter. If it is not
9309 // found (that is the debugged function does not reference 'arguments' and
9310 // does not support eval) then create an 'arguments' object.
9311 int index;
9312 if (sinfo->number_of_stack_slots() > 0) {
9313 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
9314 if (index != -1) {
9315 return Handle<Object>(frame->GetExpression(index));
9316 }
9317 }
9318
9319 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
9320 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
9321 NULL);
9322 if (index != -1) {
9323 return Handle<Object>(function_context->get(index));
9324 }
9325 }
9326
9327 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009328 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
9329 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009330
9331 AssertNoAllocation no_gc;
9332 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009333 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009334 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009335 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009336 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009337 return arguments;
9338}
9339
9340
9341// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00009342// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009343// extension part has all the parameters and locals of the function on the
9344// stack frame. A function which calls eval with the code to evaluate is then
9345// compiled in this context and called in this context. As this context
9346// replaces the context of the function on the stack frame a new (empty)
9347// function is created as well to be used as the closure for the context.
9348// This function and the context acts as replacements for the function on the
9349// stack frame presenting the same view of the values of parameters and
9350// local variables as if the piece of JavaScript was evaluated at the point
9351// where the function on the stack frame is currently stopped.
9352static Object* Runtime_DebugEvaluate(Arguments args) {
9353 HandleScope scope;
9354
9355 // Check the execution state and decode arguments frame and source to be
9356 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009357 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009358 Object* check_result = Runtime_CheckExecutionState(args);
9359 if (check_result->IsFailure()) return check_result;
9360 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9361 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009362 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
9363
9364 // Handle the processing of break.
9365 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009366
9367 // Get the frame where the debugging is performed.
9368 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9369 JavaScriptFrameIterator it(id);
9370 JavaScriptFrame* frame = it.frame();
9371 Handle<JSFunction> function(JSFunction::cast(frame->function()));
9372 Handle<Code> code(function->code());
9373 ScopeInfo<> sinfo(*code);
9374
9375 // Traverse the saved contexts chain to find the active context for the
9376 // selected frame.
9377 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009378 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009379 save = save->prev();
9380 }
9381 ASSERT(save != NULL);
9382 SaveContext savex;
9383 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009384
9385 // Create the (empty) function replacing the function on the stack frame for
9386 // the purpose of evaluating in the context created below. It is important
9387 // that this function does not describe any parameters and local variables
9388 // in the context. If it does then this will cause problems with the lookup
9389 // in Context::Lookup, where context slots for parameters and local variables
9390 // are looked at before the extension object.
9391 Handle<JSFunction> go_between =
9392 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
9393 go_between->set_context(function->context());
9394#ifdef DEBUG
9395 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
9396 ASSERT(go_between_sinfo.number_of_parameters() == 0);
9397 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
9398#endif
9399
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009400 // Materialize the content of the local scope into a JSObject.
9401 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009402
9403 // Allocate a new context for the debug evaluation and set the extension
9404 // object build.
9405 Handle<Context> context =
9406 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009407 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009408 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009409 Handle<Context> frame_context(Context::cast(frame->context()));
9410 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009411 context = CopyWithContextChain(frame_context, context);
9412
9413 // Wrap the evaluation statement in a new function compiled in the newly
9414 // created context. The function has one parameter which has to be called
9415 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00009416 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009417 // function(arguments,__source__) {return eval(__source__);}
9418 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00009419 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009420 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009421 Handle<String> function_source =
9422 Factory::NewStringFromAscii(Vector<const char>(source_str,
9423 source_str_length));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009424 Handle<SharedFunctionInfo> shared =
ager@chromium.org381abbb2009-02-25 13:23:22 +00009425 Compiler::CompileEval(function_source,
9426 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00009427 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00009428 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009429 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009430 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009431 Factory::NewFunctionFromSharedFunctionInfo(shared, context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009432
9433 // Invoke the result of the compilation to get the evaluation function.
9434 bool has_pending_exception;
9435 Handle<Object> receiver(frame->receiver());
9436 Handle<Object> evaluation_function =
9437 Execution::Call(compiled_function, receiver, 0, NULL,
9438 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009439 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009440
9441 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
9442 function_context);
9443
9444 // Invoke the evaluation function and return the result.
9445 const int argc = 2;
9446 Object** argv[argc] = { arguments.location(),
9447 Handle<Object>::cast(source).location() };
9448 Handle<Object> result =
9449 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
9450 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009451 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009452
9453 // Skip the global proxy as it has no properties and always delegates to the
9454 // real global object.
9455 if (result->IsJSGlobalProxy()) {
9456 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
9457 }
9458
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009459 return *result;
9460}
9461
9462
9463static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
9464 HandleScope scope;
9465
9466 // Check the execution state and decode arguments frame and source to be
9467 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009468 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009469 Object* check_result = Runtime_CheckExecutionState(args);
9470 if (check_result->IsFailure()) return check_result;
9471 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009472 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
9473
9474 // Handle the processing of break.
9475 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009476
9477 // Enter the top context from before the debugger was invoked.
9478 SaveContext save;
9479 SaveContext* top = &save;
9480 while (top != NULL && *top->context() == *Debug::debug_context()) {
9481 top = top->prev();
9482 }
9483 if (top != NULL) {
9484 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009485 }
9486
9487 // Get the global context now set to the top context from before the
9488 // debugger was invoked.
9489 Handle<Context> context = Top::global_context();
9490
9491 // Compile the source to be evaluated.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009492 Handle<SharedFunctionInfo> shared =
9493 Compiler::CompileEval(source,
9494 context,
9495 true,
9496 Compiler::DONT_VALIDATE_JSON);
9497 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009498 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009499 Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
9500 context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009501
9502 // Invoke the result of the compilation to get the evaluation function.
9503 bool has_pending_exception;
9504 Handle<Object> receiver = Top::global();
9505 Handle<Object> result =
9506 Execution::Call(compiled_function, receiver, 0, NULL,
9507 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009508 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009509 return *result;
9510}
9511
9512
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009513static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
9514 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009515 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009516
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009517 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009518 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009519
9520 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009521 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00009522 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
9523 // Get the script wrapper in a local handle before calling GetScriptWrapper,
9524 // because using
9525 // instances->set(i, *GetScriptWrapper(script))
9526 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
9527 // already have deferenced the instances handle.
9528 Handle<JSValue> wrapper = GetScriptWrapper(script);
9529 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009530 }
9531
9532 // Return result as a JS array.
9533 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
9534 Handle<JSArray>::cast(result)->SetContent(*instances);
9535 return *result;
9536}
9537
9538
9539// Helper function used by Runtime_DebugReferencedBy below.
9540static int DebugReferencedBy(JSObject* target,
9541 Object* instance_filter, int max_references,
9542 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009543 JSFunction* arguments_function) {
9544 NoHandleAllocation ha;
9545 AssertNoAllocation no_alloc;
9546
9547 // Iterate the heap.
9548 int count = 0;
9549 JSObject* last = NULL;
9550 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009551 HeapObject* heap_obj = NULL;
9552 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009553 (max_references == 0 || count < max_references)) {
9554 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009555 if (heap_obj->IsJSObject()) {
9556 // Skip context extension objects and argument arrays as these are
9557 // checked in the context of functions using them.
9558 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00009559 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009560 obj->map()->constructor() == arguments_function) {
9561 continue;
9562 }
9563
9564 // Check if the JS object has a reference to the object looked for.
9565 if (obj->ReferencesObject(target)) {
9566 // Check instance filter if supplied. This is normally used to avoid
9567 // references from mirror objects (see Runtime_IsInPrototypeChain).
9568 if (!instance_filter->IsUndefined()) {
9569 Object* V = obj;
9570 while (true) {
9571 Object* prototype = V->GetPrototype();
9572 if (prototype->IsNull()) {
9573 break;
9574 }
9575 if (instance_filter == prototype) {
9576 obj = NULL; // Don't add this object.
9577 break;
9578 }
9579 V = prototype;
9580 }
9581 }
9582
9583 if (obj != NULL) {
9584 // Valid reference found add to instance array if supplied an update
9585 // count.
9586 if (instances != NULL && count < instances_size) {
9587 instances->set(count, obj);
9588 }
9589 last = obj;
9590 count++;
9591 }
9592 }
9593 }
9594 }
9595
9596 // Check for circular reference only. This can happen when the object is only
9597 // referenced from mirrors and has a circular reference in which case the
9598 // object is not really alive and would have been garbage collected if not
9599 // referenced from the mirror.
9600 if (count == 1 && last == target) {
9601 count = 0;
9602 }
9603
9604 // Return the number of referencing objects found.
9605 return count;
9606}
9607
9608
9609// Scan the heap for objects with direct references to an object
9610// args[0]: the object to find references to
9611// args[1]: constructor function for instances to exclude (Mirror)
9612// args[2]: the the maximum number of objects to return
9613static Object* Runtime_DebugReferencedBy(Arguments args) {
9614 ASSERT(args.length() == 3);
9615
9616 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009617 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009618
9619 // Check parameters.
9620 CONVERT_CHECKED(JSObject, target, args[0]);
9621 Object* instance_filter = args[1];
9622 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
9623 instance_filter->IsJSObject());
9624 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
9625 RUNTIME_ASSERT(max_references >= 0);
9626
9627 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009628 JSObject* arguments_boilerplate =
9629 Top::context()->global_context()->arguments_boilerplate();
9630 JSFunction* arguments_function =
9631 JSFunction::cast(arguments_boilerplate->map()->constructor());
9632
9633 // Get the number of referencing objects.
9634 int count;
9635 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009636 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009637
9638 // Allocate an array to hold the result.
9639 Object* object = Heap::AllocateFixedArray(count);
9640 if (object->IsFailure()) return object;
9641 FixedArray* instances = FixedArray::cast(object);
9642
9643 // Fill the referencing objects.
9644 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009645 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009646
9647 // Return result as JS array.
9648 Object* result =
9649 Heap::AllocateJSObject(
9650 Top::context()->global_context()->array_function());
9651 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9652 return result;
9653}
9654
9655
9656// Helper function used by Runtime_DebugConstructedBy below.
9657static int DebugConstructedBy(JSFunction* constructor, int max_references,
9658 FixedArray* instances, int instances_size) {
9659 AssertNoAllocation no_alloc;
9660
9661 // Iterate the heap.
9662 int count = 0;
9663 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009664 HeapObject* heap_obj = NULL;
9665 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009666 (max_references == 0 || count < max_references)) {
9667 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009668 if (heap_obj->IsJSObject()) {
9669 JSObject* obj = JSObject::cast(heap_obj);
9670 if (obj->map()->constructor() == constructor) {
9671 // Valid reference found add to instance array if supplied an update
9672 // count.
9673 if (instances != NULL && count < instances_size) {
9674 instances->set(count, obj);
9675 }
9676 count++;
9677 }
9678 }
9679 }
9680
9681 // Return the number of referencing objects found.
9682 return count;
9683}
9684
9685
9686// Scan the heap for objects constructed by a specific function.
9687// args[0]: the constructor to find instances of
9688// args[1]: the the maximum number of objects to return
9689static Object* Runtime_DebugConstructedBy(Arguments args) {
9690 ASSERT(args.length() == 2);
9691
9692 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009693 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009694
9695 // Check parameters.
9696 CONVERT_CHECKED(JSFunction, constructor, args[0]);
9697 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
9698 RUNTIME_ASSERT(max_references >= 0);
9699
9700 // Get the number of referencing objects.
9701 int count;
9702 count = DebugConstructedBy(constructor, max_references, NULL, 0);
9703
9704 // Allocate an array to hold the result.
9705 Object* object = Heap::AllocateFixedArray(count);
9706 if (object->IsFailure()) return object;
9707 FixedArray* instances = FixedArray::cast(object);
9708
9709 // Fill the referencing objects.
9710 count = DebugConstructedBy(constructor, max_references, instances, count);
9711
9712 // Return result as JS array.
9713 Object* result =
9714 Heap::AllocateJSObject(
9715 Top::context()->global_context()->array_function());
9716 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9717 return result;
9718}
9719
9720
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009721// Find the effective prototype object as returned by __proto__.
9722// args[0]: the object to find the prototype for.
9723static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009724 ASSERT(args.length() == 1);
9725
9726 CONVERT_CHECKED(JSObject, obj, args[0]);
9727
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009728 // Use the __proto__ accessor.
9729 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009730}
9731
9732
9733static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009734 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009735 CPU::DebugBreak();
9736 return Heap::undefined_value();
9737}
9738
9739
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009740static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009741#ifdef DEBUG
9742 HandleScope scope;
9743 ASSERT(args.length() == 1);
9744 // Get the function and make sure it is compiled.
9745 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009746 Handle<SharedFunctionInfo> shared(func->shared());
9747 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009748 return Failure::Exception();
9749 }
9750 func->code()->PrintLn();
9751#endif // DEBUG
9752 return Heap::undefined_value();
9753}
ager@chromium.org9085a012009-05-11 19:22:57 +00009754
9755
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009756static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
9757#ifdef DEBUG
9758 HandleScope scope;
9759 ASSERT(args.length() == 1);
9760 // Get the function and make sure it is compiled.
9761 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009762 Handle<SharedFunctionInfo> shared(func->shared());
9763 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009764 return Failure::Exception();
9765 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009766 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009767#endif // DEBUG
9768 return Heap::undefined_value();
9769}
9770
9771
ager@chromium.org9085a012009-05-11 19:22:57 +00009772static Object* Runtime_FunctionGetInferredName(Arguments args) {
9773 NoHandleAllocation ha;
9774 ASSERT(args.length() == 1);
9775
9776 CONVERT_CHECKED(JSFunction, f, args[0]);
9777 return f->shared()->inferred_name();
9778}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009779
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009780
9781static int FindSharedFunctionInfosForScript(Script* script,
9782 FixedArray* buffer) {
9783 AssertNoAllocation no_allocations;
9784
9785 int counter = 0;
9786 int buffer_size = buffer->length();
9787 HeapIterator iterator;
9788 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
9789 ASSERT(obj != NULL);
9790 if (!obj->IsSharedFunctionInfo()) {
9791 continue;
9792 }
9793 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
9794 if (shared->script() != script) {
9795 continue;
9796 }
9797 if (counter < buffer_size) {
9798 buffer->set(counter, shared);
9799 }
9800 counter++;
9801 }
9802 return counter;
9803}
9804
9805// For a script finds all SharedFunctionInfo's in the heap that points
9806// to this script. Returns JSArray of SharedFunctionInfo wrapped
9807// in OpaqueReferences.
9808static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
9809 Arguments args) {
9810 ASSERT(args.length() == 1);
9811 HandleScope scope;
9812 CONVERT_CHECKED(JSValue, script_value, args[0]);
9813
9814 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
9815
9816 const int kBufferSize = 32;
9817
9818 Handle<FixedArray> array;
9819 array = Factory::NewFixedArray(kBufferSize);
9820 int number = FindSharedFunctionInfosForScript(*script, *array);
9821 if (number > kBufferSize) {
9822 array = Factory::NewFixedArray(number);
9823 FindSharedFunctionInfosForScript(*script, *array);
9824 }
9825
9826 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
9827 result->set_length(Smi::FromInt(number));
9828
9829 LiveEdit::WrapSharedFunctionInfos(result);
9830
9831 return *result;
9832}
9833
9834// For a script calculates compilation information about all its functions.
9835// The script source is explicitly specified by the second argument.
9836// The source of the actual script is not used, however it is important that
9837// all generated code keeps references to this particular instance of script.
9838// Returns a JSArray of compilation infos. The array is ordered so that
9839// each function with all its descendant is always stored in a continues range
9840// with the function itself going first. The root function is a script function.
9841static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
9842 ASSERT(args.length() == 2);
9843 HandleScope scope;
9844 CONVERT_CHECKED(JSValue, script, args[0]);
9845 CONVERT_ARG_CHECKED(String, source, 1);
9846 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
9847
9848 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
9849
9850 if (Top::has_pending_exception()) {
9851 return Failure::Exception();
9852 }
9853
9854 return result;
9855}
9856
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009857// Changes the source of the script to a new_source.
9858// If old_script_name is provided (i.e. is a String), also creates a copy of
9859// the script with its original source and sends notification to debugger.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009860static Object* Runtime_LiveEditReplaceScript(Arguments args) {
9861 ASSERT(args.length() == 3);
9862 HandleScope scope;
9863 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
9864 CONVERT_ARG_CHECKED(String, new_source, 1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009865 Handle<Object> old_script_name(args[2]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009866
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009867 CONVERT_CHECKED(Script, original_script_pointer,
9868 original_script_value->value());
9869 Handle<Script> original_script(original_script_pointer);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009870
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009871 Object* old_script = LiveEdit::ChangeScriptSource(original_script,
9872 new_source,
9873 old_script_name);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009874
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009875 if (old_script->IsScript()) {
9876 Handle<Script> script_handle(Script::cast(old_script));
9877 return *(GetScriptWrapper(script_handle));
9878 } else {
9879 return Heap::null_value();
9880 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009881}
9882
9883// Replaces code of SharedFunctionInfo with a new one.
9884static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
9885 ASSERT(args.length() == 2);
9886 HandleScope scope;
9887 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
9888 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
9889
ager@chromium.orgac091b72010-05-05 07:34:42 +00009890 return LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009891}
9892
9893// Connects SharedFunctionInfo to another script.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009894static Object* Runtime_LiveEditFunctionSetScript(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009895 ASSERT(args.length() == 2);
9896 HandleScope scope;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009897 Handle<Object> function_object(args[0]);
9898 Handle<Object> script_object(args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009899
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009900 if (function_object->IsJSValue()) {
9901 Handle<JSValue> function_wrapper = Handle<JSValue>::cast(function_object);
9902 if (script_object->IsJSValue()) {
9903 CONVERT_CHECKED(Script, script, JSValue::cast(*script_object)->value());
9904 script_object = Handle<Object>(script);
9905 }
9906
9907 LiveEdit::SetFunctionScript(function_wrapper, script_object);
9908 } else {
9909 // Just ignore this. We may not have a SharedFunctionInfo for some functions
9910 // and we check it in this function.
9911 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009912
9913 return Heap::undefined_value();
9914}
9915
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009916
9917// In a code of a parent function replaces original function as embedded object
9918// with a substitution one.
9919static Object* Runtime_LiveEditReplaceRefToNestedFunction(Arguments args) {
9920 ASSERT(args.length() == 3);
9921 HandleScope scope;
9922
9923 CONVERT_ARG_CHECKED(JSValue, parent_wrapper, 0);
9924 CONVERT_ARG_CHECKED(JSValue, orig_wrapper, 1);
9925 CONVERT_ARG_CHECKED(JSValue, subst_wrapper, 2);
9926
9927 LiveEdit::ReplaceRefToNestedFunction(parent_wrapper, orig_wrapper,
9928 subst_wrapper);
9929
9930 return Heap::undefined_value();
9931}
9932
9933
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009934// Updates positions of a shared function info (first parameter) according
9935// to script source change. Text change is described in second parameter as
9936// array of groups of 3 numbers:
9937// (change_begin, change_end, change_end_new_position).
9938// Each group describes a change in text; groups are sorted by change_begin.
9939static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
9940 ASSERT(args.length() == 2);
9941 HandleScope scope;
9942 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
9943 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
9944
ager@chromium.orgac091b72010-05-05 07:34:42 +00009945 return LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009946}
9947
9948
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009949// For array of SharedFunctionInfo's (each wrapped in JSValue)
9950// checks that none of them have activations on stacks (of any thread).
9951// Returns array of the same length with corresponding results of
9952// LiveEdit::FunctionPatchabilityStatus type.
ager@chromium.org357bf652010-04-12 11:30:10 +00009953static Object* Runtime_LiveEditCheckAndDropActivations(Arguments args) {
9954 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009955 HandleScope scope;
9956 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +00009957 CONVERT_BOOLEAN_CHECKED(do_drop, args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009958
ager@chromium.org357bf652010-04-12 11:30:10 +00009959 return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009960}
9961
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009962// Compares 2 strings line-by-line and returns diff in form of JSArray of
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00009963// triplets (pos1, pos1_end, pos2_end) describing list of diff chunks.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009964static Object* Runtime_LiveEditCompareStringsLinewise(Arguments args) {
9965 ASSERT(args.length() == 2);
9966 HandleScope scope;
9967 CONVERT_ARG_CHECKED(String, s1, 0);
9968 CONVERT_ARG_CHECKED(String, s2, 1);
9969
9970 return *LiveEdit::CompareStringsLinewise(s1, s2);
9971}
9972
9973
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009974
fschneider@chromium.org086aac62010-03-17 13:18:24 +00009975// A testing entry. Returns statement position which is the closest to
9976// source_position.
9977static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
9978 ASSERT(args.length() == 2);
9979 HandleScope scope;
9980 CONVERT_ARG_CHECKED(JSFunction, function, 0);
9981 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9982
9983 Handle<Code> code(function->code());
9984
9985 RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
9986 int closest_pc = 0;
9987 int distance = kMaxInt;
9988 while (!it.done()) {
9989 int statement_position = static_cast<int>(it.rinfo()->data());
9990 // Check if this break point is closer that what was previously found.
9991 if (source_position <= statement_position &&
9992 statement_position - source_position < distance) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00009993 closest_pc =
9994 static_cast<int>(it.rinfo()->pc() - code->instruction_start());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00009995 distance = statement_position - source_position;
9996 // Check whether we can't get any closer.
9997 if (distance == 0) break;
9998 }
9999 it.next();
10000 }
10001
10002 return Smi::FromInt(closest_pc);
10003}
10004
10005
ager@chromium.org357bf652010-04-12 11:30:10 +000010006// Calls specified function with or without entering the debugger.
10007// This is used in unit tests to run code as if debugger is entered or simply
10008// to have a stack with C++ frame in the middle.
10009static Object* Runtime_ExecuteInDebugContext(Arguments args) {
10010 ASSERT(args.length() == 2);
10011 HandleScope scope;
10012 CONVERT_ARG_CHECKED(JSFunction, function, 0);
10013 CONVERT_BOOLEAN_CHECKED(without_debugger, args[1]);
10014
10015 Handle<Object> result;
10016 bool pending_exception;
10017 {
10018 if (without_debugger) {
10019 result = Execution::Call(function, Top::global(), 0, NULL,
10020 &pending_exception);
10021 } else {
10022 EnterDebugger enter_debugger;
10023 result = Execution::Call(function, Top::global(), 0, NULL,
10024 &pending_exception);
10025 }
10026 }
10027 if (!pending_exception) {
10028 return *result;
10029 } else {
10030 return Failure::Exception();
10031 }
10032}
10033
10034
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010035#endif // ENABLE_DEBUGGER_SUPPORT
10036
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010037#ifdef ENABLE_LOGGING_AND_PROFILING
10038
10039static Object* Runtime_ProfilerResume(Arguments args) {
10040 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010041 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010042
10043 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010044 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10045 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010046 return Heap::undefined_value();
10047}
10048
10049
10050static Object* Runtime_ProfilerPause(Arguments args) {
10051 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010052 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010053
10054 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010055 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10056 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010057 return Heap::undefined_value();
10058}
10059
10060#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010061
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010062// Finds the script object from the script data. NOTE: This operation uses
10063// heap traversal to find the function generated for the source position
10064// for the requested break point. For lazily compiled functions several heap
10065// traversals might be required rendering this operation as a rather slow
10066// operation. However for setting break points which is normally done through
10067// some kind of user interaction the performance is not crucial.
10068static Handle<Object> Runtime_GetScriptFromScriptName(
10069 Handle<String> script_name) {
10070 // Scan the heap for Script objects to find the script with the requested
10071 // script data.
10072 Handle<Script> script;
10073 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010074 HeapObject* obj = NULL;
10075 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010076 // If a script is found check if it has the script data requested.
10077 if (obj->IsScript()) {
10078 if (Script::cast(obj)->name()->IsString()) {
10079 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
10080 script = Handle<Script>(Script::cast(obj));
10081 }
10082 }
10083 }
10084 }
10085
10086 // If no script with the requested script data is found return undefined.
10087 if (script.is_null()) return Factory::undefined_value();
10088
10089 // Return the script found.
10090 return GetScriptWrapper(script);
10091}
10092
10093
10094// Get the script object from script data. NOTE: Regarding performance
10095// see the NOTE for GetScriptFromScriptData.
10096// args[0]: script data for the script to find the source for
10097static Object* Runtime_GetScript(Arguments args) {
10098 HandleScope scope;
10099
10100 ASSERT(args.length() == 1);
10101
10102 CONVERT_CHECKED(String, script_name, args[0]);
10103
10104 // Find the requested script.
10105 Handle<Object> result =
10106 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
10107 return *result;
10108}
10109
10110
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010111// Determines whether the given stack frame should be displayed in
10112// a stack trace. The caller is the error constructor that asked
10113// for the stack trace to be collected. The first time a construct
10114// call to this function is encountered it is skipped. The seen_caller
10115// in/out parameter is used to remember if the caller has been seen
10116// yet.
10117static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
10118 bool* seen_caller) {
10119 // Only display JS frames.
10120 if (!raw_frame->is_java_script())
10121 return false;
10122 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
10123 Object* raw_fun = frame->function();
10124 // Not sure when this can happen but skip it just in case.
10125 if (!raw_fun->IsJSFunction())
10126 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010127 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010128 *seen_caller = true;
10129 return false;
10130 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010131 // Skip all frames until we've seen the caller. Also, skip the most
10132 // obvious builtin calls. Some builtin calls (such as Number.ADD
10133 // which is invoked using 'call') are very difficult to recognize
10134 // so we're leaving them in for now.
10135 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010136}
10137
10138
10139// Collect the raw data for a stack trace. Returns an array of three
10140// element segments each containing a receiver, function and native
10141// code offset.
10142static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010143 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010144 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010145 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
10146
10147 HandleScope scope;
10148
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000010149 limit = Max(limit, 0); // Ensure that limit is not negative.
10150 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010151 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010152
10153 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010154 // If the caller parameter is a function we skip frames until we're
10155 // under it before starting to collect.
10156 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010157 int cursor = 0;
10158 int frames_seen = 0;
10159 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010160 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010161 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010162 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010163 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010164 Object* recv = frame->receiver();
10165 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010166 Address pc = frame->pc();
10167 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010168 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010169 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010170 if (cursor + 2 < elements->length()) {
10171 elements->set(cursor++, recv);
10172 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010173 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010174 } else {
10175 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010176 Handle<Object> recv_handle(recv);
10177 Handle<Object> fun_handle(fun);
10178 SetElement(result, cursor++, recv_handle);
10179 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010180 SetElement(result, cursor++, Handle<Smi>(offset));
10181 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010182 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010183 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010184 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010185
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010186 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010187 return *result;
10188}
10189
10190
ager@chromium.org3811b432009-10-28 14:53:37 +000010191// Returns V8 version as a string.
10192static Object* Runtime_GetV8Version(Arguments args) {
10193 ASSERT_EQ(args.length(), 0);
10194
10195 NoHandleAllocation ha;
10196
10197 const char* version_string = v8::V8::GetVersion();
10198
10199 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
10200}
10201
10202
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010203static Object* Runtime_Abort(Arguments args) {
10204 ASSERT(args.length() == 2);
10205 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
10206 Smi::cast(args[1])->value());
10207 Top::PrintStack();
10208 OS::Abort();
10209 UNREACHABLE();
10210 return NULL;
10211}
10212
10213
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010214static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
10215 ASSERT(args.length() == 0);
10216 HandleScope::DeleteExtensions();
10217 return Heap::undefined_value();
10218}
10219
10220
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010221static Object* CacheMiss(FixedArray* cache_obj, int index, Object* key_obj) {
10222 ASSERT(index % 2 == 0); // index of the key
10223 ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
10224 ASSERT(index < cache_obj->length());
10225
10226 HandleScope scope;
10227
10228 Handle<FixedArray> cache(cache_obj);
10229 Handle<Object> key(key_obj);
10230 Handle<JSFunction> factory(JSFunction::cast(
10231 cache->get(JSFunctionResultCache::kFactoryIndex)));
10232 // TODO(antonm): consider passing a receiver when constructing a cache.
10233 Handle<Object> receiver(Top::global_context()->global());
10234
10235 Handle<Object> value;
10236 {
10237 // This handle is nor shared, nor used later, so it's safe.
10238 Object** argv[] = { key.location() };
10239 bool pending_exception = false;
10240 value = Execution::Call(factory,
10241 receiver,
10242 1,
10243 argv,
10244 &pending_exception);
10245 if (pending_exception) return Failure::Exception();
10246 }
10247
10248 cache->set(index, *key);
10249 cache->set(index + 1, *value);
10250 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(index));
10251
10252 return *value;
10253}
10254
10255
10256static Object* Runtime_GetFromCache(Arguments args) {
10257 // This is only called from codegen, so checks might be more lax.
10258 CONVERT_CHECKED(FixedArray, cache, args[0]);
10259 Object* key = args[1];
10260
10261 const int finger_index =
10262 Smi::cast(cache->get(JSFunctionResultCache::kFingerIndex))->value();
10263
10264 Object* o = cache->get(finger_index);
10265 if (o == key) {
10266 // The fastest case: hit the same place again.
10267 return cache->get(finger_index + 1);
10268 }
10269
10270 for (int i = finger_index - 2;
10271 i >= JSFunctionResultCache::kEntriesIndex;
10272 i -= 2) {
10273 o = cache->get(i);
10274 if (o == key) {
10275 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10276 return cache->get(i + 1);
10277 }
10278 }
10279
10280 const int size =
10281 Smi::cast(cache->get(JSFunctionResultCache::kCacheSizeIndex))->value();
10282 ASSERT(size <= cache->length());
10283
10284 for (int i = size - 2; i > finger_index; i -= 2) {
10285 o = cache->get(i);
10286 if (o == key) {
10287 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10288 return cache->get(i + 1);
10289 }
10290 }
10291
10292 // Cache miss. If we have spare room, put new data into it, otherwise
10293 // evict post finger entry which must be least recently used.
10294 if (size < cache->length()) {
10295 cache->set(JSFunctionResultCache::kCacheSizeIndex, Smi::FromInt(size + 2));
10296 return CacheMiss(cache, size, key);
10297 } else {
antonm@chromium.org397e23c2010-04-21 12:00:05 +000010298 int target_index = finger_index + JSFunctionResultCache::kEntrySize;
10299 if (target_index == cache->length()) {
10300 target_index = JSFunctionResultCache::kEntriesIndex;
10301 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010302 return CacheMiss(cache, target_index, key);
10303 }
10304}
10305
kasper.lund44510672008-07-25 07:37:58 +000010306#ifdef DEBUG
10307// ListNatives is ONLY used by the fuzz-natives.js in debug mode
10308// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010309static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +000010310 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010311 HandleScope scope;
10312 Handle<JSArray> result = Factory::NewJSArray(0);
10313 int index = 0;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010314 bool inline_runtime_functions = false;
ager@chromium.orga1645e22009-09-09 19:27:10 +000010315#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010316 { \
10317 HandleScope inner; \
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010318 Handle<String> name; \
10319 /* Inline runtime functions have an underscore in front of the name. */ \
10320 if (inline_runtime_functions) { \
10321 name = Factory::NewStringFromAscii( \
10322 Vector<const char>("_" #Name, StrLength("_" #Name))); \
10323 } else { \
10324 name = Factory::NewStringFromAscii( \
10325 Vector<const char>(#Name, StrLength(#Name))); \
10326 } \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010327 Handle<JSArray> pair = Factory::NewJSArray(0); \
10328 SetElement(pair, 0, name); \
10329 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
10330 SetElement(result, index++, pair); \
10331 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010332 inline_runtime_functions = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010333 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010334 inline_runtime_functions = true;
10335 INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010336#undef ADD_ENTRY
10337 return *result;
10338}
kasper.lund44510672008-07-25 07:37:58 +000010339#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010340
10341
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010342static Object* Runtime_Log(Arguments args) {
10343 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +000010344 CONVERT_CHECKED(String, format, args[0]);
10345 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010346 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010347 Logger::LogRuntime(chars, elms);
10348 return Heap::undefined_value();
10349}
10350
10351
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010352static Object* Runtime_IS_VAR(Arguments args) {
10353 UNREACHABLE(); // implemented as macro in the parser
10354 return NULL;
10355}
10356
10357
10358// ----------------------------------------------------------------------------
10359// Implementation of Runtime
10360
ager@chromium.orga1645e22009-09-09 19:27:10 +000010361#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010362 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +000010363 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010364
10365static Runtime::Function Runtime_functions[] = {
10366 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010367 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010368};
10369
10370#undef F
10371
10372
10373Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
10374 ASSERT(0 <= fid && fid < kNofFunctions);
10375 return &Runtime_functions[fid];
10376}
10377
10378
10379Runtime::Function* Runtime::FunctionForName(const char* name) {
10380 for (Function* f = Runtime_functions; f->name != NULL; f++) {
10381 if (strcmp(f->name, name) == 0) {
10382 return f;
10383 }
10384 }
10385 return NULL;
10386}
10387
10388
10389void Runtime::PerformGC(Object* result) {
10390 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010391 if (failure->IsRetryAfterGC()) {
10392 // Try to do a garbage collection; ignore it if it fails. The C
10393 // entry stub will throw an out-of-memory exception in that case.
10394 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
10395 } else {
10396 // Handle last resort GC and make sure to allow future allocations
10397 // to grow the heap without causing GCs (if possible).
10398 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +000010399 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010400 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010401}
10402
10403
10404} } // namespace v8::internal