blob: a3eb09ffa8c564c13bb5e6dcf1eba24a6b8805ca [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
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +0000681static Object* Runtime_PreventExtensions(Arguments args) {
682 ASSERT(args.length() == 1);
683 CONVERT_CHECKED(JSObject, obj, args[0]);
684 return obj->PreventExtensions();
685}
686
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000687static Object* Runtime_IsExtensible(Arguments args) {
688 ASSERT(args.length() == 1);
689 CONVERT_CHECKED(JSObject, obj, args[0]);
690 return obj->map()->is_extensible() ? Heap::true_value()
691 : Heap::false_value();
692}
693
694
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000695static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000696 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000697 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000698 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
699 CONVERT_ARG_CHECKED(String, pattern, 1);
700 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000701 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
702 if (result.is_null()) return Failure::Exception();
703 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000704}
705
706
707static Object* Runtime_CreateApiFunction(Arguments args) {
708 HandleScope scope;
709 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000710 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000711 return *Factory::CreateApiFunction(data);
712}
713
714
715static Object* Runtime_IsTemplate(Arguments args) {
716 ASSERT(args.length() == 1);
717 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000718 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000719 return Heap::ToBoolean(result);
720}
721
722
723static Object* Runtime_GetTemplateField(Arguments args) {
724 ASSERT(args.length() == 2);
725 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000726 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000727 int index = field->value();
728 int offset = index * kPointerSize + HeapObject::kHeaderSize;
729 InstanceType type = templ->map()->instance_type();
730 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
731 type == OBJECT_TEMPLATE_INFO_TYPE);
732 RUNTIME_ASSERT(offset > 0);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000733 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000734 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
735 } else {
736 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
737 }
738 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000739}
740
741
ager@chromium.org870a0b62008-11-04 11:43:05 +0000742static Object* Runtime_DisableAccessChecks(Arguments args) {
743 ASSERT(args.length() == 1);
744 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000745 Map* old_map = object->map();
746 bool needs_access_checks = old_map->is_access_check_needed();
747 if (needs_access_checks) {
748 // Copy map so it won't interfere constructor's initial map.
749 Object* new_map = old_map->CopyDropTransitions();
750 if (new_map->IsFailure()) return new_map;
751
752 Map::cast(new_map)->set_is_access_check_needed(false);
753 object->set_map(Map::cast(new_map));
754 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000755 return needs_access_checks ? Heap::true_value() : Heap::false_value();
756}
757
758
759static Object* Runtime_EnableAccessChecks(Arguments args) {
760 ASSERT(args.length() == 1);
761 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000762 Map* old_map = object->map();
763 if (!old_map->is_access_check_needed()) {
764 // Copy map so it won't interfere constructor's initial map.
765 Object* new_map = old_map->CopyDropTransitions();
766 if (new_map->IsFailure()) return new_map;
767
768 Map::cast(new_map)->set_is_access_check_needed(true);
769 object->set_map(Map::cast(new_map));
770 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000771 return Heap::undefined_value();
772}
773
774
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000775static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
776 HandleScope scope;
777 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
778 Handle<Object> args[2] = { type_handle, name };
779 Handle<Object> error =
780 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
781 return Top::Throw(*error);
782}
783
784
785static Object* Runtime_DeclareGlobals(Arguments args) {
786 HandleScope scope;
787 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
788
ager@chromium.org3811b432009-10-28 14:53:37 +0000789 Handle<Context> context = args.at<Context>(0);
790 CONVERT_ARG_CHECKED(FixedArray, pairs, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000791 bool is_eval = Smi::cast(args[2])->value() == 1;
792
793 // Compute the property attributes. According to ECMA-262, section
794 // 13, page 71, the property must be read-only and
795 // non-deletable. However, neither SpiderMonkey nor KJS creates the
796 // property as read-only, so we don't either.
797 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
798
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000799 // Traverse the name/value pairs and set the properties.
800 int length = pairs->length();
801 for (int i = 0; i < length; i += 2) {
802 HandleScope scope;
803 Handle<String> name(String::cast(pairs->get(i)));
804 Handle<Object> value(pairs->get(i + 1));
805
806 // We have to declare a global const property. To capture we only
807 // assign to it when evaluating the assignment for "const x =
808 // <expr>" the initial value is the hole.
809 bool is_const_property = value->IsTheHole();
810
811 if (value->IsUndefined() || is_const_property) {
812 // Lookup the property in the global object, and don't set the
813 // value of the variable if the property is already there.
814 LookupResult lookup;
815 global->Lookup(*name, &lookup);
816 if (lookup.IsProperty()) {
817 // Determine if the property is local by comparing the holder
818 // against the global object. The information will be used to
819 // avoid throwing re-declaration errors when declaring
820 // variables or constants that exist in the prototype chain.
821 bool is_local = (*global == lookup.holder());
822 // Get the property attributes and determine if the property is
823 // read-only.
824 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
825 bool is_read_only = (attributes & READ_ONLY) != 0;
826 if (lookup.type() == INTERCEPTOR) {
827 // If the interceptor says the property is there, we
828 // just return undefined without overwriting the property.
829 // Otherwise, we continue to setting the property.
830 if (attributes != ABSENT) {
831 // Check if the existing property conflicts with regards to const.
832 if (is_local && (is_read_only || is_const_property)) {
833 const char* type = (is_read_only) ? "const" : "var";
834 return ThrowRedeclarationError(type, name);
835 };
836 // The property already exists without conflicting: Go to
837 // the next declaration.
838 continue;
839 }
840 // Fall-through and introduce the absent property by using
841 // SetProperty.
842 } else {
843 if (is_local && (is_read_only || is_const_property)) {
844 const char* type = (is_read_only) ? "const" : "var";
845 return ThrowRedeclarationError(type, name);
846 }
847 // The property already exists without conflicting: Go to
848 // the next declaration.
849 continue;
850 }
851 }
852 } else {
853 // Copy the function and update its context. Use it as value.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000854 Handle<SharedFunctionInfo> shared =
855 Handle<SharedFunctionInfo>::cast(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000856 Handle<JSFunction> function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000857 Factory::NewFunctionFromSharedFunctionInfo(shared, context, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000858 value = function;
859 }
860
861 LookupResult lookup;
862 global->LocalLookup(*name, &lookup);
863
864 PropertyAttributes attributes = is_const_property
865 ? static_cast<PropertyAttributes>(base | READ_ONLY)
866 : base;
867
868 if (lookup.IsProperty()) {
869 // There's a local property that we need to overwrite because
870 // we're either declaring a function or there's an interceptor
871 // that claims the property is absent.
872
873 // Check for conflicting re-declarations. We cannot have
874 // conflicting types in case of intercepted properties because
875 // they are absent.
876 if (lookup.type() != INTERCEPTOR &&
877 (lookup.IsReadOnly() || is_const_property)) {
878 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
879 return ThrowRedeclarationError(type, name);
880 }
881 SetProperty(global, name, value, attributes);
882 } else {
883 // If a property with this name does not already exist on the
884 // global object add the property locally. We take special
885 // precautions to always add it as a local property even in case
886 // of callbacks in the prototype chain (this rules out using
887 // SetProperty). Also, we must use the handle-based version to
888 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000889 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000890 }
891 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000892
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000893 return Heap::undefined_value();
894}
895
896
897static Object* Runtime_DeclareContextSlot(Arguments args) {
898 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000899 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000900
ager@chromium.org7c537e22008-10-16 08:43:32 +0000901 CONVERT_ARG_CHECKED(Context, context, 0);
902 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000903 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000904 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000905 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000906 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000907
908 // Declarations are always done in the function context.
909 context = Handle<Context>(context->fcontext());
910
911 int index;
912 PropertyAttributes attributes;
913 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000914 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000915 context->Lookup(name, flags, &index, &attributes);
916
917 if (attributes != ABSENT) {
918 // The name was declared before; check for conflicting
919 // re-declarations: This is similar to the code in parser.cc in
920 // the AstBuildingParser::Declare function.
921 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
922 // Functions are not read-only.
923 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
924 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
925 return ThrowRedeclarationError(type, name);
926 }
927
928 // Initialize it if necessary.
929 if (*initial_value != NULL) {
930 if (index >= 0) {
931 // The variable or constant context slot should always be in
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000932 // the function context or the arguments object.
933 if (holder->IsContext()) {
934 ASSERT(holder.is_identical_to(context));
935 if (((attributes & READ_ONLY) == 0) ||
936 context->get(index)->IsTheHole()) {
937 context->set(index, *initial_value);
938 }
939 } else {
940 Handle<JSObject>::cast(holder)->SetElement(index, *initial_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000941 }
942 } else {
943 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000944 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000945 SetProperty(context_ext, name, initial_value, mode);
946 }
947 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000948
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000949 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000950 // The property is not in the function context. It needs to be
951 // "declared" in the function context's extension context, or in the
952 // global context.
953 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000954 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000955 // The function context's extension context exists - use it.
956 context_ext = Handle<JSObject>(context->extension());
957 } else {
958 // The function context's extension context does not exists - allocate
959 // it.
960 context_ext = Factory::NewJSObject(Top::context_extension_function());
961 // And store it in the extension slot.
962 context->set_extension(*context_ext);
963 }
964 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000965
ager@chromium.org7c537e22008-10-16 08:43:32 +0000966 // Declare the property by setting it to the initial value if provided,
967 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
968 // constant declarations).
969 ASSERT(!context_ext->HasLocalProperty(*name));
970 Handle<Object> value(Heap::undefined_value());
971 if (*initial_value != NULL) value = initial_value;
972 SetProperty(context_ext, name, value, mode);
973 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
974 }
975
976 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000977}
978
979
980static Object* Runtime_InitializeVarGlobal(Arguments args) {
981 NoHandleAllocation nha;
982
983 // Determine if we need to assign to the variable if it already
984 // exists (based on the number of arguments).
985 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
986 bool assign = args.length() == 2;
987
988 CONVERT_ARG_CHECKED(String, name, 0);
989 GlobalObject* global = Top::context()->global();
990
991 // According to ECMA-262, section 12.2, page 62, the property must
992 // not be deletable.
993 PropertyAttributes attributes = DONT_DELETE;
994
995 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000996 // there, there is a property with this name in the prototype chain.
997 // We follow Safari and Firefox behavior and only set the property
998 // locally if there is an explicit initialization value that we have
999 // to assign to the property. When adding the property we take
1000 // special precautions to always add it as a local property even in
1001 // case of callbacks in the prototype chain (this rules out using
1002 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
1003 // this.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001004 // Note that objects can have hidden prototypes, so we need to traverse
1005 // the whole chain of hidden prototypes to do a 'local' lookup.
1006 JSObject* real_holder = global;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001007 LookupResult lookup;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001008 while (true) {
1009 real_holder->LocalLookup(*name, &lookup);
1010 if (lookup.IsProperty()) {
1011 // Determine if this is a redeclaration of something read-only.
1012 if (lookup.IsReadOnly()) {
1013 // If we found readonly property on one of hidden prototypes,
1014 // just shadow it.
1015 if (real_holder != Top::context()->global()) break;
1016 return ThrowRedeclarationError("const", name);
1017 }
1018
1019 // Determine if this is a redeclaration of an intercepted read-only
1020 // property and figure out if the property exists at all.
1021 bool found = true;
1022 PropertyType type = lookup.type();
1023 if (type == INTERCEPTOR) {
1024 HandleScope handle_scope;
1025 Handle<JSObject> holder(real_holder);
1026 PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
1027 real_holder = *holder;
1028 if (intercepted == ABSENT) {
1029 // The interceptor claims the property isn't there. We need to
1030 // make sure to introduce it.
1031 found = false;
1032 } else if ((intercepted & READ_ONLY) != 0) {
1033 // The property is present, but read-only. Since we're trying to
1034 // overwrite it with a variable declaration we must throw a
1035 // re-declaration error. However if we found readonly property
1036 // on one of hidden prototypes, just shadow it.
1037 if (real_holder != Top::context()->global()) break;
1038 return ThrowRedeclarationError("const", name);
1039 }
1040 }
1041
1042 if (found && !assign) {
1043 // The global property is there and we're not assigning any value
1044 // to it. Just return.
1045 return Heap::undefined_value();
1046 }
1047
1048 // Assign the value (or undefined) to the property.
1049 Object* value = (assign) ? args[1] : Heap::undefined_value();
1050 return real_holder->SetProperty(&lookup, *name, value, attributes);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001051 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001052
1053 Object* proto = real_holder->GetPrototype();
1054 if (!proto->IsJSObject())
1055 break;
1056
1057 if (!JSObject::cast(proto)->map()->is_hidden_prototype())
1058 break;
1059
1060 real_holder = JSObject::cast(proto);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001061 }
1062
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001063 global = Top::context()->global();
1064 if (assign) {
1065 return global->IgnoreAttributesAndSetLocalProperty(*name,
1066 args[1],
1067 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001068 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001069 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001070}
1071
1072
1073static Object* Runtime_InitializeConstGlobal(Arguments args) {
1074 // All constants are declared with an initial value. The name
1075 // of the constant is the first argument and the initial value
1076 // is the second.
1077 RUNTIME_ASSERT(args.length() == 2);
1078 CONVERT_ARG_CHECKED(String, name, 0);
1079 Handle<Object> value = args.at<Object>(1);
1080
1081 // Get the current global object from top.
1082 GlobalObject* global = Top::context()->global();
1083
1084 // According to ECMA-262, section 12.2, page 62, the property must
1085 // not be deletable. Since it's a const, it must be READ_ONLY too.
1086 PropertyAttributes attributes =
1087 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
1088
1089 // Lookup the property locally in the global object. If it isn't
1090 // there, we add the property and take special precautions to always
1091 // add it as a local property even in case of callbacks in the
1092 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001093 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001094 LookupResult lookup;
1095 global->LocalLookup(*name, &lookup);
1096 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001097 return global->IgnoreAttributesAndSetLocalProperty(*name,
1098 *value,
1099 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001100 }
1101
1102 // Determine if this is a redeclaration of something not
1103 // read-only. In case the result is hidden behind an interceptor we
1104 // need to ask it for the property attributes.
1105 if (!lookup.IsReadOnly()) {
1106 if (lookup.type() != INTERCEPTOR) {
1107 return ThrowRedeclarationError("var", name);
1108 }
1109
1110 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
1111
1112 // Throw re-declaration error if the intercepted property is present
1113 // but not read-only.
1114 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
1115 return ThrowRedeclarationError("var", name);
1116 }
1117
1118 // Restore global object from context (in case of GC) and continue
1119 // with setting the value because the property is either absent or
1120 // read-only. We also have to do redo the lookup.
1121 global = Top::context()->global();
1122
1123 // BUG 1213579: Handle the case where we have to set a read-only
1124 // property through an interceptor and only do it if it's
1125 // uninitialized, e.g. the hole. Nirk...
1126 global->SetProperty(*name, *value, attributes);
1127 return *value;
1128 }
1129
1130 // Set the value, but only we're assigning the initial value to a
1131 // constant. For now, we determine this by checking if the
1132 // current value is the hole.
1133 PropertyType type = lookup.type();
1134 if (type == FIELD) {
1135 FixedArray* properties = global->properties();
1136 int index = lookup.GetFieldIndex();
1137 if (properties->get(index)->IsTheHole()) {
1138 properties->set(index, *value);
1139 }
1140 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001141 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
1142 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001143 }
1144 } else {
1145 // Ignore re-initialization of constants that have already been
1146 // assigned a function value.
1147 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
1148 }
1149
1150 // Use the set value as the result of the operation.
1151 return *value;
1152}
1153
1154
1155static Object* Runtime_InitializeConstContextSlot(Arguments args) {
1156 HandleScope scope;
1157 ASSERT(args.length() == 3);
1158
1159 Handle<Object> value(args[0]);
1160 ASSERT(!value->IsTheHole());
1161 CONVERT_ARG_CHECKED(Context, context, 1);
1162 Handle<String> name(String::cast(args[2]));
1163
1164 // Initializations are always done in the function context.
1165 context = Handle<Context>(context->fcontext());
1166
1167 int index;
1168 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001169 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001170 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001171 context->Lookup(name, flags, &index, &attributes);
1172
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001173 // In most situations, the property introduced by the const
1174 // declaration should be present in the context extension object.
1175 // However, because declaration and initialization are separate, the
1176 // property might have been deleted (if it was introduced by eval)
1177 // before we reach the initialization point.
1178 //
1179 // Example:
1180 //
1181 // function f() { eval("delete x; const x;"); }
1182 //
1183 // In that case, the initialization behaves like a normal assignment
1184 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001185 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001186 // Property was found in a context.
1187 if (holder->IsContext()) {
1188 // The holder cannot be the function context. If it is, there
1189 // should have been a const redeclaration error when declaring
1190 // the const property.
1191 ASSERT(!holder.is_identical_to(context));
1192 if ((attributes & READ_ONLY) == 0) {
1193 Handle<Context>::cast(holder)->set(index, *value);
1194 }
1195 } else {
1196 // The holder is an arguments object.
1197 ASSERT((attributes & READ_ONLY) == 0);
1198 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001199 }
1200 return *value;
1201 }
1202
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001203 // The property could not be found, we introduce it in the global
1204 // context.
1205 if (attributes == ABSENT) {
1206 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1207 SetProperty(global, name, value, NONE);
1208 return *value;
1209 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001210
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001211 // The property was present in a context extension object.
1212 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001213
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001214 if (*context_ext == context->extension()) {
1215 // This is the property that was introduced by the const
1216 // declaration. Set it if it hasn't been set before. NOTE: We
1217 // cannot use GetProperty() to get the current value as it
1218 // 'unholes' the value.
1219 LookupResult lookup;
1220 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1221 ASSERT(lookup.IsProperty()); // the property was declared
1222 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1223
1224 PropertyType type = lookup.type();
1225 if (type == FIELD) {
1226 FixedArray* properties = context_ext->properties();
1227 int index = lookup.GetFieldIndex();
1228 if (properties->get(index)->IsTheHole()) {
1229 properties->set(index, *value);
1230 }
1231 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001232 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
1233 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001234 }
1235 } else {
1236 // We should not reach here. Any real, named property should be
1237 // either a field or a dictionary slot.
1238 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001239 }
1240 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001241 // The property was found in a different context extension object.
1242 // Set it if it is not a read-only property.
1243 if ((attributes & READ_ONLY) == 0) {
1244 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1245 // Setting a property might throw an exception. Exceptions
1246 // are converted to empty handles in handle operations. We
1247 // need to convert back to exceptions here.
1248 if (set.is_null()) {
1249 ASSERT(Top::has_pending_exception());
1250 return Failure::Exception();
1251 }
1252 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001253 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001254
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001255 return *value;
1256}
1257
1258
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001259static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1260 Arguments args) {
1261 HandleScope scope;
1262 ASSERT(args.length() == 2);
1263 CONVERT_ARG_CHECKED(JSObject, object, 0);
1264 CONVERT_SMI_CHECKED(properties, args[1]);
1265 if (object->HasFastProperties()) {
1266 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1267 }
1268 return *object;
1269}
1270
1271
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001272static Object* Runtime_RegExpExec(Arguments args) {
1273 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001274 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001275 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1276 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001277 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001278 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001279 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001280 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001281 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001282 RUNTIME_ASSERT(index >= 0);
1283 RUNTIME_ASSERT(index <= subject->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001284 Counters::regexp_entry_runtime.Increment();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001285 Handle<Object> result = RegExpImpl::Exec(regexp,
1286 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001287 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001288 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001289 if (result.is_null()) return Failure::Exception();
1290 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001291}
1292
1293
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00001294static Object* Runtime_RegExpConstructResult(Arguments args) {
1295 ASSERT(args.length() == 3);
1296 CONVERT_SMI_CHECKED(elements_count, args[0]);
1297 if (elements_count > JSArray::kMaxFastElementsLength) {
1298 return Top::ThrowIllegalOperation();
1299 }
1300 Object* new_object = Heap::AllocateFixedArrayWithHoles(elements_count);
1301 if (new_object->IsFailure()) return new_object;
1302 FixedArray* elements = FixedArray::cast(new_object);
1303 new_object = Heap::AllocateRaw(JSRegExpResult::kSize,
1304 NEW_SPACE,
1305 OLD_POINTER_SPACE);
1306 if (new_object->IsFailure()) return new_object;
1307 {
1308 AssertNoAllocation no_gc;
1309 HandleScope scope;
1310 reinterpret_cast<HeapObject*>(new_object)->
1311 set_map(Top::global_context()->regexp_result_map());
1312 }
1313 JSArray* array = JSArray::cast(new_object);
1314 array->set_properties(Heap::empty_fixed_array());
1315 array->set_elements(elements);
1316 array->set_length(Smi::FromInt(elements_count));
1317 // Write in-object properties after the length of the array.
1318 array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, args[1]);
1319 array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, args[2]);
1320 return array;
1321}
1322
1323
lrn@chromium.org25156de2010-04-06 13:10:27 +00001324static Object* Runtime_RegExpInitializeObject(Arguments args) {
1325 AssertNoAllocation no_alloc;
1326 ASSERT(args.length() == 5);
1327 CONVERT_CHECKED(JSRegExp, regexp, args[0]);
1328 CONVERT_CHECKED(String, source, args[1]);
1329
1330 Object* global = args[2];
1331 if (!global->IsTrue()) global = Heap::false_value();
1332
1333 Object* ignoreCase = args[3];
1334 if (!ignoreCase->IsTrue()) ignoreCase = Heap::false_value();
1335
1336 Object* multiline = args[4];
1337 if (!multiline->IsTrue()) multiline = Heap::false_value();
1338
1339 Map* map = regexp->map();
1340 Object* constructor = map->constructor();
1341 if (constructor->IsJSFunction() &&
1342 JSFunction::cast(constructor)->initial_map() == map) {
1343 // If we still have the original map, set in-object properties directly.
1344 regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, source);
1345 // TODO(lrn): Consider skipping write barrier on booleans as well.
1346 // Both true and false should be in oldspace at all times.
1347 regexp->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, global);
1348 regexp->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex, ignoreCase);
1349 regexp->InObjectPropertyAtPut(JSRegExp::kMultilineFieldIndex, multiline);
1350 regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex,
1351 Smi::FromInt(0),
1352 SKIP_WRITE_BARRIER);
1353 return regexp;
1354 }
1355
1356 // Map has changed, so use generic, but slower, method.
1357 PropertyAttributes final =
1358 static_cast<PropertyAttributes>(READ_ONLY | DONT_ENUM | DONT_DELETE);
1359 PropertyAttributes writable =
1360 static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
1361 regexp->IgnoreAttributesAndSetLocalProperty(Heap::source_symbol(),
1362 source,
1363 final);
1364 regexp->IgnoreAttributesAndSetLocalProperty(Heap::global_symbol(),
1365 global,
1366 final);
1367 regexp->IgnoreAttributesAndSetLocalProperty(Heap::ignore_case_symbol(),
1368 ignoreCase,
1369 final);
1370 regexp->IgnoreAttributesAndSetLocalProperty(Heap::multiline_symbol(),
1371 multiline,
1372 final);
1373 regexp->IgnoreAttributesAndSetLocalProperty(Heap::last_index_symbol(),
1374 Smi::FromInt(0),
1375 writable);
1376 return regexp;
1377}
1378
1379
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001380static Object* Runtime_FinishArrayPrototypeSetup(Arguments args) {
1381 HandleScope scope;
1382 ASSERT(args.length() == 1);
1383 CONVERT_ARG_CHECKED(JSArray, prototype, 0);
1384 // This is necessary to enable fast checks for absence of elements
1385 // on Array.prototype and below.
1386 prototype->set_elements(Heap::empty_fixed_array());
1387 return Smi::FromInt(0);
1388}
1389
1390
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001391static Handle<JSFunction> InstallBuiltin(Handle<JSObject> holder,
1392 const char* name,
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001393 Builtins::Name builtin_name) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001394 Handle<String> key = Factory::LookupAsciiSymbol(name);
1395 Handle<Code> code(Builtins::builtin(builtin_name));
1396 Handle<JSFunction> optimized = Factory::NewFunction(key,
1397 JS_OBJECT_TYPE,
1398 JSObject::kHeaderSize,
1399 code,
1400 false);
1401 optimized->shared()->DontAdaptArguments();
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001402 SetProperty(holder, key, optimized, NONE);
1403 return optimized;
1404}
1405
1406
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001407static Object* Runtime_SpecialArrayFunctions(Arguments args) {
1408 HandleScope scope;
1409 ASSERT(args.length() == 1);
1410 CONVERT_ARG_CHECKED(JSObject, holder, 0);
1411
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001412 InstallBuiltin(holder, "pop", Builtins::ArrayPop);
1413 InstallBuiltin(holder, "push", Builtins::ArrayPush);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001414 InstallBuiltin(holder, "shift", Builtins::ArrayShift);
1415 InstallBuiltin(holder, "unshift", Builtins::ArrayUnshift);
1416 InstallBuiltin(holder, "slice", Builtins::ArraySlice);
1417 InstallBuiltin(holder, "splice", Builtins::ArraySplice);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001418 InstallBuiltin(holder, "concat", Builtins::ArrayConcat);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001419
1420 return *holder;
1421}
1422
1423
ager@chromium.org357bf652010-04-12 11:30:10 +00001424static Object* Runtime_GetGlobalReceiver(Arguments args) {
1425 // Returns a real global receiver, not one of builtins object.
1426 Context* global_context = Top::context()->global()->global_context();
1427 return global_context->global()->global_receiver();
1428}
1429
1430
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001431static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1432 HandleScope scope;
1433 ASSERT(args.length() == 4);
1434 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1435 int index = Smi::cast(args[1])->value();
1436 Handle<String> pattern = args.at<String>(2);
1437 Handle<String> flags = args.at<String>(3);
1438
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001439 // Get the RegExp function from the context in the literals array.
1440 // This is the RegExp function from the context in which the
1441 // function was created. We do not use the RegExp function from the
1442 // current global context because this might be the RegExp function
1443 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001444 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001445 Handle<JSFunction>(
1446 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001447 // Compute the regular expression literal.
1448 bool has_pending_exception;
1449 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001450 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1451 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001452 if (has_pending_exception) {
1453 ASSERT(Top::has_pending_exception());
1454 return Failure::Exception();
1455 }
1456 literals->set(index, *regexp);
1457 return *regexp;
1458}
1459
1460
1461static Object* Runtime_FunctionGetName(Arguments args) {
1462 NoHandleAllocation ha;
1463 ASSERT(args.length() == 1);
1464
1465 CONVERT_CHECKED(JSFunction, f, args[0]);
1466 return f->shared()->name();
1467}
1468
1469
ager@chromium.org236ad962008-09-25 09:45:57 +00001470static Object* Runtime_FunctionSetName(Arguments args) {
1471 NoHandleAllocation ha;
1472 ASSERT(args.length() == 2);
1473
1474 CONVERT_CHECKED(JSFunction, f, args[0]);
1475 CONVERT_CHECKED(String, name, args[1]);
1476 f->shared()->set_name(name);
1477 return Heap::undefined_value();
1478}
1479
1480
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001481static Object* Runtime_FunctionRemovePrototype(Arguments args) {
1482 NoHandleAllocation ha;
1483 ASSERT(args.length() == 1);
1484
1485 CONVERT_CHECKED(JSFunction, f, args[0]);
1486 Object* obj = f->RemovePrototype();
1487 if (obj->IsFailure()) return obj;
1488
1489 return Heap::undefined_value();
1490}
1491
1492
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001493static Object* Runtime_FunctionGetScript(Arguments args) {
1494 HandleScope scope;
1495 ASSERT(args.length() == 1);
1496
1497 CONVERT_CHECKED(JSFunction, fun, args[0]);
1498 Handle<Object> script = Handle<Object>(fun->shared()->script());
1499 if (!script->IsScript()) return Heap::undefined_value();
1500
1501 return *GetScriptWrapper(Handle<Script>::cast(script));
1502}
1503
1504
1505static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1506 NoHandleAllocation ha;
1507 ASSERT(args.length() == 1);
1508
1509 CONVERT_CHECKED(JSFunction, f, args[0]);
1510 return f->shared()->GetSourceCode();
1511}
1512
1513
1514static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1515 NoHandleAllocation ha;
1516 ASSERT(args.length() == 1);
1517
1518 CONVERT_CHECKED(JSFunction, fun, args[0]);
1519 int pos = fun->shared()->start_position();
1520 return Smi::FromInt(pos);
1521}
1522
1523
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001524static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1525 ASSERT(args.length() == 2);
1526
1527 CONVERT_CHECKED(JSFunction, fun, args[0]);
1528 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1529
1530 Code* code = fun->code();
1531 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1532
1533 Address pc = code->address() + offset;
1534 return Smi::FromInt(fun->code()->SourcePosition(pc));
1535}
1536
1537
1538
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001539static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1540 NoHandleAllocation ha;
1541 ASSERT(args.length() == 2);
1542
1543 CONVERT_CHECKED(JSFunction, fun, args[0]);
1544 CONVERT_CHECKED(String, name, args[1]);
1545 fun->SetInstanceClassName(name);
1546 return Heap::undefined_value();
1547}
1548
1549
1550static Object* Runtime_FunctionSetLength(Arguments args) {
1551 NoHandleAllocation ha;
1552 ASSERT(args.length() == 2);
1553
1554 CONVERT_CHECKED(JSFunction, fun, args[0]);
1555 CONVERT_CHECKED(Smi, length, args[1]);
1556 fun->shared()->set_length(length->value());
1557 return length;
1558}
1559
1560
1561static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001562 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001563 ASSERT(args.length() == 2);
1564
1565 CONVERT_CHECKED(JSFunction, fun, args[0]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001566 ASSERT(fun->should_have_prototype());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001567 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1568 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001569 return args[0]; // return TOS
1570}
1571
1572
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001573static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1574 NoHandleAllocation ha;
1575 ASSERT(args.length() == 1);
1576
1577 CONVERT_CHECKED(JSFunction, f, args[0]);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001578 return f->shared()->IsApiFunction() ? Heap::true_value()
1579 : Heap::false_value();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001580}
1581
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001582static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1583 NoHandleAllocation ha;
1584 ASSERT(args.length() == 1);
1585
1586 CONVERT_CHECKED(JSFunction, f, args[0]);
1587 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1588}
1589
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001590
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001591static Object* Runtime_SetCode(Arguments args) {
1592 HandleScope scope;
1593 ASSERT(args.length() == 2);
1594
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001595 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001596 Handle<Object> code = args.at<Object>(1);
1597
1598 Handle<Context> context(target->context());
1599
1600 if (!code->IsNull()) {
1601 RUNTIME_ASSERT(code->IsJSFunction());
1602 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001603 Handle<SharedFunctionInfo> shared(fun->shared());
1604 SetExpectedNofProperties(target, shared->expected_nof_properties());
1605
1606 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001607 return Failure::Exception();
1608 }
1609 // Set the code, formal parameter count, and the length of the target
1610 // function.
1611 target->set_code(fun->code());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001612 target->shared()->set_length(shared->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001613 target->shared()->set_formal_parameter_count(
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001614 shared->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001615 // Set the source code of the target function to undefined.
1616 // SetCode is only used for built-in constructors like String,
1617 // Array, and Object, and some web code
1618 // doesn't like seeing source code for constructors.
1619 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001620 // Clear the optimization hints related to the compiled code as these are no
1621 // longer valid when the code is overwritten.
1622 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001623 context = Handle<Context>(fun->context());
1624
1625 // Make sure we get a fresh copy of the literal vector to avoid
1626 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001627 int number_of_literals = fun->NumberOfLiterals();
1628 Handle<FixedArray> literals =
1629 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001630 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001631 // Insert the object, regexp and array functions in the literals
1632 // array prefix. These are the functions that will be used when
1633 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001634 literals->set(JSFunction::kLiteralGlobalContextIndex,
1635 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001636 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001637 // It's okay to skip the write barrier here because the literals
1638 // are guaranteed to be in old space.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001639 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001640 }
1641
1642 target->set_context(*context);
1643 return *target;
1644}
1645
1646
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001647static Object* CharFromCode(Object* char_code) {
1648 uint32_t code;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001649 if (char_code->ToArrayIndex(&code)) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001650 if (code <= 0xffff) {
1651 return Heap::LookupSingleCharacterStringFromCode(code);
1652 }
1653 }
1654 return Heap::empty_string();
1655}
1656
1657
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001658static Object* Runtime_StringCharCodeAt(Arguments args) {
1659 NoHandleAllocation ha;
1660 ASSERT(args.length() == 2);
1661
1662 CONVERT_CHECKED(String, subject, args[0]);
1663 Object* index = args[1];
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001664 RUNTIME_ASSERT(index->IsNumber());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001665
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001666 uint32_t i = 0;
1667 if (index->IsSmi()) {
1668 int value = Smi::cast(index)->value();
1669 if (value < 0) return Heap::nan_value();
1670 i = value;
1671 } else {
1672 ASSERT(index->IsHeapNumber());
1673 double value = HeapNumber::cast(index)->value();
1674 i = static_cast<uint32_t>(DoubleToInteger(value));
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001675 }
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001676
1677 // Flatten the string. If someone wants to get a char at an index
1678 // in a cons string, it is likely that more indices will be
1679 // accessed.
1680 Object* flat = subject->TryFlatten();
1681 if (flat->IsFailure()) return flat;
1682 subject = String::cast(flat);
1683
1684 if (i >= static_cast<uint32_t>(subject->length())) {
1685 return Heap::nan_value();
1686 }
1687
1688 return Smi::FromInt(subject->Get(i));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001689}
1690
1691
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001692static Object* Runtime_CharFromCode(Arguments args) {
1693 NoHandleAllocation ha;
1694 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001695 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001696}
1697
lrn@chromium.org25156de2010-04-06 13:10:27 +00001698
1699class FixedArrayBuilder {
1700 public:
1701 explicit FixedArrayBuilder(int initial_capacity)
1702 : array_(Factory::NewFixedArrayWithHoles(initial_capacity)),
1703 length_(0) {
1704 // Require a non-zero initial size. Ensures that doubling the size to
1705 // extend the array will work.
1706 ASSERT(initial_capacity > 0);
1707 }
1708
1709 explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
1710 : array_(backing_store),
1711 length_(0) {
1712 // Require a non-zero initial size. Ensures that doubling the size to
1713 // extend the array will work.
1714 ASSERT(backing_store->length() > 0);
1715 }
1716
1717 bool HasCapacity(int elements) {
1718 int length = array_->length();
1719 int required_length = length_ + elements;
1720 return (length >= required_length);
1721 }
1722
1723 void EnsureCapacity(int elements) {
1724 int length = array_->length();
1725 int required_length = length_ + elements;
1726 if (length < required_length) {
1727 int new_length = length;
1728 do {
1729 new_length *= 2;
1730 } while (new_length < required_length);
1731 Handle<FixedArray> extended_array =
1732 Factory::NewFixedArrayWithHoles(new_length);
1733 array_->CopyTo(0, *extended_array, 0, length_);
1734 array_ = extended_array;
1735 }
1736 }
1737
1738 void Add(Object* value) {
1739 ASSERT(length_ < capacity());
1740 array_->set(length_, value);
1741 length_++;
1742 }
1743
1744 void Add(Smi* value) {
1745 ASSERT(length_ < capacity());
1746 array_->set(length_, value);
1747 length_++;
1748 }
1749
1750 Handle<FixedArray> array() {
1751 return array_;
1752 }
1753
1754 int length() {
1755 return length_;
1756 }
1757
1758 int capacity() {
1759 return array_->length();
1760 }
1761
1762 Handle<JSArray> ToJSArray() {
1763 Handle<JSArray> result_array = Factory::NewJSArrayWithElements(array_);
1764 result_array->set_length(Smi::FromInt(length_));
1765 return result_array;
1766 }
1767
1768 Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
1769 target_array->set_elements(*array_);
1770 target_array->set_length(Smi::FromInt(length_));
1771 return target_array;
1772 }
1773
1774 private:
1775 Handle<FixedArray> array_;
1776 int length_;
1777};
1778
1779
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001780// Forward declarations.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001781const int kStringBuilderConcatHelperLengthBits = 11;
1782const int kStringBuilderConcatHelperPositionBits = 19;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001783
1784template <typename schar>
1785static inline void StringBuilderConcatHelper(String*,
1786 schar*,
1787 FixedArray*,
1788 int);
1789
lrn@chromium.org25156de2010-04-06 13:10:27 +00001790typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
1791 StringBuilderSubstringLength;
1792typedef BitField<int,
1793 kStringBuilderConcatHelperLengthBits,
1794 kStringBuilderConcatHelperPositionBits>
1795 StringBuilderSubstringPosition;
1796
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001797
1798class ReplacementStringBuilder {
1799 public:
1800 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
lrn@chromium.org25156de2010-04-06 13:10:27 +00001801 : array_builder_(estimated_part_count),
1802 subject_(subject),
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001803 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001804 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001805 // Require a non-zero initial size. Ensures that doubling the size to
1806 // extend the array will work.
1807 ASSERT(estimated_part_count > 0);
1808 }
1809
lrn@chromium.org25156de2010-04-06 13:10:27 +00001810 static inline void AddSubjectSlice(FixedArrayBuilder* builder,
1811 int from,
1812 int to) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001813 ASSERT(from >= 0);
1814 int length = to - from;
1815 ASSERT(length > 0);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001816 if (StringBuilderSubstringLength::is_valid(length) &&
1817 StringBuilderSubstringPosition::is_valid(from)) {
1818 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1819 StringBuilderSubstringPosition::encode(from);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001820 builder->Add(Smi::FromInt(encoded_slice));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001821 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001822 // Otherwise encode as two smis.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001823 builder->Add(Smi::FromInt(-length));
1824 builder->Add(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001825 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00001826 }
1827
1828
1829 void EnsureCapacity(int elements) {
1830 array_builder_.EnsureCapacity(elements);
1831 }
1832
1833
1834 void AddSubjectSlice(int from, int to) {
1835 AddSubjectSlice(&array_builder_, from, to);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001836 IncrementCharacterCount(to - from);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001837 }
1838
1839
1840 void AddString(Handle<String> string) {
1841 int length = string->length();
1842 ASSERT(length > 0);
1843 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001844 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001845 is_ascii_ = false;
1846 }
1847 IncrementCharacterCount(length);
1848 }
1849
1850
1851 Handle<String> ToString() {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001852 if (array_builder_.length() == 0) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001853 return Factory::empty_string();
1854 }
1855
1856 Handle<String> joined_string;
1857 if (is_ascii_) {
1858 joined_string = NewRawAsciiString(character_count_);
1859 AssertNoAllocation no_alloc;
1860 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1861 char* char_buffer = seq->GetChars();
1862 StringBuilderConcatHelper(*subject_,
1863 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001864 *array_builder_.array(),
1865 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001866 } else {
1867 // Non-ASCII.
1868 joined_string = NewRawTwoByteString(character_count_);
1869 AssertNoAllocation no_alloc;
1870 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1871 uc16* char_buffer = seq->GetChars();
1872 StringBuilderConcatHelper(*subject_,
1873 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001874 *array_builder_.array(),
1875 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001876 }
1877 return joined_string;
1878 }
1879
1880
1881 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001882 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001883 V8::FatalProcessOutOfMemory("String.replace result too large.");
1884 }
1885 character_count_ += by;
1886 }
1887
lrn@chromium.org25156de2010-04-06 13:10:27 +00001888 Handle<JSArray> GetParts() {
1889 Handle<JSArray> result =
1890 Factory::NewJSArrayWithElements(array_builder_.array());
1891 result->set_length(Smi::FromInt(array_builder_.length()));
1892 return result;
1893 }
kmillikin@chromium.orgd9825192010-03-30 08:36:16 +00001894
lrn@chromium.org25156de2010-04-06 13:10:27 +00001895 private:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001896 Handle<String> NewRawAsciiString(int size) {
1897 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1898 }
1899
1900
1901 Handle<String> NewRawTwoByteString(int size) {
1902 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1903 }
1904
1905
1906 void AddElement(Object* element) {
1907 ASSERT(element->IsSmi() || element->IsString());
lrn@chromium.org25156de2010-04-06 13:10:27 +00001908 ASSERT(array_builder_.capacity() > array_builder_.length());
1909 array_builder_.Add(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001910 }
1911
lrn@chromium.org25156de2010-04-06 13:10:27 +00001912 FixedArrayBuilder array_builder_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001913 Handle<String> subject_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001914 int character_count_;
1915 bool is_ascii_;
1916};
1917
1918
1919class CompiledReplacement {
1920 public:
1921 CompiledReplacement()
1922 : parts_(1), replacement_substrings_(0) {}
1923
1924 void Compile(Handle<String> replacement,
1925 int capture_count,
1926 int subject_length);
1927
1928 void Apply(ReplacementStringBuilder* builder,
1929 int match_from,
1930 int match_to,
1931 Handle<JSArray> last_match_info);
1932
1933 // Number of distinct parts of the replacement pattern.
1934 int parts() {
1935 return parts_.length();
1936 }
1937 private:
1938 enum PartType {
1939 SUBJECT_PREFIX = 1,
1940 SUBJECT_SUFFIX,
1941 SUBJECT_CAPTURE,
1942 REPLACEMENT_SUBSTRING,
1943 REPLACEMENT_STRING,
1944
1945 NUMBER_OF_PART_TYPES
1946 };
1947
1948 struct ReplacementPart {
1949 static inline ReplacementPart SubjectMatch() {
1950 return ReplacementPart(SUBJECT_CAPTURE, 0);
1951 }
1952 static inline ReplacementPart SubjectCapture(int capture_index) {
1953 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1954 }
1955 static inline ReplacementPart SubjectPrefix() {
1956 return ReplacementPart(SUBJECT_PREFIX, 0);
1957 }
1958 static inline ReplacementPart SubjectSuffix(int subject_length) {
1959 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1960 }
1961 static inline ReplacementPart ReplacementString() {
1962 return ReplacementPart(REPLACEMENT_STRING, 0);
1963 }
1964 static inline ReplacementPart ReplacementSubString(int from, int to) {
1965 ASSERT(from >= 0);
1966 ASSERT(to > from);
1967 return ReplacementPart(-from, to);
1968 }
1969
1970 // If tag <= 0 then it is the negation of a start index of a substring of
1971 // the replacement pattern, otherwise it's a value from PartType.
1972 ReplacementPart(int tag, int data)
1973 : tag(tag), data(data) {
1974 // Must be non-positive or a PartType value.
1975 ASSERT(tag < NUMBER_OF_PART_TYPES);
1976 }
1977 // Either a value of PartType or a non-positive number that is
1978 // the negation of an index into the replacement string.
1979 int tag;
1980 // The data value's interpretation depends on the value of tag:
1981 // tag == SUBJECT_PREFIX ||
1982 // tag == SUBJECT_SUFFIX: data is unused.
1983 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1984 // tag == REPLACEMENT_SUBSTRING ||
1985 // tag == REPLACEMENT_STRING: data is index into array of substrings
1986 // of the replacement string.
1987 // tag <= 0: Temporary representation of the substring of the replacement
1988 // string ranging over -tag .. data.
1989 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1990 // substring objects.
1991 int data;
1992 };
1993
1994 template<typename Char>
1995 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1996 Vector<Char> characters,
1997 int capture_count,
1998 int subject_length) {
1999 int length = characters.length();
2000 int last = 0;
2001 for (int i = 0; i < length; i++) {
2002 Char c = characters[i];
2003 if (c == '$') {
2004 int next_index = i + 1;
2005 if (next_index == length) { // No next character!
2006 break;
2007 }
2008 Char c2 = characters[next_index];
2009 switch (c2) {
2010 case '$':
2011 if (i > last) {
2012 // There is a substring before. Include the first "$".
2013 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
2014 last = next_index + 1; // Continue after the second "$".
2015 } else {
2016 // Let the next substring start with the second "$".
2017 last = next_index;
2018 }
2019 i = next_index;
2020 break;
2021 case '`':
2022 if (i > last) {
2023 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2024 }
2025 parts->Add(ReplacementPart::SubjectPrefix());
2026 i = next_index;
2027 last = i + 1;
2028 break;
2029 case '\'':
2030 if (i > last) {
2031 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2032 }
2033 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
2034 i = next_index;
2035 last = i + 1;
2036 break;
2037 case '&':
2038 if (i > last) {
2039 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2040 }
2041 parts->Add(ReplacementPart::SubjectMatch());
2042 i = next_index;
2043 last = i + 1;
2044 break;
2045 case '0':
2046 case '1':
2047 case '2':
2048 case '3':
2049 case '4':
2050 case '5':
2051 case '6':
2052 case '7':
2053 case '8':
2054 case '9': {
2055 int capture_ref = c2 - '0';
2056 if (capture_ref > capture_count) {
2057 i = next_index;
2058 continue;
2059 }
2060 int second_digit_index = next_index + 1;
2061 if (second_digit_index < length) {
2062 // Peek ahead to see if we have two digits.
2063 Char c3 = characters[second_digit_index];
2064 if ('0' <= c3 && c3 <= '9') { // Double digits.
2065 int double_digit_ref = capture_ref * 10 + c3 - '0';
2066 if (double_digit_ref <= capture_count) {
2067 next_index = second_digit_index;
2068 capture_ref = double_digit_ref;
2069 }
2070 }
2071 }
2072 if (capture_ref > 0) {
2073 if (i > last) {
2074 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2075 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002076 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002077 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
2078 last = next_index + 1;
2079 }
2080 i = next_index;
2081 break;
2082 }
2083 default:
2084 i = next_index;
2085 break;
2086 }
2087 }
2088 }
2089 if (length > last) {
2090 if (last == 0) {
2091 parts->Add(ReplacementPart::ReplacementString());
2092 } else {
2093 parts->Add(ReplacementPart::ReplacementSubString(last, length));
2094 }
2095 }
2096 }
2097
2098 ZoneList<ReplacementPart> parts_;
2099 ZoneList<Handle<String> > replacement_substrings_;
2100};
2101
2102
2103void CompiledReplacement::Compile(Handle<String> replacement,
2104 int capture_count,
2105 int subject_length) {
2106 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00002107 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002108 AssertNoAllocation no_alloc;
2109 ParseReplacementPattern(&parts_,
2110 replacement->ToAsciiVector(),
2111 capture_count,
2112 subject_length);
2113 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00002114 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002115 AssertNoAllocation no_alloc;
2116
2117 ParseReplacementPattern(&parts_,
2118 replacement->ToUC16Vector(),
2119 capture_count,
2120 subject_length);
2121 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002122 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002123 int substring_index = 0;
2124 for (int i = 0, n = parts_.length(); i < n; i++) {
2125 int tag = parts_[i].tag;
2126 if (tag <= 0) { // A replacement string slice.
2127 int from = -tag;
2128 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002129 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002130 parts_[i].tag = REPLACEMENT_SUBSTRING;
2131 parts_[i].data = substring_index;
2132 substring_index++;
2133 } else if (tag == REPLACEMENT_STRING) {
2134 replacement_substrings_.Add(replacement);
2135 parts_[i].data = substring_index;
2136 substring_index++;
2137 }
2138 }
2139}
2140
2141
2142void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
2143 int match_from,
2144 int match_to,
2145 Handle<JSArray> last_match_info) {
2146 for (int i = 0, n = parts_.length(); i < n; i++) {
2147 ReplacementPart part = parts_[i];
2148 switch (part.tag) {
2149 case SUBJECT_PREFIX:
2150 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
2151 break;
2152 case SUBJECT_SUFFIX: {
2153 int subject_length = part.data;
2154 if (match_to < subject_length) {
2155 builder->AddSubjectSlice(match_to, subject_length);
2156 }
2157 break;
2158 }
2159 case SUBJECT_CAPTURE: {
2160 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002161 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002162 int from = RegExpImpl::GetCapture(match_info, capture * 2);
2163 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
2164 if (from >= 0 && to > from) {
2165 builder->AddSubjectSlice(from, to);
2166 }
2167 break;
2168 }
2169 case REPLACEMENT_SUBSTRING:
2170 case REPLACEMENT_STRING:
2171 builder->AddString(replacement_substrings_[part.data]);
2172 break;
2173 default:
2174 UNREACHABLE();
2175 }
2176 }
2177}
2178
2179
2180
2181static Object* StringReplaceRegExpWithString(String* subject,
2182 JSRegExp* regexp,
2183 String* replacement,
2184 JSArray* last_match_info) {
2185 ASSERT(subject->IsFlat());
2186 ASSERT(replacement->IsFlat());
2187
2188 HandleScope handles;
2189
2190 int length = subject->length();
2191 Handle<String> subject_handle(subject);
2192 Handle<JSRegExp> regexp_handle(regexp);
2193 Handle<String> replacement_handle(replacement);
2194 Handle<JSArray> last_match_info_handle(last_match_info);
2195 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2196 subject_handle,
2197 0,
2198 last_match_info_handle);
2199 if (match.is_null()) {
2200 return Failure::Exception();
2201 }
2202 if (match->IsNull()) {
2203 return *subject_handle;
2204 }
2205
2206 int capture_count = regexp_handle->CaptureCount();
2207
2208 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002209 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002210 CompiledReplacement compiled_replacement;
2211 compiled_replacement.Compile(replacement_handle,
2212 capture_count,
2213 length);
2214
2215 bool is_global = regexp_handle->GetFlags().is_global();
2216
2217 // Guessing the number of parts that the final result string is built
2218 // from. Global regexps can match any number of times, so we guess
2219 // conservatively.
2220 int expected_parts =
2221 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
2222 ReplacementStringBuilder builder(subject_handle, expected_parts);
2223
2224 // Index of end of last match.
2225 int prev = 0;
2226
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002227 // Number of parts added by compiled replacement plus preceeding
2228 // string and possibly suffix after last match. It is possible for
2229 // all components to use two elements when encoded as two smis.
2230 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002231 bool matched = true;
2232 do {
2233 ASSERT(last_match_info_handle->HasFastElements());
2234 // Increase the capacity of the builder before entering local handle-scope,
2235 // so its internal buffer can safely allocate a new handle if it grows.
2236 builder.EnsureCapacity(parts_added_per_loop);
2237
2238 HandleScope loop_scope;
2239 int start, end;
2240 {
2241 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002242 FixedArray* match_info_array =
2243 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002244
2245 ASSERT_EQ(capture_count * 2 + 2,
2246 RegExpImpl::GetLastCaptureCount(match_info_array));
2247 start = RegExpImpl::GetCapture(match_info_array, 0);
2248 end = RegExpImpl::GetCapture(match_info_array, 1);
2249 }
2250
2251 if (prev < start) {
2252 builder.AddSubjectSlice(prev, start);
2253 }
2254 compiled_replacement.Apply(&builder,
2255 start,
2256 end,
2257 last_match_info_handle);
2258 prev = end;
2259
2260 // Only continue checking for global regexps.
2261 if (!is_global) break;
2262
2263 // Continue from where the match ended, unless it was an empty match.
2264 int next = end;
2265 if (start == end) {
2266 next = end + 1;
2267 if (next > length) break;
2268 }
2269
2270 match = RegExpImpl::Exec(regexp_handle,
2271 subject_handle,
2272 next,
2273 last_match_info_handle);
2274 if (match.is_null()) {
2275 return Failure::Exception();
2276 }
2277 matched = !match->IsNull();
2278 } while (matched);
2279
2280 if (prev < length) {
2281 builder.AddSubjectSlice(prev, length);
2282 }
2283
2284 return *(builder.ToString());
2285}
2286
2287
2288static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2289 ASSERT(args.length() == 4);
2290
2291 CONVERT_CHECKED(String, subject, args[0]);
2292 if (!subject->IsFlat()) {
2293 Object* flat_subject = subject->TryFlatten();
2294 if (flat_subject->IsFailure()) {
2295 return flat_subject;
2296 }
2297 subject = String::cast(flat_subject);
2298 }
2299
2300 CONVERT_CHECKED(String, replacement, args[2]);
2301 if (!replacement->IsFlat()) {
2302 Object* flat_replacement = replacement->TryFlatten();
2303 if (flat_replacement->IsFailure()) {
2304 return flat_replacement;
2305 }
2306 replacement = String::cast(flat_replacement);
2307 }
2308
2309 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2310 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2311
2312 ASSERT(last_match_info->HasFastElements());
2313
2314 return StringReplaceRegExpWithString(subject,
2315 regexp,
2316 replacement,
2317 last_match_info);
2318}
2319
2320
ager@chromium.org7c537e22008-10-16 08:43:32 +00002321// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2322// limit, we can fix the size of tables.
2323static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002324// Reduce alphabet to this size.
2325static const int kBMAlphabetSize = 0x100;
2326// For patterns below this length, the skip length of Boyer-Moore is too short
2327// to compensate for the algorithmic overhead compared to simple brute force.
2328static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002329
ager@chromium.org7c537e22008-10-16 08:43:32 +00002330// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2331// shift. Only allows the last kBMMaxShift characters of the needle
2332// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002333class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002334 public:
2335 BMGoodSuffixBuffers() {}
2336 inline void init(int needle_length) {
2337 ASSERT(needle_length > 1);
2338 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2339 int len = needle_length - start;
2340 biased_suffixes_ = suffixes_ - start;
2341 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2342 for (int i = 0; i <= len; i++) {
2343 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002344 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002345 }
2346 inline int& suffix(int index) {
2347 ASSERT(biased_suffixes_ + index >= suffixes_);
2348 return biased_suffixes_[index];
2349 }
2350 inline int& shift(int index) {
2351 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2352 return biased_good_suffix_shift_[index];
2353 }
2354 private:
2355 int suffixes_[kBMMaxShift + 1];
2356 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002357 int* biased_suffixes_;
2358 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002359 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2360};
2361
2362// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002363static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002364static BMGoodSuffixBuffers bmgs_buffers;
2365
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002366// State of the string match tables.
2367// SIMPLE: No usable content in the buffers.
2368// BOYER_MOORE_HORSPOOL: The bad_char_occurences table has been populated.
2369// BOYER_MOORE: The bmgs_buffers tables have also been populated.
2370// Whenever starting with a new needle, one should call InitializeStringSearch
2371// to determine which search strategy to use, and in the case of a long-needle
2372// strategy, the call also initializes the algorithm to SIMPLE.
2373enum StringSearchAlgorithm { SIMPLE_SEARCH, BOYER_MOORE_HORSPOOL, BOYER_MOORE };
2374static StringSearchAlgorithm algorithm;
2375
2376
ager@chromium.org7c537e22008-10-16 08:43:32 +00002377// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002378template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002379static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern) {
2380 // Only preprocess at most kBMMaxShift last characters of pattern.
2381 int start = pattern.length() < kBMMaxShift ? 0
2382 : pattern.length() - kBMMaxShift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002383 // Run forwards to populate bad_char_table, so that *last* instance
2384 // of character equivalence class is the one registered.
2385 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002386 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2387 : kBMAlphabetSize;
2388 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002389 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002390 } else {
2391 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002392 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002393 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002394 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002395 for (int i = start; i < pattern.length() - 1; i++) {
2396 pchar c = pattern[i];
2397 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002398 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002399 }
2400}
2401
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002402
ager@chromium.org7c537e22008-10-16 08:43:32 +00002403template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002404static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002405 int m = pattern.length();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002406 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002407 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002408 // Compute Good Suffix tables.
2409 bmgs_buffers.init(m);
2410
2411 bmgs_buffers.shift(m-1) = 1;
2412 bmgs_buffers.suffix(m) = m + 1;
2413 pchar last_char = pattern[m - 1];
2414 int suffix = m + 1;
2415 for (int i = m; i > start;) {
2416 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2417 if (bmgs_buffers.shift(suffix) == len) {
2418 bmgs_buffers.shift(suffix) = suffix - i;
2419 }
2420 suffix = bmgs_buffers.suffix(suffix);
2421 }
2422 i--;
2423 suffix--;
2424 bmgs_buffers.suffix(i) = suffix;
2425 if (suffix == m) {
2426 // No suffix to extend, so we check against last_char only.
2427 while (i > start && pattern[i - 1] != last_char) {
2428 if (bmgs_buffers.shift(m) == len) {
2429 bmgs_buffers.shift(m) = m - i;
2430 }
2431 i--;
2432 bmgs_buffers.suffix(i) = m;
2433 }
2434 if (i > start) {
2435 i--;
2436 suffix--;
2437 bmgs_buffers.suffix(i) = suffix;
2438 }
2439 }
2440 }
2441 if (suffix < m) {
2442 for (int i = start; i <= m; i++) {
2443 if (bmgs_buffers.shift(i) == len) {
2444 bmgs_buffers.shift(i) = suffix - start;
2445 }
2446 if (i == suffix) {
2447 suffix = bmgs_buffers.suffix(suffix);
2448 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002449 }
2450 }
2451}
2452
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002453
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002454template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002455static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002456 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002457 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002458 }
2459 if (sizeof(pchar) == 1) {
2460 if (char_code > String::kMaxAsciiCharCode) {
2461 return -1;
2462 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002463 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002464 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002465 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002466}
2467
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002468
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002469// Restricted simplified Boyer-Moore string matching.
2470// Uses only the bad-shift table of Boyer-Moore and only uses it
2471// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002472template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002473static int BoyerMooreHorspool(Vector<const schar> subject,
2474 Vector<const pchar> pattern,
2475 int start_index,
2476 bool* complete) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002477 ASSERT(algorithm <= BOYER_MOORE_HORSPOOL);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002478 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002479 int m = pattern.length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002480
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002481 int badness = -m;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002482
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002483 // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002484 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002485 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002486 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002487 // Perform search
2488 for (idx = start_index; idx <= n - m;) {
2489 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002490 int c;
2491 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002492 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002493 int shift = j - bc_occ;
2494 idx += shift;
2495 badness += 1 - shift; // at most zero, so badness cannot increase.
2496 if (idx > n - m) {
2497 *complete = true;
2498 return -1;
2499 }
2500 }
2501 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002502 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002503 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002504 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002505 return idx;
2506 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002507 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002508 // Badness increases by the number of characters we have
2509 // checked, and decreases by the number of characters we
2510 // can skip by shifting. It's a measure of how we are doing
2511 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002512 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002513 if (badness > 0) {
2514 *complete = false;
2515 return idx;
2516 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002517 }
2518 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002519 *complete = true;
2520 return -1;
2521}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002522
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002523
2524template <typename schar, typename pchar>
2525static int BoyerMooreIndexOf(Vector<const schar> subject,
2526 Vector<const pchar> pattern,
2527 int idx) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002528 ASSERT(algorithm <= BOYER_MOORE);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002529 int n = subject.length();
2530 int m = pattern.length();
2531 // Only preprocess at most kBMMaxShift last characters of pattern.
2532 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2533
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002534 pchar last_char = pattern[m - 1];
2535 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002536 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002537 int j = m - 1;
2538 schar c;
2539 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002540 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002541 idx += shift;
2542 if (idx > n - m) {
2543 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002544 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002545 }
2546 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2547 if (j < 0) {
2548 return idx;
2549 } else if (j < start) {
2550 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002551 // Fall back on BMH shift.
2552 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002553 } else {
2554 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002555 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002556 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002557 if (gs_shift > shift) {
2558 shift = gs_shift;
2559 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002560 idx += shift;
2561 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002562 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002563
2564 return -1;
2565}
2566
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002567
2568template <typename schar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002569static inline int SingleCharIndexOf(Vector<const schar> string,
2570 schar pattern_char,
2571 int start_index) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002572 if (sizeof(schar) == 1) {
2573 const schar* pos = reinterpret_cast<const schar*>(
2574 memchr(string.start() + start_index,
2575 pattern_char,
2576 string.length() - start_index));
2577 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002578 return static_cast<int>(pos - string.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002579 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002580 for (int i = start_index, n = string.length(); i < n; i++) {
2581 if (pattern_char == string[i]) {
2582 return i;
2583 }
2584 }
2585 return -1;
2586}
2587
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002588
2589template <typename schar>
2590static int SingleCharLastIndexOf(Vector<const schar> string,
2591 schar pattern_char,
2592 int start_index) {
2593 for (int i = start_index; i >= 0; i--) {
2594 if (pattern_char == string[i]) {
2595 return i;
2596 }
2597 }
2598 return -1;
2599}
2600
2601
ager@chromium.org7c537e22008-10-16 08:43:32 +00002602// Trivial string search for shorter strings.
2603// On return, if "complete" is set to true, the return value is the
2604// final result of searching for the patter in the subject.
2605// If "complete" is set to false, the return value is the index where
2606// further checking should start, i.e., it's guaranteed that the pattern
2607// does not occur at a position prior to the returned index.
2608template <typename pchar, typename schar>
2609static int SimpleIndexOf(Vector<const schar> subject,
2610 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002611 int idx,
2612 bool* complete) {
2613 // Badness is a count of how much work we have done. When we have
2614 // done enough work we decide it's probably worth switching to a better
2615 // algorithm.
2616 int badness = -10 - (pattern.length() << 2);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002617
ager@chromium.org7c537e22008-10-16 08:43:32 +00002618 // We know our pattern is at least 2 characters, we cache the first so
2619 // the common case of the first character not matching is faster.
2620 pchar pattern_first_char = pattern[0];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002621 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2622 badness++;
2623 if (badness > 0) {
2624 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002625 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002626 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002627 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2628 const schar* pos = reinterpret_cast<const schar*>(
2629 memchr(subject.start() + i,
2630 pattern_first_char,
2631 n - i + 1));
2632 if (pos == NULL) {
2633 *complete = true;
2634 return -1;
2635 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002636 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002637 } else {
2638 if (subject[i] != pattern_first_char) continue;
2639 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002640 int j = 1;
2641 do {
2642 if (pattern[j] != subject[i+j]) {
2643 break;
2644 }
2645 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002646 } while (j < pattern.length());
2647 if (j == pattern.length()) {
2648 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002649 return i;
2650 }
2651 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002652 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002653 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002654 return -1;
2655}
2656
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002657// Simple indexOf that never bails out. For short patterns only.
2658template <typename pchar, typename schar>
2659static int SimpleIndexOf(Vector<const schar> subject,
2660 Vector<const pchar> pattern,
2661 int idx) {
2662 pchar pattern_first_char = pattern[0];
2663 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002664 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2665 const schar* pos = reinterpret_cast<const schar*>(
2666 memchr(subject.start() + i,
2667 pattern_first_char,
2668 n - i + 1));
2669 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002670 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002671 } else {
2672 if (subject[i] != pattern_first_char) continue;
2673 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002674 int j = 1;
2675 do {
2676 if (pattern[j] != subject[i+j]) {
2677 break;
2678 }
2679 j++;
2680 } while (j < pattern.length());
2681 if (j == pattern.length()) {
2682 return i;
2683 }
2684 }
2685 return -1;
2686}
2687
2688
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002689// Strategy for searching for a string in another string.
2690enum StringSearchStrategy { SEARCH_FAIL, SEARCH_SHORT, SEARCH_LONG };
ager@chromium.org7c537e22008-10-16 08:43:32 +00002691
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002692
2693template <typename pchar>
2694static inline StringSearchStrategy InitializeStringSearch(
2695 Vector<const pchar> pat, bool ascii_subject) {
2696 ASSERT(pat.length() > 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002697 // We have an ASCII haystack and a non-ASCII needle. Check if there
2698 // really is a non-ASCII character in the needle and bail out if there
2699 // is.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002700 if (ascii_subject && sizeof(pchar) > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002701 for (int i = 0; i < pat.length(); i++) {
2702 uc16 c = pat[i];
2703 if (c > String::kMaxAsciiCharCode) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002704 return SEARCH_FAIL;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002705 }
2706 }
2707 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002708 if (pat.length() < kBMMinPatternLength) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002709 return SEARCH_SHORT;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002710 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002711 algorithm = SIMPLE_SEARCH;
2712 return SEARCH_LONG;
2713}
2714
2715
2716// Dispatch long needle searches to different algorithms.
2717template <typename schar, typename pchar>
2718static int ComplexIndexOf(Vector<const schar> sub,
2719 Vector<const pchar> pat,
2720 int start_index) {
2721 ASSERT(pat.length() >= kBMMinPatternLength);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002722 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002723 bool complete;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002724 int idx = start_index;
2725 switch (algorithm) {
2726 case SIMPLE_SEARCH:
2727 idx = SimpleIndexOf(sub, pat, idx, &complete);
2728 if (complete) return idx;
2729 BoyerMoorePopulateBadCharTable(pat);
2730 algorithm = BOYER_MOORE_HORSPOOL;
2731 // FALLTHROUGH.
2732 case BOYER_MOORE_HORSPOOL:
2733 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
2734 if (complete) return idx;
2735 // Build the Good Suffix table and continue searching.
2736 BoyerMoorePopulateGoodSuffixTable(pat);
2737 algorithm = BOYER_MOORE;
2738 // FALLTHROUGH.
2739 case BOYER_MOORE:
2740 return BoyerMooreIndexOf(sub, pat, idx);
2741 }
2742 UNREACHABLE();
2743 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002744}
2745
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002746
2747// Dispatch to different search strategies for a single search.
2748// If searching multiple times on the same needle, the search
2749// strategy should only be computed once and then dispatch to different
2750// loops.
2751template <typename schar, typename pchar>
2752static int StringSearch(Vector<const schar> sub,
2753 Vector<const pchar> pat,
2754 int start_index) {
2755 bool ascii_subject = (sizeof(schar) == 1);
2756 StringSearchStrategy strategy = InitializeStringSearch(pat, ascii_subject);
2757 switch (strategy) {
2758 case SEARCH_FAIL: return -1;
2759 case SEARCH_SHORT: return SimpleIndexOf(sub, pat, start_index);
2760 case SEARCH_LONG: return ComplexIndexOf(sub, pat, start_index);
2761 }
2762 UNREACHABLE();
2763 return -1;
2764}
2765
2766
ager@chromium.org7c537e22008-10-16 08:43:32 +00002767// Perform string match of pattern on subject, starting at start index.
2768// Caller must ensure that 0 <= start_index <= sub->length(),
2769// and should check that pat->length() + start_index <= sub->length()
2770int Runtime::StringMatch(Handle<String> sub,
2771 Handle<String> pat,
2772 int start_index) {
2773 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002774 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002775
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002776 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002777 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002778
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002779 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002780 if (start_index + pattern_length > subject_length) return -1;
2781
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002782 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002783 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002784 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002785
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002786 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002787 // character patterns linear search is necessary, so any smart
2788 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002789 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002790 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002791 String* seq_sub = *sub;
2792 if (seq_sub->IsConsString()) {
2793 seq_sub = ConsString::cast(seq_sub)->first();
2794 }
2795 if (seq_sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002796 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002797 if (pchar > String::kMaxAsciiCharCode) {
2798 return -1;
2799 }
2800 Vector<const char> ascii_vector =
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002801 seq_sub->ToAsciiVector().SubVector(start_index, subject_length);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002802 const void* pos = memchr(ascii_vector.start(),
2803 static_cast<const char>(pchar),
2804 static_cast<size_t>(ascii_vector.length()));
2805 if (pos == NULL) {
2806 return -1;
2807 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002808 return static_cast<int>(reinterpret_cast<const char*>(pos)
2809 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002810 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002811 return SingleCharIndexOf(seq_sub->ToUC16Vector(),
2812 pat->Get(0),
2813 start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002814 }
2815
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002816 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002817 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002818 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002819
ager@chromium.org7c537e22008-10-16 08:43:32 +00002820 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002821 // Extract flattened substrings of cons strings before determining asciiness.
2822 String* seq_sub = *sub;
2823 if (seq_sub->IsConsString()) {
2824 seq_sub = ConsString::cast(seq_sub)->first();
2825 }
2826 String* seq_pat = *pat;
2827 if (seq_pat->IsConsString()) {
2828 seq_pat = ConsString::cast(seq_pat)->first();
2829 }
2830
ager@chromium.org7c537e22008-10-16 08:43:32 +00002831 // dispatch on type of strings
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002832 if (seq_pat->IsAsciiRepresentation()) {
2833 Vector<const char> pat_vector = seq_pat->ToAsciiVector();
2834 if (seq_sub->IsAsciiRepresentation()) {
2835 return StringSearch(seq_sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002836 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002837 return StringSearch(seq_sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002838 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002839 Vector<const uc16> pat_vector = seq_pat->ToUC16Vector();
2840 if (seq_sub->IsAsciiRepresentation()) {
2841 return StringSearch(seq_sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002842 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002843 return StringSearch(seq_sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002844}
2845
2846
2847static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002848 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002849 ASSERT(args.length() == 3);
2850
ager@chromium.org7c537e22008-10-16 08:43:32 +00002851 CONVERT_ARG_CHECKED(String, sub, 0);
2852 CONVERT_ARG_CHECKED(String, pat, 1);
2853
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002854 Object* index = args[2];
2855 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002856 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002857
ager@chromium.org870a0b62008-11-04 11:43:05 +00002858 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002859 int position = Runtime::StringMatch(sub, pat, start_index);
2860 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002861}
2862
2863
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002864template <typename schar, typename pchar>
2865static int StringMatchBackwards(Vector<const schar> sub,
2866 Vector<const pchar> pat,
2867 int idx) {
2868 ASSERT(pat.length() >= 1);
2869 ASSERT(idx + pat.length() <= sub.length());
2870
2871 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
2872 for (int i = 0; i < pat.length(); i++) {
2873 uc16 c = pat[i];
2874 if (c > String::kMaxAsciiCharCode) {
2875 return -1;
2876 }
2877 }
2878 }
2879
2880 pchar pattern_first_char = pat[0];
2881 for (int i = idx; i >= 0; i--) {
2882 if (sub[i] != pattern_first_char) continue;
2883 int j = 1;
2884 while (j < pat.length()) {
2885 if (pat[j] != sub[i+j]) {
2886 break;
2887 }
2888 j++;
2889 }
2890 if (j == pat.length()) {
2891 return i;
2892 }
2893 }
2894 return -1;
2895}
2896
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002897static Object* Runtime_StringLastIndexOf(Arguments args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002898 HandleScope scope; // create a new handle scope
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002899 ASSERT(args.length() == 3);
2900
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002901 CONVERT_ARG_CHECKED(String, sub, 0);
2902 CONVERT_ARG_CHECKED(String, pat, 1);
2903
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002904 Object* index = args[2];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002905 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002906 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002907
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002908 uint32_t pat_length = pat->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002909 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002910
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002911 if (start_index + pat_length > sub_length) {
2912 start_index = sub_length - pat_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002913 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002914
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002915 if (pat_length == 0) {
2916 return Smi::FromInt(start_index);
2917 }
2918
2919 if (!sub->IsFlat()) {
2920 FlattenString(sub);
2921 }
2922
2923 if (pat_length == 1) {
2924 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2925 if (sub->IsAsciiRepresentation()) {
2926 uc16 pchar = pat->Get(0);
2927 if (pchar > String::kMaxAsciiCharCode) {
2928 return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002929 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002930 return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
2931 static_cast<char>(pat->Get(0)),
2932 start_index));
2933 } else {
2934 return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
2935 pat->Get(0),
2936 start_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002937 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002938 }
2939
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002940 if (!pat->IsFlat()) {
2941 FlattenString(pat);
2942 }
2943
2944 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2945
2946 int position = -1;
2947
2948 if (pat->IsAsciiRepresentation()) {
2949 Vector<const char> pat_vector = pat->ToAsciiVector();
2950 if (sub->IsAsciiRepresentation()) {
2951 position = StringMatchBackwards(sub->ToAsciiVector(),
2952 pat_vector,
2953 start_index);
2954 } else {
2955 position = StringMatchBackwards(sub->ToUC16Vector(),
2956 pat_vector,
2957 start_index);
2958 }
2959 } else {
2960 Vector<const uc16> pat_vector = pat->ToUC16Vector();
2961 if (sub->IsAsciiRepresentation()) {
2962 position = StringMatchBackwards(sub->ToAsciiVector(),
2963 pat_vector,
2964 start_index);
2965 } else {
2966 position = StringMatchBackwards(sub->ToUC16Vector(),
2967 pat_vector,
2968 start_index);
2969 }
2970 }
2971
2972 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002973}
2974
2975
2976static Object* Runtime_StringLocaleCompare(Arguments args) {
2977 NoHandleAllocation ha;
2978 ASSERT(args.length() == 2);
2979
2980 CONVERT_CHECKED(String, str1, args[0]);
2981 CONVERT_CHECKED(String, str2, args[1]);
2982
2983 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002984 int str1_length = str1->length();
2985 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002986
2987 // Decide trivial cases without flattening.
2988 if (str1_length == 0) {
2989 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2990 return Smi::FromInt(-str2_length);
2991 } else {
2992 if (str2_length == 0) return Smi::FromInt(str1_length);
2993 }
2994
2995 int end = str1_length < str2_length ? str1_length : str2_length;
2996
2997 // No need to flatten if we are going to find the answer on the first
2998 // character. At this point we know there is at least one character
2999 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003000 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003001 if (d != 0) return Smi::FromInt(d);
3002
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003003 str1->TryFlatten();
3004 str2->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003005
3006 static StringInputBuffer buf1;
3007 static StringInputBuffer buf2;
3008
3009 buf1.Reset(str1);
3010 buf2.Reset(str2);
3011
3012 for (int i = 0; i < end; i++) {
3013 uint16_t char1 = buf1.GetNext();
3014 uint16_t char2 = buf2.GetNext();
3015 if (char1 != char2) return Smi::FromInt(char1 - char2);
3016 }
3017
3018 return Smi::FromInt(str1_length - str2_length);
3019}
3020
3021
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003022static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003023 NoHandleAllocation ha;
3024 ASSERT(args.length() == 3);
3025
3026 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00003027 Object* from = args[1];
3028 Object* to = args[2];
3029 int start, end;
3030 // We have a fast integer-only case here to avoid a conversion to double in
3031 // the common case where from and to are Smis.
3032 if (from->IsSmi() && to->IsSmi()) {
3033 start = Smi::cast(from)->value();
3034 end = Smi::cast(to)->value();
3035 } else {
3036 CONVERT_DOUBLE_CHECKED(from_number, from);
3037 CONVERT_DOUBLE_CHECKED(to_number, to);
3038 start = FastD2I(from_number);
3039 end = FastD2I(to_number);
3040 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003041 RUNTIME_ASSERT(end >= start);
3042 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003043 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003044 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003045 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003046}
3047
3048
ager@chromium.org41826e72009-03-30 13:30:57 +00003049static Object* Runtime_StringMatch(Arguments args) {
3050 ASSERT_EQ(3, args.length());
3051
3052 CONVERT_ARG_CHECKED(String, subject, 0);
3053 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
3054 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
3055 HandleScope handles;
3056
3057 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
3058
3059 if (match.is_null()) {
3060 return Failure::Exception();
3061 }
3062 if (match->IsNull()) {
3063 return Heap::null_value();
3064 }
3065 int length = subject->length();
3066
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00003067 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00003068 ZoneList<int> offsets(8);
3069 do {
3070 int start;
3071 int end;
3072 {
3073 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003074 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00003075 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
3076 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
3077 }
3078 offsets.Add(start);
3079 offsets.Add(end);
3080 int index = start < end ? end : end + 1;
3081 if (index > length) break;
3082 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
3083 if (match.is_null()) {
3084 return Failure::Exception();
3085 }
3086 } while (!match->IsNull());
3087 int matches = offsets.length() / 2;
3088 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
3089 for (int i = 0; i < matches ; i++) {
3090 int from = offsets.at(i * 2);
3091 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003092 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00003093 }
3094 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
3095 result->set_length(Smi::FromInt(matches));
3096 return *result;
3097}
3098
3099
lrn@chromium.org25156de2010-04-06 13:10:27 +00003100// Two smis before and after the match, for very long strings.
3101const int kMaxBuilderEntriesPerRegExpMatch = 5;
3102
3103
3104static void SetLastMatchInfoNoCaptures(Handle<String> subject,
3105 Handle<JSArray> last_match_info,
3106 int match_start,
3107 int match_end) {
3108 // Fill last_match_info with a single capture.
3109 last_match_info->EnsureSize(2 + RegExpImpl::kLastMatchOverhead);
3110 AssertNoAllocation no_gc;
3111 FixedArray* elements = FixedArray::cast(last_match_info->elements());
3112 RegExpImpl::SetLastCaptureCount(elements, 2);
3113 RegExpImpl::SetLastInput(elements, *subject);
3114 RegExpImpl::SetLastSubject(elements, *subject);
3115 RegExpImpl::SetCapture(elements, 0, match_start);
3116 RegExpImpl::SetCapture(elements, 1, match_end);
3117}
3118
3119
3120template <typename schar>
3121static bool SearchCharMultiple(Vector<schar> subject,
3122 String* pattern,
3123 schar pattern_char,
3124 FixedArrayBuilder* builder,
3125 int* match_pos) {
3126 // Position of last match.
3127 int pos = *match_pos;
3128 int subject_length = subject.length();
3129 while (pos < subject_length) {
3130 int match_end = pos + 1;
3131 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3132 *match_pos = pos;
3133 return false;
3134 }
3135 int new_pos = SingleCharIndexOf(subject, pattern_char, match_end);
3136 if (new_pos >= 0) {
3137 // Match has been found.
3138 if (new_pos > match_end) {
3139 ReplacementStringBuilder::AddSubjectSlice(builder, match_end, new_pos);
3140 }
3141 pos = new_pos;
3142 builder->Add(pattern);
3143 } else {
3144 break;
3145 }
3146 }
3147 if (pos + 1 < subject_length) {
3148 ReplacementStringBuilder::AddSubjectSlice(builder, pos + 1, subject_length);
3149 }
3150 *match_pos = pos;
3151 return true;
3152}
3153
3154
3155static bool SearchCharMultiple(Handle<String> subject,
3156 Handle<String> pattern,
3157 Handle<JSArray> last_match_info,
3158 FixedArrayBuilder* builder) {
3159 ASSERT(subject->IsFlat());
3160 ASSERT_EQ(1, pattern->length());
3161 uc16 pattern_char = pattern->Get(0);
3162 // Treating position before first as initial "previous match position".
3163 int match_pos = -1;
3164
3165 for (;;) { // Break when search complete.
3166 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3167 AssertNoAllocation no_gc;
3168 if (subject->IsAsciiRepresentation()) {
3169 if (pattern_char > String::kMaxAsciiCharCode) {
3170 break;
3171 }
3172 Vector<const char> subject_vector = subject->ToAsciiVector();
3173 char pattern_ascii_char = static_cast<char>(pattern_char);
3174 bool complete = SearchCharMultiple<const char>(subject_vector,
3175 *pattern,
3176 pattern_ascii_char,
3177 builder,
3178 &match_pos);
3179 if (complete) break;
3180 } else {
3181 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3182 bool complete = SearchCharMultiple<const uc16>(subject_vector,
3183 *pattern,
3184 pattern_char,
3185 builder,
3186 &match_pos);
3187 if (complete) break;
3188 }
3189 }
3190
3191 if (match_pos >= 0) {
3192 SetLastMatchInfoNoCaptures(subject,
3193 last_match_info,
3194 match_pos,
3195 match_pos + 1);
3196 return true;
3197 }
3198 return false; // No matches at all.
3199}
3200
3201
3202template <typename schar, typename pchar>
3203static bool SearchStringMultiple(Vector<schar> subject,
3204 String* pattern,
3205 Vector<pchar> pattern_string,
3206 FixedArrayBuilder* builder,
3207 int* match_pos) {
3208 int pos = *match_pos;
3209 int subject_length = subject.length();
3210 int pattern_length = pattern_string.length();
3211 int max_search_start = subject_length - pattern_length;
3212 bool is_ascii = (sizeof(schar) == 1);
3213 StringSearchStrategy strategy =
3214 InitializeStringSearch(pattern_string, is_ascii);
3215 switch (strategy) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003216 case SEARCH_FAIL: break;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003217 case SEARCH_SHORT:
3218 while (pos <= max_search_start) {
3219 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3220 *match_pos = pos;
3221 return false;
3222 }
3223 // Position of end of previous match.
3224 int match_end = pos + pattern_length;
3225 int new_pos = SimpleIndexOf(subject, pattern_string, match_end);
3226 if (new_pos >= 0) {
3227 // A match.
3228 if (new_pos > match_end) {
3229 ReplacementStringBuilder::AddSubjectSlice(builder,
3230 match_end,
3231 new_pos);
3232 }
3233 pos = new_pos;
3234 builder->Add(pattern);
3235 } else {
3236 break;
3237 }
3238 }
3239 break;
3240 case SEARCH_LONG:
3241 while (pos <= max_search_start) {
3242 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003243 *match_pos = pos;
3244 return false;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003245 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003246 int match_end = pos + pattern_length;
3247 int new_pos = ComplexIndexOf(subject, pattern_string, match_end);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003248 if (new_pos >= 0) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003249 // A match has been found.
3250 if (new_pos > match_end) {
3251 ReplacementStringBuilder::AddSubjectSlice(builder,
3252 match_end,
3253 new_pos);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003254 }
3255 pos = new_pos;
3256 builder->Add(pattern);
3257 } else {
3258 break;
3259 }
3260 }
3261 break;
3262 }
3263 if (pos < max_search_start) {
3264 ReplacementStringBuilder::AddSubjectSlice(builder,
3265 pos + pattern_length,
3266 subject_length);
3267 }
3268 *match_pos = pos;
3269 return true;
3270}
3271
3272
3273static bool SearchStringMultiple(Handle<String> subject,
3274 Handle<String> pattern,
3275 Handle<JSArray> last_match_info,
3276 FixedArrayBuilder* builder) {
3277 ASSERT(subject->IsFlat());
3278 ASSERT(pattern->IsFlat());
3279 ASSERT(pattern->length() > 1);
3280
3281 // Treating as if a previous match was before first character.
3282 int match_pos = -pattern->length();
3283
3284 for (;;) { // Break when search complete.
3285 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3286 AssertNoAllocation no_gc;
3287 if (subject->IsAsciiRepresentation()) {
3288 Vector<const char> subject_vector = subject->ToAsciiVector();
3289 if (pattern->IsAsciiRepresentation()) {
3290 if (SearchStringMultiple(subject_vector,
3291 *pattern,
3292 pattern->ToAsciiVector(),
3293 builder,
3294 &match_pos)) break;
3295 } else {
3296 if (SearchStringMultiple(subject_vector,
3297 *pattern,
3298 pattern->ToUC16Vector(),
3299 builder,
3300 &match_pos)) break;
3301 }
3302 } else {
3303 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3304 if (pattern->IsAsciiRepresentation()) {
3305 if (SearchStringMultiple(subject_vector,
3306 *pattern,
3307 pattern->ToAsciiVector(),
3308 builder,
3309 &match_pos)) break;
3310 } else {
3311 if (SearchStringMultiple(subject_vector,
3312 *pattern,
3313 pattern->ToUC16Vector(),
3314 builder,
3315 &match_pos)) break;
3316 }
3317 }
3318 }
3319
3320 if (match_pos >= 0) {
3321 SetLastMatchInfoNoCaptures(subject,
3322 last_match_info,
3323 match_pos,
3324 match_pos + pattern->length());
3325 return true;
3326 }
3327 return false; // No matches at all.
3328}
3329
3330
3331static RegExpImpl::IrregexpResult SearchRegExpNoCaptureMultiple(
3332 Handle<String> subject,
3333 Handle<JSRegExp> regexp,
3334 Handle<JSArray> last_match_array,
3335 FixedArrayBuilder* builder) {
3336 ASSERT(subject->IsFlat());
3337 int match_start = -1;
3338 int match_end = 0;
3339 int pos = 0;
3340 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3341 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3342
3343 OffsetsVector registers(required_registers);
3344 Vector<int> register_vector(registers.vector(), registers.length());
3345 int subject_length = subject->length();
3346
3347 for (;;) { // Break on failure, return on exception.
3348 RegExpImpl::IrregexpResult result =
3349 RegExpImpl::IrregexpExecOnce(regexp,
3350 subject,
3351 pos,
3352 register_vector);
3353 if (result == RegExpImpl::RE_SUCCESS) {
3354 match_start = register_vector[0];
3355 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3356 if (match_end < match_start) {
3357 ReplacementStringBuilder::AddSubjectSlice(builder,
3358 match_end,
3359 match_start);
3360 }
3361 match_end = register_vector[1];
3362 HandleScope loop_scope;
3363 builder->Add(*Factory::NewSubString(subject, match_start, match_end));
3364 if (match_start != match_end) {
3365 pos = match_end;
3366 } else {
3367 pos = match_end + 1;
3368 if (pos > subject_length) break;
3369 }
3370 } else if (result == RegExpImpl::RE_FAILURE) {
3371 break;
3372 } else {
3373 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3374 return result;
3375 }
3376 }
3377
3378 if (match_start >= 0) {
3379 if (match_end < subject_length) {
3380 ReplacementStringBuilder::AddSubjectSlice(builder,
3381 match_end,
3382 subject_length);
3383 }
3384 SetLastMatchInfoNoCaptures(subject,
3385 last_match_array,
3386 match_start,
3387 match_end);
3388 return RegExpImpl::RE_SUCCESS;
3389 } else {
3390 return RegExpImpl::RE_FAILURE; // No matches at all.
3391 }
3392}
3393
3394
3395static RegExpImpl::IrregexpResult SearchRegExpMultiple(
3396 Handle<String> subject,
3397 Handle<JSRegExp> regexp,
3398 Handle<JSArray> last_match_array,
3399 FixedArrayBuilder* builder) {
3400
3401 ASSERT(subject->IsFlat());
3402 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3403 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3404
3405 OffsetsVector registers(required_registers);
3406 Vector<int> register_vector(registers.vector(), registers.length());
3407
3408 RegExpImpl::IrregexpResult result =
3409 RegExpImpl::IrregexpExecOnce(regexp,
3410 subject,
3411 0,
3412 register_vector);
3413
3414 int capture_count = regexp->CaptureCount();
3415 int subject_length = subject->length();
3416
3417 // Position to search from.
3418 int pos = 0;
3419 // End of previous match. Differs from pos if match was empty.
3420 int match_end = 0;
3421 if (result == RegExpImpl::RE_SUCCESS) {
3422 // Need to keep a copy of the previous match for creating last_match_info
3423 // at the end, so we have two vectors that we swap between.
3424 OffsetsVector registers2(required_registers);
3425 Vector<int> prev_register_vector(registers2.vector(), registers2.length());
3426
3427 do {
3428 int match_start = register_vector[0];
3429 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3430 if (match_end < match_start) {
3431 ReplacementStringBuilder::AddSubjectSlice(builder,
3432 match_end,
3433 match_start);
3434 }
3435 match_end = register_vector[1];
3436
3437 {
3438 // Avoid accumulating new handles inside loop.
3439 HandleScope temp_scope;
3440 // Arguments array to replace function is match, captures, index and
3441 // subject, i.e., 3 + capture count in total.
3442 Handle<FixedArray> elements = Factory::NewFixedArray(3 + capture_count);
3443 elements->set(0, *Factory::NewSubString(subject,
3444 match_start,
3445 match_end));
3446 for (int i = 1; i <= capture_count; i++) {
3447 int start = register_vector[i * 2];
3448 if (start >= 0) {
3449 int end = register_vector[i * 2 + 1];
3450 ASSERT(start <= end);
3451 Handle<String> substring = Factory::NewSubString(subject,
3452 start,
3453 end);
3454 elements->set(i, *substring);
3455 } else {
3456 ASSERT(register_vector[i * 2 + 1] < 0);
3457 elements->set(i, Heap::undefined_value());
3458 }
3459 }
3460 elements->set(capture_count + 1, Smi::FromInt(match_start));
3461 elements->set(capture_count + 2, *subject);
3462 builder->Add(*Factory::NewJSArrayWithElements(elements));
3463 }
3464 // Swap register vectors, so the last successful match is in
3465 // prev_register_vector.
3466 Vector<int> tmp = prev_register_vector;
3467 prev_register_vector = register_vector;
3468 register_vector = tmp;
3469
3470 if (match_end > match_start) {
3471 pos = match_end;
3472 } else {
3473 pos = match_end + 1;
3474 if (pos > subject_length) {
3475 break;
3476 }
3477 }
3478
3479 result = RegExpImpl::IrregexpExecOnce(regexp,
3480 subject,
3481 pos,
3482 register_vector);
3483 } while (result == RegExpImpl::RE_SUCCESS);
3484
3485 if (result != RegExpImpl::RE_EXCEPTION) {
3486 // Finished matching, with at least one match.
3487 if (match_end < subject_length) {
3488 ReplacementStringBuilder::AddSubjectSlice(builder,
3489 match_end,
3490 subject_length);
3491 }
3492
3493 int last_match_capture_count = (capture_count + 1) * 2;
3494 int last_match_array_size =
3495 last_match_capture_count + RegExpImpl::kLastMatchOverhead;
3496 last_match_array->EnsureSize(last_match_array_size);
3497 AssertNoAllocation no_gc;
3498 FixedArray* elements = FixedArray::cast(last_match_array->elements());
3499 RegExpImpl::SetLastCaptureCount(elements, last_match_capture_count);
3500 RegExpImpl::SetLastSubject(elements, *subject);
3501 RegExpImpl::SetLastInput(elements, *subject);
3502 for (int i = 0; i < last_match_capture_count; i++) {
3503 RegExpImpl::SetCapture(elements, i, prev_register_vector[i]);
3504 }
3505 return RegExpImpl::RE_SUCCESS;
3506 }
3507 }
3508 // No matches at all, return failure or exception result directly.
3509 return result;
3510}
3511
3512
3513static Object* Runtime_RegExpExecMultiple(Arguments args) {
3514 ASSERT(args.length() == 4);
3515 HandleScope handles;
3516
3517 CONVERT_ARG_CHECKED(String, subject, 1);
3518 if (!subject->IsFlat()) { FlattenString(subject); }
3519 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
3520 CONVERT_ARG_CHECKED(JSArray, last_match_info, 2);
3521 CONVERT_ARG_CHECKED(JSArray, result_array, 3);
3522
3523 ASSERT(last_match_info->HasFastElements());
3524 ASSERT(regexp->GetFlags().is_global());
3525 Handle<FixedArray> result_elements;
3526 if (result_array->HasFastElements()) {
3527 result_elements =
3528 Handle<FixedArray>(FixedArray::cast(result_array->elements()));
3529 } else {
3530 result_elements = Factory::NewFixedArrayWithHoles(16);
3531 }
3532 FixedArrayBuilder builder(result_elements);
3533
3534 if (regexp->TypeTag() == JSRegExp::ATOM) {
3535 Handle<String> pattern(
3536 String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex)));
3537 int pattern_length = pattern->length();
3538 if (pattern_length == 1) {
3539 if (SearchCharMultiple(subject, pattern, last_match_info, &builder)) {
3540 return *builder.ToJSArray(result_array);
3541 }
3542 return Heap::null_value();
3543 }
3544
3545 if (!pattern->IsFlat()) FlattenString(pattern);
3546 if (SearchStringMultiple(subject, pattern, last_match_info, &builder)) {
3547 return *builder.ToJSArray(result_array);
3548 }
3549 return Heap::null_value();
3550 }
3551
3552 ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
3553
3554 RegExpImpl::IrregexpResult result;
3555 if (regexp->CaptureCount() == 0) {
3556 result = SearchRegExpNoCaptureMultiple(subject,
3557 regexp,
3558 last_match_info,
3559 &builder);
3560 } else {
3561 result = SearchRegExpMultiple(subject, regexp, last_match_info, &builder);
3562 }
3563 if (result == RegExpImpl::RE_SUCCESS) return *builder.ToJSArray(result_array);
3564 if (result == RegExpImpl::RE_FAILURE) return Heap::null_value();
3565 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3566 return Failure::Exception();
3567}
3568
3569
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003570static Object* Runtime_NumberToRadixString(Arguments args) {
3571 NoHandleAllocation ha;
3572 ASSERT(args.length() == 2);
3573
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003574 // Fast case where the result is a one character string.
3575 if (args[0]->IsSmi() && args[1]->IsSmi()) {
3576 int value = Smi::cast(args[0])->value();
3577 int radix = Smi::cast(args[1])->value();
3578 if (value >= 0 && value < radix) {
3579 RUNTIME_ASSERT(radix <= 36);
3580 // Character array used for conversion.
3581 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
3582 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
3583 }
3584 }
3585
3586 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003587 CONVERT_DOUBLE_CHECKED(value, args[0]);
3588 if (isnan(value)) {
3589 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3590 }
3591 if (isinf(value)) {
3592 if (value < 0) {
3593 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3594 }
3595 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3596 }
3597 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
3598 int radix = FastD2I(radix_number);
3599 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3600 char* str = DoubleToRadixCString(value, radix);
3601 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3602 DeleteArray(str);
3603 return result;
3604}
3605
3606
3607static Object* Runtime_NumberToFixed(Arguments args) {
3608 NoHandleAllocation ha;
3609 ASSERT(args.length() == 2);
3610
3611 CONVERT_DOUBLE_CHECKED(value, args[0]);
3612 if (isnan(value)) {
3613 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3614 }
3615 if (isinf(value)) {
3616 if (value < 0) {
3617 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3618 }
3619 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3620 }
3621 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3622 int f = FastD2I(f_number);
3623 RUNTIME_ASSERT(f >= 0);
3624 char* str = DoubleToFixedCString(value, f);
3625 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3626 DeleteArray(str);
3627 return res;
3628}
3629
3630
3631static Object* Runtime_NumberToExponential(Arguments args) {
3632 NoHandleAllocation ha;
3633 ASSERT(args.length() == 2);
3634
3635 CONVERT_DOUBLE_CHECKED(value, args[0]);
3636 if (isnan(value)) {
3637 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3638 }
3639 if (isinf(value)) {
3640 if (value < 0) {
3641 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3642 }
3643 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3644 }
3645 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3646 int f = FastD2I(f_number);
3647 RUNTIME_ASSERT(f >= -1 && f <= 20);
3648 char* str = DoubleToExponentialCString(value, f);
3649 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3650 DeleteArray(str);
3651 return res;
3652}
3653
3654
3655static Object* Runtime_NumberToPrecision(Arguments args) {
3656 NoHandleAllocation ha;
3657 ASSERT(args.length() == 2);
3658
3659 CONVERT_DOUBLE_CHECKED(value, args[0]);
3660 if (isnan(value)) {
3661 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3662 }
3663 if (isinf(value)) {
3664 if (value < 0) {
3665 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3666 }
3667 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3668 }
3669 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3670 int f = FastD2I(f_number);
3671 RUNTIME_ASSERT(f >= 1 && f <= 21);
3672 char* str = DoubleToPrecisionCString(value, f);
3673 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3674 DeleteArray(str);
3675 return res;
3676}
3677
3678
3679// Returns a single character string where first character equals
3680// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003681static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003682 if (index < static_cast<uint32_t>(string->length())) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003683 string->TryFlatten();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003684 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003685 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003686 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003687 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003688}
3689
3690
3691Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
3692 // Handle [] indexing on Strings
3693 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003694 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
3695 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003696 }
3697
3698 // Handle [] indexing on String objects
3699 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003700 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
3701 Handle<Object> result =
3702 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
3703 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003704 }
3705
3706 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003707 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003708 return prototype->GetElement(index);
3709 }
3710
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003711 return GetElement(object, index);
3712}
3713
3714
3715Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003716 return object->GetElement(index);
3717}
3718
3719
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003720Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
3721 HandleScope scope;
3722
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003723 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003724 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003725 Handle<Object> error =
3726 Factory::NewTypeError("non_object_property_load",
3727 HandleVector(args, 2));
3728 return Top::Throw(*error);
3729 }
3730
3731 // Check if the given key is an array index.
3732 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003733 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003734 return GetElementOrCharAt(object, index);
3735 }
3736
3737 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003738 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003739 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003740 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003741 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003742 bool has_pending_exception = false;
3743 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003744 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003745 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003746 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003747 }
3748
ager@chromium.org32912102009-01-16 10:38:43 +00003749 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003750 // the element if so.
3751 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003752 return GetElementOrCharAt(object, index);
3753 } else {
3754 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003755 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003756 }
3757}
3758
3759
3760static Object* Runtime_GetProperty(Arguments args) {
3761 NoHandleAllocation ha;
3762 ASSERT(args.length() == 2);
3763
3764 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003765 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003766
3767 return Runtime::GetObjectProperty(object, key);
3768}
3769
3770
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003771// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003772static Object* Runtime_KeyedGetProperty(Arguments args) {
3773 NoHandleAllocation ha;
3774 ASSERT(args.length() == 2);
3775
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003776 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00003777 // itself.
3778 //
3779 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00003780 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00003781 // global proxy object never has properties. This is the case
3782 // because the global proxy object forwards everything to its hidden
3783 // prototype including local lookups.
3784 //
3785 // Additionally, we need to make sure that we do not cache results
3786 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003787 if (args[0]->IsJSObject() &&
3788 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00003789 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003790 args[1]->IsString()) {
3791 JSObject* receiver = JSObject::cast(args[0]);
3792 String* key = String::cast(args[1]);
3793 if (receiver->HasFastProperties()) {
3794 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003795 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003796 int offset = KeyedLookupCache::Lookup(receiver_map, key);
3797 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003798 Object* value = receiver->FastPropertyAt(offset);
3799 return value->IsTheHole() ? Heap::undefined_value() : value;
3800 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003801 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003802 LookupResult result;
3803 receiver->LocalLookup(key, &result);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00003804 if (result.IsProperty() && result.type() == FIELD) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003805 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003806 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003807 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003808 }
3809 } else {
3810 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003811 StringDictionary* dictionary = receiver->property_dictionary();
3812 int entry = dictionary->FindEntry(key);
3813 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003814 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003815 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003816 if (!receiver->IsGlobalObject()) return value;
3817 value = JSGlobalPropertyCell::cast(value)->value();
3818 if (!value->IsTheHole()) return value;
3819 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003820 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003821 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003822 } else if (args[0]->IsString() && args[1]->IsSmi()) {
3823 // Fast case for string indexing using [] with a smi index.
3824 HandleScope scope;
3825 Handle<String> str = args.at<String>(0);
3826 int index = Smi::cast(args[1])->value();
3827 Handle<Object> result = GetCharAt(str, index);
3828 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003829 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003830
3831 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003832 return Runtime::GetObjectProperty(args.at<Object>(0),
3833 args.at<Object>(1));
3834}
3835
3836
ager@chromium.org5c838252010-02-19 08:53:10 +00003837static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
3838 ASSERT(args.length() == 5);
3839 HandleScope scope;
3840 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3841 CONVERT_CHECKED(String, name, args[1]);
3842 CONVERT_CHECKED(Smi, flag_setter, args[2]);
3843 CONVERT_CHECKED(JSFunction, fun, args[3]);
3844 CONVERT_CHECKED(Smi, flag_attr, args[4]);
3845 int unchecked = flag_attr->value();
3846 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3847 RUNTIME_ASSERT(!obj->IsNull());
3848 LookupResult result;
3849 obj->LocalLookupRealNamedProperty(name, &result);
3850
3851 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3852 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
3853 // delete it to avoid running into trouble in DefineAccessor, which
3854 // handles this incorrectly if the property is readonly (does nothing)
3855 if (result.IsProperty() &&
3856 (result.type() == FIELD || result.type() == NORMAL
3857 || result.type() == CONSTANT_FUNCTION)) {
3858 obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
3859 }
3860 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
3861}
3862
3863static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
3864 ASSERT(args.length() == 4);
3865 HandleScope scope;
3866 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
3867 CONVERT_ARG_CHECKED(String, name, 1);
3868 Handle<Object> obj_value = args.at<Object>(2);
3869
3870 CONVERT_CHECKED(Smi, flag, args[3]);
3871 int unchecked = flag->value();
3872 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3873
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00003874 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3875
3876 // Check if this is an element.
3877 uint32_t index;
3878 bool is_element = name->AsArrayIndex(&index);
3879
3880 // Special case for elements if any of the flags are true.
3881 // If elements are in fast case we always implicitly assume that:
3882 // DONT_DELETE: false, DONT_ENUM: false, READ_ONLY: false.
3883 if (((unchecked & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) &&
3884 is_element) {
3885 // Normalize the elements to enable attributes on the property.
3886 js_object->NormalizeElements();
3887 NumberDictionary* dictionary = js_object->element_dictionary();
3888 // Make sure that we never go back to fast case.
3889 dictionary->set_requires_slow_elements();
3890 PropertyDetails details = PropertyDetails(attr, NORMAL);
3891 dictionary->Set(index, *obj_value, details);
3892 }
3893
ager@chromium.org5c838252010-02-19 08:53:10 +00003894 LookupResult result;
3895 js_object->LocalLookupRealNamedProperty(*name, &result);
3896
ager@chromium.org5c838252010-02-19 08:53:10 +00003897 // Take special care when attributes are different and there is already
3898 // a property. For simplicity we normalize the property which enables us
3899 // to not worry about changing the instance_descriptor and creating a new
3900 // map. The current version of SetObjectProperty does not handle attributes
3901 // correctly in the case where a property is a field and is reset with
3902 // new attributes.
3903 if (result.IsProperty() && attr != result.GetAttributes()) {
3904 // New attributes - normalize to avoid writing to instance descriptor
3905 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3906 // Use IgnoreAttributes version since a readonly property may be
3907 // overridden and SetProperty does not allow this.
3908 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
3909 *obj_value,
3910 attr);
3911 }
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00003912
ager@chromium.org5c838252010-02-19 08:53:10 +00003913 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
3914}
3915
3916
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003917Object* Runtime::SetObjectProperty(Handle<Object> object,
3918 Handle<Object> key,
3919 Handle<Object> value,
3920 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003921 HandleScope scope;
3922
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003923 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003924 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003925 Handle<Object> error =
3926 Factory::NewTypeError("non_object_property_store",
3927 HandleVector(args, 2));
3928 return Top::Throw(*error);
3929 }
3930
3931 // If the object isn't a JavaScript object, we ignore the store.
3932 if (!object->IsJSObject()) return *value;
3933
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003934 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3935
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003936 // Check if the given key is an array index.
3937 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003938 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003939 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3940 // of a string using [] notation. We need to support this too in
3941 // JavaScript.
3942 // In the case of a String object we just need to redirect the assignment to
3943 // the underlying string if the index is in range. Since the underlying
3944 // string does nothing with the assignment then we can ignore such
3945 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003946 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003947 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003948 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003949
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003950 Handle<Object> result = SetElement(js_object, index, value);
3951 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003952 return *value;
3953 }
3954
3955 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003956 Handle<Object> result;
3957 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003958 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003959 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003960 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003961 key_string->TryFlatten();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003962 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003963 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003964 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003965 return *value;
3966 }
3967
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003968 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003969 bool has_pending_exception = false;
3970 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3971 if (has_pending_exception) return Failure::Exception();
3972 Handle<String> name = Handle<String>::cast(converted);
3973
3974 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003975 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003976 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003977 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003978 }
3979}
3980
3981
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003982Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
3983 Handle<Object> key,
3984 Handle<Object> value,
3985 PropertyAttributes attr) {
3986 HandleScope scope;
3987
3988 // Check if the given key is an array index.
3989 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003990 if (key->ToArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003991 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3992 // of a string using [] notation. We need to support this too in
3993 // JavaScript.
3994 // In the case of a String object we just need to redirect the assignment to
3995 // the underlying string if the index is in range. Since the underlying
3996 // string does nothing with the assignment then we can ignore such
3997 // assignments.
3998 if (js_object->IsStringObjectWithCharacterAt(index)) {
3999 return *value;
4000 }
4001
4002 return js_object->SetElement(index, *value);
4003 }
4004
4005 if (key->IsString()) {
4006 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004007 return js_object->SetElement(index, *value);
4008 } else {
4009 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004010 key_string->TryFlatten();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004011 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
4012 *value,
4013 attr);
4014 }
4015 }
4016
4017 // Call-back into JavaScript to convert the key to a string.
4018 bool has_pending_exception = false;
4019 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4020 if (has_pending_exception) return Failure::Exception();
4021 Handle<String> name = Handle<String>::cast(converted);
4022
4023 if (name->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004024 return js_object->SetElement(index, *value);
4025 } else {
4026 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
4027 }
4028}
4029
4030
ager@chromium.orge2902be2009-06-08 12:21:35 +00004031Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
4032 Handle<Object> key) {
4033 HandleScope scope;
4034
4035 // Check if the given key is an array index.
4036 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004037 if (key->ToArrayIndex(&index)) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00004038 // In Firefox/SpiderMonkey, Safari and Opera you can access the
4039 // characters of a string using [] notation. In the case of a
4040 // String object we just need to redirect the deletion to the
4041 // underlying string if the index is in range. Since the
4042 // underlying string does nothing with the deletion, we can ignore
4043 // such deletions.
4044 if (js_object->IsStringObjectWithCharacterAt(index)) {
4045 return Heap::true_value();
4046 }
4047
4048 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
4049 }
4050
4051 Handle<String> key_string;
4052 if (key->IsString()) {
4053 key_string = Handle<String>::cast(key);
4054 } else {
4055 // Call-back into JavaScript to convert the key to a string.
4056 bool has_pending_exception = false;
4057 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4058 if (has_pending_exception) return Failure::Exception();
4059 key_string = Handle<String>::cast(converted);
4060 }
4061
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004062 key_string->TryFlatten();
ager@chromium.orge2902be2009-06-08 12:21:35 +00004063 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
4064}
4065
4066
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004067static Object* Runtime_SetProperty(Arguments args) {
4068 NoHandleAllocation ha;
4069 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
4070
4071 Handle<Object> object = args.at<Object>(0);
4072 Handle<Object> key = args.at<Object>(1);
4073 Handle<Object> value = args.at<Object>(2);
4074
4075 // Compute attributes.
4076 PropertyAttributes attributes = NONE;
4077 if (args.length() == 4) {
4078 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004079 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004080 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004081 RUNTIME_ASSERT(
4082 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4083 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004084 }
4085 return Runtime::SetObjectProperty(object, key, value, attributes);
4086}
4087
4088
4089// Set a local property, even if it is READ_ONLY. If the property does not
4090// exist, it will be added with attributes NONE.
4091static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
4092 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004093 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004094 CONVERT_CHECKED(JSObject, object, args[0]);
4095 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004096 // Compute attributes.
4097 PropertyAttributes attributes = NONE;
4098 if (args.length() == 4) {
4099 CONVERT_CHECKED(Smi, value_obj, args[3]);
4100 int unchecked_value = value_obj->value();
4101 // Only attribute bits should be set.
4102 RUNTIME_ASSERT(
4103 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4104 attributes = static_cast<PropertyAttributes>(unchecked_value);
4105 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004106
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004107 return object->
4108 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004109}
4110
4111
4112static Object* Runtime_DeleteProperty(Arguments args) {
4113 NoHandleAllocation ha;
4114 ASSERT(args.length() == 2);
4115
4116 CONVERT_CHECKED(JSObject, object, args[0]);
4117 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00004118 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004119}
4120
4121
ager@chromium.org9085a012009-05-11 19:22:57 +00004122static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
4123 Handle<String> key) {
4124 if (object->HasLocalProperty(*key)) return Heap::true_value();
4125 // Handle hidden prototypes. If there's a hidden prototype above this thing
4126 // then we have to check it for properties, because they are supposed to
4127 // look like they are on this object.
4128 Handle<Object> proto(object->GetPrototype());
4129 if (proto->IsJSObject() &&
4130 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
4131 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
4132 }
4133 return Heap::false_value();
4134}
4135
4136
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004137static Object* Runtime_HasLocalProperty(Arguments args) {
4138 NoHandleAllocation ha;
4139 ASSERT(args.length() == 2);
4140 CONVERT_CHECKED(String, key, args[1]);
4141
ager@chromium.org9085a012009-05-11 19:22:57 +00004142 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004143 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00004144 if (obj->IsJSObject()) {
4145 JSObject* object = JSObject::cast(obj);
4146 // Fast case - no interceptors.
4147 if (object->HasRealNamedProperty(key)) return Heap::true_value();
4148 // Slow case. Either it's not there or we have an interceptor. We should
4149 // have handles for this kind of deal.
4150 HandleScope scope;
4151 return HasLocalPropertyImplementation(Handle<JSObject>(object),
4152 Handle<String>(key));
4153 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004154 // Well, there is one exception: Handle [] on strings.
4155 uint32_t index;
4156 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00004157 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004158 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004159 return Heap::true_value();
4160 }
4161 }
4162 return Heap::false_value();
4163}
4164
4165
4166static Object* Runtime_HasProperty(Arguments args) {
4167 NoHandleAllocation na;
4168 ASSERT(args.length() == 2);
4169
4170 // Only JS objects can have properties.
4171 if (args[0]->IsJSObject()) {
4172 JSObject* object = JSObject::cast(args[0]);
4173 CONVERT_CHECKED(String, key, args[1]);
4174 if (object->HasProperty(key)) return Heap::true_value();
4175 }
4176 return Heap::false_value();
4177}
4178
4179
4180static Object* Runtime_HasElement(Arguments args) {
4181 NoHandleAllocation na;
4182 ASSERT(args.length() == 2);
4183
4184 // Only JS objects can have elements.
4185 if (args[0]->IsJSObject()) {
4186 JSObject* object = JSObject::cast(args[0]);
4187 CONVERT_CHECKED(Smi, index_obj, args[1]);
4188 uint32_t index = index_obj->value();
4189 if (object->HasElement(index)) return Heap::true_value();
4190 }
4191 return Heap::false_value();
4192}
4193
4194
4195static Object* Runtime_IsPropertyEnumerable(Arguments args) {
4196 NoHandleAllocation ha;
4197 ASSERT(args.length() == 2);
4198
4199 CONVERT_CHECKED(JSObject, object, args[0]);
4200 CONVERT_CHECKED(String, key, args[1]);
4201
4202 uint32_t index;
4203 if (key->AsArrayIndex(&index)) {
4204 return Heap::ToBoolean(object->HasElement(index));
4205 }
4206
ager@chromium.org870a0b62008-11-04 11:43:05 +00004207 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
4208 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004209}
4210
4211
4212static Object* Runtime_GetPropertyNames(Arguments args) {
4213 HandleScope scope;
4214 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004215 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004216 return *GetKeysFor(object);
4217}
4218
4219
4220// Returns either a FixedArray as Runtime_GetPropertyNames,
4221// or, if the given object has an enum cache that contains
4222// all enumerable properties of the object and its prototypes
4223// have none, the map of the object. This is used to speed up
4224// the check for deletions during a for-in.
4225static Object* Runtime_GetPropertyNamesFast(Arguments args) {
4226 ASSERT(args.length() == 1);
4227
4228 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4229
4230 if (raw_object->IsSimpleEnum()) return raw_object->map();
4231
4232 HandleScope scope;
4233 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004234 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
4235 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004236
4237 // Test again, since cache may have been built by preceding call.
4238 if (object->IsSimpleEnum()) return object->map();
4239
4240 return *content;
4241}
4242
4243
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004244// Find the length of the prototype chain that is to to handled as one. If a
4245// prototype object is hidden it is to be viewed as part of the the object it
4246// is prototype for.
4247static int LocalPrototypeChainLength(JSObject* obj) {
4248 int count = 1;
4249 Object* proto = obj->GetPrototype();
4250 while (proto->IsJSObject() &&
4251 JSObject::cast(proto)->map()->is_hidden_prototype()) {
4252 count++;
4253 proto = JSObject::cast(proto)->GetPrototype();
4254 }
4255 return count;
4256}
4257
4258
4259// Return the names of the local named properties.
4260// args[0]: object
4261static Object* Runtime_GetLocalPropertyNames(Arguments args) {
4262 HandleScope scope;
4263 ASSERT(args.length() == 1);
4264 if (!args[0]->IsJSObject()) {
4265 return Heap::undefined_value();
4266 }
4267 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4268
4269 // Skip the global proxy as it has no properties and always delegates to the
4270 // real global object.
4271 if (obj->IsJSGlobalProxy()) {
4272 // Only collect names if access is permitted.
4273 if (obj->IsAccessCheckNeeded() &&
4274 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
4275 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
4276 return *Factory::NewJSArray(0);
4277 }
4278 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
4279 }
4280
4281 // Find the number of objects making up this.
4282 int length = LocalPrototypeChainLength(*obj);
4283
4284 // Find the number of local properties for each of the objects.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004285 ScopedVector<int> local_property_count(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004286 int total_property_count = 0;
4287 Handle<JSObject> jsproto = obj;
4288 for (int i = 0; i < length; i++) {
4289 // Only collect names if access is permitted.
4290 if (jsproto->IsAccessCheckNeeded() &&
4291 !Top::MayNamedAccess(*jsproto,
4292 Heap::undefined_value(),
4293 v8::ACCESS_KEYS)) {
4294 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
4295 return *Factory::NewJSArray(0);
4296 }
4297 int n;
4298 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4299 local_property_count[i] = n;
4300 total_property_count += n;
4301 if (i < length - 1) {
4302 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4303 }
4304 }
4305
4306 // Allocate an array with storage for all the property names.
4307 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
4308
4309 // Get the property names.
4310 jsproto = obj;
4311 int proto_with_hidden_properties = 0;
4312 for (int i = 0; i < length; i++) {
4313 jsproto->GetLocalPropertyNames(*names,
4314 i == 0 ? 0 : local_property_count[i - 1]);
4315 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
4316 proto_with_hidden_properties++;
4317 }
4318 if (i < length - 1) {
4319 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4320 }
4321 }
4322
4323 // Filter out name of hidden propeties object.
4324 if (proto_with_hidden_properties > 0) {
4325 Handle<FixedArray> old_names = names;
4326 names = Factory::NewFixedArray(
4327 names->length() - proto_with_hidden_properties);
4328 int dest_pos = 0;
4329 for (int i = 0; i < total_property_count; i++) {
4330 Object* name = old_names->get(i);
4331 if (name == Heap::hidden_symbol()) {
4332 continue;
4333 }
4334 names->set(dest_pos++, name);
4335 }
4336 }
4337
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004338 return *Factory::NewJSArrayWithElements(names);
4339}
4340
4341
4342// Return the names of the local indexed properties.
4343// args[0]: object
4344static Object* Runtime_GetLocalElementNames(Arguments args) {
4345 HandleScope scope;
4346 ASSERT(args.length() == 1);
4347 if (!args[0]->IsJSObject()) {
4348 return Heap::undefined_value();
4349 }
4350 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4351
4352 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4353 Handle<FixedArray> names = Factory::NewFixedArray(n);
4354 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4355 return *Factory::NewJSArrayWithElements(names);
4356}
4357
4358
4359// Return information on whether an object has a named or indexed interceptor.
4360// args[0]: object
4361static Object* Runtime_GetInterceptorInfo(Arguments args) {
4362 HandleScope scope;
4363 ASSERT(args.length() == 1);
4364 if (!args[0]->IsJSObject()) {
4365 return Smi::FromInt(0);
4366 }
4367 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4368
4369 int result = 0;
4370 if (obj->HasNamedInterceptor()) result |= 2;
4371 if (obj->HasIndexedInterceptor()) result |= 1;
4372
4373 return Smi::FromInt(result);
4374}
4375
4376
4377// Return property names from named interceptor.
4378// args[0]: object
4379static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
4380 HandleScope scope;
4381 ASSERT(args.length() == 1);
4382 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4383
4384 if (obj->HasNamedInterceptor()) {
4385 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4386 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4387 }
4388 return Heap::undefined_value();
4389}
4390
4391
4392// Return element names from indexed interceptor.
4393// args[0]: object
4394static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
4395 HandleScope scope;
4396 ASSERT(args.length() == 1);
4397 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4398
4399 if (obj->HasIndexedInterceptor()) {
4400 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4401 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4402 }
4403 return Heap::undefined_value();
4404}
4405
4406
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004407static Object* Runtime_LocalKeys(Arguments args) {
4408 ASSERT_EQ(args.length(), 1);
4409 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4410 HandleScope scope;
4411 Handle<JSObject> object(raw_object);
4412 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
4413 LOCAL_ONLY);
4414 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
4415 // property array and since the result is mutable we have to create
4416 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004417 int length = contents->length();
4418 Handle<FixedArray> copy = Factory::NewFixedArray(length);
4419 for (int i = 0; i < length; i++) {
4420 Object* entry = contents->get(i);
4421 if (entry->IsString()) {
4422 copy->set(i, entry);
4423 } else {
4424 ASSERT(entry->IsNumber());
4425 HandleScope scope;
4426 Handle<Object> entry_handle(entry);
4427 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
4428 copy->set(i, *entry_str);
4429 }
4430 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004431 return *Factory::NewJSArrayWithElements(copy);
4432}
4433
4434
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004435static Object* Runtime_GetArgumentsProperty(Arguments args) {
4436 NoHandleAllocation ha;
4437 ASSERT(args.length() == 1);
4438
4439 // Compute the frame holding the arguments.
4440 JavaScriptFrameIterator it;
4441 it.AdvanceToArgumentsFrame();
4442 JavaScriptFrame* frame = it.frame();
4443
4444 // Get the actual number of provided arguments.
4445 const uint32_t n = frame->GetProvidedParametersCount();
4446
4447 // Try to convert the key to an index. If successful and within
4448 // index return the the argument from the frame.
4449 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004450 if (args[0]->ToArrayIndex(&index) && index < n) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004451 return frame->GetParameter(index);
4452 }
4453
4454 // Convert the key to a string.
4455 HandleScope scope;
4456 bool exception = false;
4457 Handle<Object> converted =
4458 Execution::ToString(args.at<Object>(0), &exception);
4459 if (exception) return Failure::Exception();
4460 Handle<String> key = Handle<String>::cast(converted);
4461
4462 // Try to convert the string key into an array index.
4463 if (key->AsArrayIndex(&index)) {
4464 if (index < n) {
4465 return frame->GetParameter(index);
4466 } else {
4467 return Top::initial_object_prototype()->GetElement(index);
4468 }
4469 }
4470
4471 // Handle special arguments properties.
4472 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
4473 if (key->Equals(Heap::callee_symbol())) return frame->function();
4474
4475 // Lookup in the initial Object.prototype object.
4476 return Top::initial_object_prototype()->GetProperty(*key);
4477}
4478
4479
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004480static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004481 HandleScope scope;
4482
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004483 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004484 Handle<Object> object = args.at<Object>(0);
4485 if (object->IsJSObject()) {
4486 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00004487 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
4488 js_object->TransformToFastProperties(0);
4489 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004490 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004491 return *object;
4492}
4493
4494
4495static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004496 HandleScope scope;
4497
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004498 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004499 Handle<Object> object = args.at<Object>(0);
4500 if (object->IsJSObject()) {
4501 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004502 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004503 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004504 return *object;
4505}
4506
4507
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004508static Object* Runtime_ToBool(Arguments args) {
4509 NoHandleAllocation ha;
4510 ASSERT(args.length() == 1);
4511
4512 return args[0]->ToBoolean();
4513}
4514
4515
4516// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
4517// Possible optimizations: put the type string into the oddballs.
4518static Object* Runtime_Typeof(Arguments args) {
4519 NoHandleAllocation ha;
4520
4521 Object* obj = args[0];
4522 if (obj->IsNumber()) return Heap::number_symbol();
4523 HeapObject* heap_obj = HeapObject::cast(obj);
4524
4525 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004526 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004527
4528 InstanceType instance_type = heap_obj->map()->instance_type();
4529 if (instance_type < FIRST_NONSTRING_TYPE) {
4530 return Heap::string_symbol();
4531 }
4532
4533 switch (instance_type) {
4534 case ODDBALL_TYPE:
4535 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
4536 return Heap::boolean_symbol();
4537 }
4538 if (heap_obj->IsNull()) {
4539 return Heap::object_symbol();
4540 }
4541 ASSERT(heap_obj->IsUndefined());
4542 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004543 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004544 return Heap::function_symbol();
4545 default:
4546 // For any kind of object not handled above, the spec rule for
4547 // host objects gives that it is okay to return "object"
4548 return Heap::object_symbol();
4549 }
4550}
4551
4552
lrn@chromium.org25156de2010-04-06 13:10:27 +00004553static bool AreDigits(const char*s, int from, int to) {
4554 for (int i = from; i < to; i++) {
4555 if (s[i] < '0' || s[i] > '9') return false;
4556 }
4557
4558 return true;
4559}
4560
4561
4562static int ParseDecimalInteger(const char*s, int from, int to) {
4563 ASSERT(to - from < 10); // Overflow is not possible.
4564 ASSERT(from < to);
4565 int d = s[from] - '0';
4566
4567 for (int i = from + 1; i < to; i++) {
4568 d = 10 * d + (s[i] - '0');
4569 }
4570
4571 return d;
4572}
4573
4574
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004575static Object* Runtime_StringToNumber(Arguments args) {
4576 NoHandleAllocation ha;
4577 ASSERT(args.length() == 1);
4578 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004579 subject->TryFlatten();
lrn@chromium.org25156de2010-04-06 13:10:27 +00004580
4581 // Fast case: short integer or some sorts of junk values.
4582 int len = subject->length();
4583 if (subject->IsSeqAsciiString()) {
4584 if (len == 0) return Smi::FromInt(0);
4585
4586 char const* data = SeqAsciiString::cast(subject)->GetChars();
4587 bool minus = (data[0] == '-');
4588 int start_pos = (minus ? 1 : 0);
4589
4590 if (start_pos == len) {
4591 return Heap::nan_value();
4592 } else if (data[start_pos] > '9') {
4593 // Fast check for a junk value. A valid string may start from a
4594 // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit or
4595 // the 'I' character ('Infinity'). All of that have codes not greater than
4596 // '9' except 'I'.
4597 if (data[start_pos] != 'I') {
4598 return Heap::nan_value();
4599 }
4600 } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
4601 // The maximal/minimal smi has 10 digits. If the string has less digits we
4602 // know it will fit into the smi-data type.
4603 int d = ParseDecimalInteger(data, start_pos, len);
4604 if (minus) {
4605 if (d == 0) return Heap::minus_zero_value();
4606 d = -d;
4607 }
4608 return Smi::FromInt(d);
4609 }
4610 }
4611
4612 // Slower case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004613 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
4614}
4615
4616
4617static Object* Runtime_StringFromCharCodeArray(Arguments args) {
4618 NoHandleAllocation ha;
4619 ASSERT(args.length() == 1);
4620
4621 CONVERT_CHECKED(JSArray, codes, args[0]);
4622 int length = Smi::cast(codes->length())->value();
4623
4624 // Check if the string can be ASCII.
4625 int i;
4626 for (i = 0; i < length; i++) {
4627 Object* element = codes->GetElement(i);
4628 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
4629 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
4630 break;
4631 }
4632
4633 Object* object = NULL;
4634 if (i == length) { // The string is ASCII.
4635 object = Heap::AllocateRawAsciiString(length);
4636 } else { // The string is not ASCII.
4637 object = Heap::AllocateRawTwoByteString(length);
4638 }
4639
4640 if (object->IsFailure()) return object;
4641 String* result = String::cast(object);
4642 for (int i = 0; i < length; i++) {
4643 Object* element = codes->GetElement(i);
4644 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004645 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004646 }
4647 return result;
4648}
4649
4650
4651// kNotEscaped is generated by the following:
4652//
4653// #!/bin/perl
4654// for (my $i = 0; $i < 256; $i++) {
4655// print "\n" if $i % 16 == 0;
4656// my $c = chr($i);
4657// my $escaped = 1;
4658// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
4659// print $escaped ? "0, " : "1, ";
4660// }
4661
4662
4663static bool IsNotEscaped(uint16_t character) {
4664 // Only for 8 bit characters, the rest are always escaped (in a different way)
4665 ASSERT(character < 256);
4666 static const char kNotEscaped[256] = {
4667 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4668 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4669 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
4670 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
4671 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4672 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
4673 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4674 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4678 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4679 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4680 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4681 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4682 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4683 };
4684 return kNotEscaped[character] != 0;
4685}
4686
4687
4688static Object* Runtime_URIEscape(Arguments args) {
4689 const char hex_chars[] = "0123456789ABCDEF";
4690 NoHandleAllocation ha;
4691 ASSERT(args.length() == 1);
4692 CONVERT_CHECKED(String, source, args[0]);
4693
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004694 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004695
4696 int escaped_length = 0;
4697 int length = source->length();
4698 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004699 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004700 buffer->Reset(source);
4701 while (buffer->has_more()) {
4702 uint16_t character = buffer->GetNext();
4703 if (character >= 256) {
4704 escaped_length += 6;
4705 } else if (IsNotEscaped(character)) {
4706 escaped_length++;
4707 } else {
4708 escaped_length += 3;
4709 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004710 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004711 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004712 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004713 Top::context()->mark_out_of_memory();
4714 return Failure::OutOfMemoryException();
4715 }
4716 }
4717 }
4718 // No length change implies no change. Return original string if no change.
4719 if (escaped_length == length) {
4720 return source;
4721 }
4722 Object* o = Heap::AllocateRawAsciiString(escaped_length);
4723 if (o->IsFailure()) return o;
4724 String* destination = String::cast(o);
4725 int dest_position = 0;
4726
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004727 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004728 buffer->Rewind();
4729 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00004730 uint16_t chr = buffer->GetNext();
4731 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004732 destination->Set(dest_position, '%');
4733 destination->Set(dest_position+1, 'u');
4734 destination->Set(dest_position+2, hex_chars[chr >> 12]);
4735 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
4736 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
4737 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004738 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00004739 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004740 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004741 dest_position++;
4742 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004743 destination->Set(dest_position, '%');
4744 destination->Set(dest_position+1, hex_chars[chr >> 4]);
4745 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004746 dest_position += 3;
4747 }
4748 }
4749 return destination;
4750}
4751
4752
4753static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
4754 static const signed char kHexValue['g'] = {
4755 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4756 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4757 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4758 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
4759 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4760 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4761 -1, 10, 11, 12, 13, 14, 15 };
4762
4763 if (character1 > 'f') return -1;
4764 int hi = kHexValue[character1];
4765 if (hi == -1) return -1;
4766 if (character2 > 'f') return -1;
4767 int lo = kHexValue[character2];
4768 if (lo == -1) return -1;
4769 return (hi << 4) + lo;
4770}
4771
4772
ager@chromium.org870a0b62008-11-04 11:43:05 +00004773static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004774 int i,
4775 int length,
4776 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004777 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00004778 int32_t hi = 0;
4779 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004780 if (character == '%' &&
4781 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004782 source->Get(i + 1) == 'u' &&
4783 (hi = TwoDigitHex(source->Get(i + 2),
4784 source->Get(i + 3))) != -1 &&
4785 (lo = TwoDigitHex(source->Get(i + 4),
4786 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004787 *step = 6;
4788 return (hi << 8) + lo;
4789 } else if (character == '%' &&
4790 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004791 (lo = TwoDigitHex(source->Get(i + 1),
4792 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004793 *step = 3;
4794 return lo;
4795 } else {
4796 *step = 1;
4797 return character;
4798 }
4799}
4800
4801
4802static Object* Runtime_URIUnescape(Arguments args) {
4803 NoHandleAllocation ha;
4804 ASSERT(args.length() == 1);
4805 CONVERT_CHECKED(String, source, args[0]);
4806
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004807 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004808
4809 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004810 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004811
4812 int unescaped_length = 0;
4813 for (int i = 0; i < length; unescaped_length++) {
4814 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004815 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004816 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004817 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004818 i += step;
4819 }
4820
4821 // No length change implies no change. Return original string if no change.
4822 if (unescaped_length == length)
4823 return source;
4824
4825 Object* o = ascii ?
4826 Heap::AllocateRawAsciiString(unescaped_length) :
4827 Heap::AllocateRawTwoByteString(unescaped_length);
4828 if (o->IsFailure()) return o;
4829 String* destination = String::cast(o);
4830
4831 int dest_position = 0;
4832 for (int i = 0; i < length; dest_position++) {
4833 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004834 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004835 i += step;
4836 }
4837 return destination;
4838}
4839
4840
4841static Object* Runtime_StringParseInt(Arguments args) {
4842 NoHandleAllocation ha;
4843
4844 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004845 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004846
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004847 s->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004848
lrn@chromium.org25156de2010-04-06 13:10:27 +00004849 RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
4850 double value = StringToInt(s, radix);
4851 return Heap::NumberFromDouble(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004852 return Heap::nan_value();
4853}
4854
4855
4856static Object* Runtime_StringParseFloat(Arguments args) {
4857 NoHandleAllocation ha;
4858 CONVERT_CHECKED(String, str, args[0]);
4859
4860 // ECMA-262 section 15.1.2.3, empty string is NaN
4861 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
4862
4863 // Create a number object from the value.
4864 return Heap::NumberFromDouble(value);
4865}
4866
4867
4868static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
4869static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
4870
4871
4872template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004873static Object* ConvertCaseHelper(String* s,
4874 int length,
4875 int input_string_length,
4876 unibrow::Mapping<Converter, 128>* mapping) {
4877 // We try this twice, once with the assumption that the result is no longer
4878 // than the input and, if that assumption breaks, again with the exact
4879 // length. This may not be pretty, but it is nicer than what was here before
4880 // and I hereby claim my vaffel-is.
4881 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004882 // Allocate the resulting string.
4883 //
4884 // NOTE: This assumes that the upper/lower case of an ascii
4885 // character is also ascii. This is currently the case, but it
4886 // might break in the future if we implement more context and locale
4887 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00004888 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004889 ? Heap::AllocateRawAsciiString(length)
4890 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004891 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004892 String* result = String::cast(o);
4893 bool has_changed_character = false;
4894
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004895 // Convert all characters to upper case, assuming that they will fit
4896 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004897 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004898 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004899 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004900 // We can assume that the string is not empty
4901 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004902 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00004903 bool has_next = buffer->has_more();
4904 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004905 int char_length = mapping->get(current, next, chars);
4906 if (char_length == 0) {
4907 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004908 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004909 i++;
4910 } else if (char_length == 1) {
4911 // Common case: converting the letter resulted in one character.
4912 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004913 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004914 has_changed_character = true;
4915 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004916 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004917 // We've assumed that the result would be as long as the
4918 // input but here is a character that converts to several
4919 // characters. No matter, we calculate the exact length
4920 // of the result and try the whole thing again.
4921 //
4922 // Note that this leaves room for optimization. We could just
4923 // memcpy what we already have to the result string. Also,
4924 // the result string is the last object allocated we could
4925 // "realloc" it and probably, in the vast majority of cases,
4926 // extend the existing string to be able to hold the full
4927 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00004928 int next_length = 0;
4929 if (has_next) {
4930 next_length = mapping->get(next, 0, chars);
4931 if (next_length == 0) next_length = 1;
4932 }
4933 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004934 while (buffer->has_more()) {
4935 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00004936 // NOTE: we use 0 as the next character here because, while
4937 // the next character may affect what a character converts to,
4938 // it does not in any case affect the length of what it convert
4939 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004940 int char_length = mapping->get(current, 0, chars);
4941 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00004942 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004943 if (current_length > Smi::kMaxValue) {
4944 Top::context()->mark_out_of_memory();
4945 return Failure::OutOfMemoryException();
4946 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004947 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004948 // Try again with the real length.
4949 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004950 } else {
4951 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004952 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004953 i++;
4954 }
4955 has_changed_character = true;
4956 }
4957 current = next;
4958 }
4959 if (has_changed_character) {
4960 return result;
4961 } else {
4962 // If we didn't actually change anything in doing the conversion
4963 // we simple return the result and let the converted string
4964 // become garbage; there is no reason to keep two identical strings
4965 // alive.
4966 return s;
4967 }
4968}
4969
4970
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004971namespace {
4972
4973struct ToLowerTraits {
4974 typedef unibrow::ToLowercase UnibrowConverter;
4975
4976 static bool ConvertAscii(char* dst, char* src, int length) {
4977 bool changed = false;
4978 for (int i = 0; i < length; ++i) {
4979 char c = src[i];
4980 if ('A' <= c && c <= 'Z') {
4981 c += ('a' - 'A');
4982 changed = true;
4983 }
4984 dst[i] = c;
4985 }
4986 return changed;
4987 }
4988};
4989
4990
4991struct ToUpperTraits {
4992 typedef unibrow::ToUppercase UnibrowConverter;
4993
4994 static bool ConvertAscii(char* dst, char* src, int length) {
4995 bool changed = false;
4996 for (int i = 0; i < length; ++i) {
4997 char c = src[i];
4998 if ('a' <= c && c <= 'z') {
4999 c -= ('a' - 'A');
5000 changed = true;
5001 }
5002 dst[i] = c;
5003 }
5004 return changed;
5005 }
5006};
5007
5008} // namespace
5009
5010
5011template <typename ConvertTraits>
5012static Object* ConvertCase(
5013 Arguments args,
5014 unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005015 NoHandleAllocation ha;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005016 CONVERT_CHECKED(String, s, args[0]);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005017 s = s->TryFlattenGetString();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005018
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005019 const int length = s->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005020 // Assume that the string is not empty; we need this assumption later
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005021 if (length == 0) return s;
5022
5023 // Simpler handling of ascii strings.
5024 //
5025 // NOTE: This assumes that the upper/lower case of an ascii
5026 // character is also ascii. This is currently the case, but it
5027 // might break in the future if we implement more context and locale
5028 // dependent upper/lower conversions.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005029 if (s->IsSeqAsciiString()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005030 Object* o = Heap::AllocateRawAsciiString(length);
5031 if (o->IsFailure()) return o;
5032 SeqAsciiString* result = SeqAsciiString::cast(o);
5033 bool has_changed_character = ConvertTraits::ConvertAscii(
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005034 result->GetChars(), SeqAsciiString::cast(s)->GetChars(), length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005035 return has_changed_character ? result : s;
5036 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005037
5038 Object* answer = ConvertCaseHelper(s, length, length, mapping);
5039 if (answer->IsSmi()) {
5040 // Retry with correct length.
5041 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
5042 }
5043 return answer; // This may be a failure.
5044}
5045
5046
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005047static Object* Runtime_StringToLowerCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005048 return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005049}
5050
5051
5052static Object* Runtime_StringToUpperCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005053 return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005054}
5055
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005056
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005057static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
5058 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
5059}
5060
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005061
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005062static Object* Runtime_StringTrim(Arguments args) {
5063 NoHandleAllocation ha;
5064 ASSERT(args.length() == 3);
5065
5066 CONVERT_CHECKED(String, s, args[0]);
5067 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
5068 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
5069
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005070 s->TryFlatten();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005071 int length = s->length();
5072
5073 int left = 0;
5074 if (trimLeft) {
5075 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
5076 left++;
5077 }
5078 }
5079
5080 int right = length;
5081 if (trimRight) {
5082 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
5083 right--;
5084 }
5085 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005086 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005087}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005088
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005089
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005090template <typename schar, typename pchar>
5091void FindStringIndices(Vector<const schar> subject,
5092 Vector<const pchar> pattern,
5093 ZoneList<int>* indices,
5094 unsigned int limit) {
5095 ASSERT(limit > 0);
5096 // Collect indices of pattern in subject, and the end-of-string index.
5097 // Stop after finding at most limit values.
5098 StringSearchStrategy strategy =
5099 InitializeStringSearch(pattern, sizeof(schar) == 1);
5100 switch (strategy) {
5101 case SEARCH_FAIL: return;
5102 case SEARCH_SHORT: {
5103 int pattern_length = pattern.length();
5104 int index = 0;
5105 while (limit > 0) {
5106 index = SimpleIndexOf(subject, pattern, index);
5107 if (index < 0) return;
5108 indices->Add(index);
5109 index += pattern_length;
5110 limit--;
5111 }
5112 return;
5113 }
5114 case SEARCH_LONG: {
5115 int pattern_length = pattern.length();
5116 int index = 0;
5117 while (limit > 0) {
5118 index = ComplexIndexOf(subject, pattern, index);
5119 if (index < 0) return;
5120 indices->Add(index);
5121 index += pattern_length;
5122 limit--;
5123 }
5124 return;
5125 }
5126 default:
5127 UNREACHABLE();
5128 return;
5129 }
5130}
5131
5132template <typename schar>
5133inline void FindCharIndices(Vector<const schar> subject,
5134 const schar pattern_char,
5135 ZoneList<int>* indices,
5136 unsigned int limit) {
5137 // Collect indices of pattern_char in subject, and the end-of-string index.
5138 // Stop after finding at most limit values.
5139 int index = 0;
5140 while (limit > 0) {
5141 index = SingleCharIndexOf(subject, pattern_char, index);
5142 if (index < 0) return;
5143 indices->Add(index);
5144 index++;
5145 limit--;
5146 }
5147}
5148
5149
5150static Object* Runtime_StringSplit(Arguments args) {
5151 ASSERT(args.length() == 3);
5152 HandleScope handle_scope;
5153 CONVERT_ARG_CHECKED(String, subject, 0);
5154 CONVERT_ARG_CHECKED(String, pattern, 1);
5155 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
5156
5157 int subject_length = subject->length();
5158 int pattern_length = pattern->length();
5159 RUNTIME_ASSERT(pattern_length > 0);
5160
5161 // The limit can be very large (0xffffffffu), but since the pattern
5162 // isn't empty, we can never create more parts than ~half the length
5163 // of the subject.
5164
5165 if (!subject->IsFlat()) FlattenString(subject);
5166
5167 static const int kMaxInitialListCapacity = 16;
5168
5169 ZoneScope scope(DELETE_ON_EXIT);
5170
5171 // Find (up to limit) indices of separator and end-of-string in subject
5172 int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
5173 ZoneList<int> indices(initial_capacity);
5174 if (pattern_length == 1) {
5175 // Special case, go directly to fast single-character split.
5176 AssertNoAllocation nogc;
5177 uc16 pattern_char = pattern->Get(0);
5178 if (subject->IsTwoByteRepresentation()) {
5179 FindCharIndices(subject->ToUC16Vector(), pattern_char,
5180 &indices,
5181 limit);
5182 } else if (pattern_char <= String::kMaxAsciiCharCode) {
5183 FindCharIndices(subject->ToAsciiVector(),
5184 static_cast<char>(pattern_char),
5185 &indices,
5186 limit);
5187 }
5188 } else {
5189 if (!pattern->IsFlat()) FlattenString(pattern);
5190 AssertNoAllocation nogc;
5191 if (subject->IsAsciiRepresentation()) {
5192 Vector<const char> subject_vector = subject->ToAsciiVector();
5193 if (pattern->IsAsciiRepresentation()) {
5194 FindStringIndices(subject_vector,
5195 pattern->ToAsciiVector(),
5196 &indices,
5197 limit);
5198 } else {
5199 FindStringIndices(subject_vector,
5200 pattern->ToUC16Vector(),
5201 &indices,
5202 limit);
5203 }
5204 } else {
5205 Vector<const uc16> subject_vector = subject->ToUC16Vector();
5206 if (pattern->IsAsciiRepresentation()) {
5207 FindStringIndices(subject_vector,
5208 pattern->ToAsciiVector(),
5209 &indices,
5210 limit);
5211 } else {
5212 FindStringIndices(subject_vector,
5213 pattern->ToUC16Vector(),
5214 &indices,
5215 limit);
5216 }
5217 }
5218 }
5219 if (static_cast<uint32_t>(indices.length()) < limit) {
5220 indices.Add(subject_length);
5221 }
5222 // The list indices now contains the end of each part to create.
5223
5224
5225 // Create JSArray of substrings separated by separator.
5226 int part_count = indices.length();
5227
5228 Handle<JSArray> result = Factory::NewJSArray(part_count);
5229 result->set_length(Smi::FromInt(part_count));
5230
5231 ASSERT(result->HasFastElements());
5232
5233 if (part_count == 1 && indices.at(0) == subject_length) {
5234 FixedArray::cast(result->elements())->set(0, *subject);
5235 return *result;
5236 }
5237
5238 Handle<FixedArray> elements(FixedArray::cast(result->elements()));
5239 int part_start = 0;
5240 for (int i = 0; i < part_count; i++) {
5241 HandleScope local_loop_handle;
5242 int part_end = indices.at(i);
5243 Handle<String> substring =
5244 Factory::NewSubString(subject, part_start, part_end);
5245 elements->set(i, *substring);
5246 part_start = part_end + pattern_length;
5247 }
5248
5249 return *result;
5250}
5251
5252
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005253// Copies ascii characters to the given fixed array looking up
5254// one-char strings in the cache. Gives up on the first char that is
5255// not in the cache and fills the remainder with smi zeros. Returns
5256// the length of the successfully copied prefix.
5257static int CopyCachedAsciiCharsToArray(const char* chars,
5258 FixedArray* elements,
5259 int length) {
5260 AssertNoAllocation nogc;
5261 FixedArray* ascii_cache = Heap::single_character_string_cache();
5262 Object* undefined = Heap::undefined_value();
5263 int i;
5264 for (i = 0; i < length; ++i) {
5265 Object* value = ascii_cache->get(chars[i]);
5266 if (value == undefined) break;
5267 ASSERT(!Heap::InNewSpace(value));
5268 elements->set(i, value, SKIP_WRITE_BARRIER);
5269 }
5270 if (i < length) {
5271 ASSERT(Smi::FromInt(0) == 0);
5272 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
5273 }
5274#ifdef DEBUG
5275 for (int j = 0; j < length; ++j) {
5276 Object* element = elements->get(j);
5277 ASSERT(element == Smi::FromInt(0) ||
5278 (element->IsString() && String::cast(element)->LooksValid()));
5279 }
5280#endif
5281 return i;
5282}
5283
5284
5285// Converts a String to JSArray.
5286// For example, "foo" => ["f", "o", "o"].
5287static Object* Runtime_StringToArray(Arguments args) {
5288 HandleScope scope;
5289 ASSERT(args.length() == 1);
5290 CONVERT_ARG_CHECKED(String, s, 0);
5291
5292 s->TryFlatten();
5293 const int length = s->length();
5294
5295 Handle<FixedArray> elements;
5296 if (s->IsFlat() && s->IsAsciiRepresentation()) {
5297 Object* obj = Heap::AllocateUninitializedFixedArray(length);
5298 if (obj->IsFailure()) return obj;
5299 elements = Handle<FixedArray>(FixedArray::cast(obj));
5300
5301 Vector<const char> chars = s->ToAsciiVector();
5302 // Note, this will initialize all elements (not only the prefix)
5303 // to prevent GC from seeing partially initialized array.
5304 int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
5305 *elements,
5306 length);
5307
5308 for (int i = num_copied_from_cache; i < length; ++i) {
5309 elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
5310 }
5311 } else {
5312 elements = Factory::NewFixedArray(length);
5313 for (int i = 0; i < length; ++i) {
5314 elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
5315 }
5316 }
5317
5318#ifdef DEBUG
5319 for (int i = 0; i < length; ++i) {
5320 ASSERT(String::cast(elements->get(i))->length() == 1);
5321 }
5322#endif
5323
5324 return *Factory::NewJSArrayWithElements(elements);
5325}
5326
5327
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00005328bool Runtime::IsUpperCaseChar(uint16_t ch) {
5329 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
5330 int char_length = to_upper_mapping.get(ch, 0, chars);
5331 return char_length == 0;
5332}
5333
5334
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005335static Object* Runtime_NumberToString(Arguments args) {
5336 NoHandleAllocation ha;
5337 ASSERT(args.length() == 1);
5338
5339 Object* number = args[0];
5340 RUNTIME_ASSERT(number->IsNumber());
5341
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005342 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005343}
5344
5345
ager@chromium.org357bf652010-04-12 11:30:10 +00005346static Object* Runtime_NumberToStringSkipCache(Arguments args) {
5347 NoHandleAllocation ha;
5348 ASSERT(args.length() == 1);
5349
5350 Object* number = args[0];
5351 RUNTIME_ASSERT(number->IsNumber());
5352
5353 return Heap::NumberToString(number, false);
5354}
5355
5356
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005357static Object* Runtime_NumberToInteger(Arguments args) {
5358 NoHandleAllocation ha;
5359 ASSERT(args.length() == 1);
5360
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005361 CONVERT_DOUBLE_CHECKED(number, args[0]);
5362
5363 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5364 if (number > 0 && number <= Smi::kMaxValue) {
5365 return Smi::FromInt(static_cast<int>(number));
5366 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005367 return Heap::NumberFromDouble(DoubleToInteger(number));
5368}
5369
5370
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005371static Object* Runtime_NumberToIntegerMapMinusZero(Arguments args) {
5372 NoHandleAllocation ha;
5373 ASSERT(args.length() == 1);
5374
5375 CONVERT_DOUBLE_CHECKED(number, args[0]);
5376
5377 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5378 if (number > 0 && number <= Smi::kMaxValue) {
5379 return Smi::FromInt(static_cast<int>(number));
5380 }
5381
5382 double double_value = DoubleToInteger(number);
5383 // Map both -0 and +0 to +0.
5384 if (double_value == 0) double_value = 0;
5385
5386 return Heap::NumberFromDouble(double_value);
5387}
5388
5389
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005390static Object* Runtime_NumberToJSUint32(Arguments args) {
5391 NoHandleAllocation ha;
5392 ASSERT(args.length() == 1);
5393
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005394 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005395 return Heap::NumberFromUint32(number);
5396}
5397
5398
5399static Object* Runtime_NumberToJSInt32(Arguments args) {
5400 NoHandleAllocation ha;
5401 ASSERT(args.length() == 1);
5402
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005403 CONVERT_DOUBLE_CHECKED(number, args[0]);
5404
5405 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5406 if (number > 0 && number <= Smi::kMaxValue) {
5407 return Smi::FromInt(static_cast<int>(number));
5408 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005409 return Heap::NumberFromInt32(DoubleToInt32(number));
5410}
5411
5412
ager@chromium.org870a0b62008-11-04 11:43:05 +00005413// Converts a Number to a Smi, if possible. Returns NaN if the number is not
5414// a small integer.
5415static Object* Runtime_NumberToSmi(Arguments args) {
5416 NoHandleAllocation ha;
5417 ASSERT(args.length() == 1);
5418
5419 Object* obj = args[0];
5420 if (obj->IsSmi()) {
5421 return obj;
5422 }
5423 if (obj->IsHeapNumber()) {
5424 double value = HeapNumber::cast(obj)->value();
5425 int int_value = FastD2I(value);
5426 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
5427 return Smi::FromInt(int_value);
5428 }
5429 }
5430 return Heap::nan_value();
5431}
5432
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005433
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005434static Object* Runtime_NumberAdd(Arguments args) {
5435 NoHandleAllocation ha;
5436 ASSERT(args.length() == 2);
5437
5438 CONVERT_DOUBLE_CHECKED(x, args[0]);
5439 CONVERT_DOUBLE_CHECKED(y, args[1]);
5440 return Heap::AllocateHeapNumber(x + y);
5441}
5442
5443
5444static Object* Runtime_NumberSub(Arguments args) {
5445 NoHandleAllocation ha;
5446 ASSERT(args.length() == 2);
5447
5448 CONVERT_DOUBLE_CHECKED(x, args[0]);
5449 CONVERT_DOUBLE_CHECKED(y, args[1]);
5450 return Heap::AllocateHeapNumber(x - y);
5451}
5452
5453
5454static Object* Runtime_NumberMul(Arguments args) {
5455 NoHandleAllocation ha;
5456 ASSERT(args.length() == 2);
5457
5458 CONVERT_DOUBLE_CHECKED(x, args[0]);
5459 CONVERT_DOUBLE_CHECKED(y, args[1]);
5460 return Heap::AllocateHeapNumber(x * y);
5461}
5462
5463
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005464static Object* Runtime_NumberUnaryMinus(Arguments args) {
5465 NoHandleAllocation ha;
5466 ASSERT(args.length() == 1);
5467
5468 CONVERT_DOUBLE_CHECKED(x, args[0]);
5469 return Heap::AllocateHeapNumber(-x);
5470}
5471
5472
5473static Object* Runtime_NumberDiv(Arguments args) {
5474 NoHandleAllocation ha;
5475 ASSERT(args.length() == 2);
5476
5477 CONVERT_DOUBLE_CHECKED(x, args[0]);
5478 CONVERT_DOUBLE_CHECKED(y, args[1]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005479 return Heap::NumberFromDouble(x / y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005480}
5481
5482
5483static Object* Runtime_NumberMod(Arguments args) {
5484 NoHandleAllocation ha;
5485 ASSERT(args.length() == 2);
5486
5487 CONVERT_DOUBLE_CHECKED(x, args[0]);
5488 CONVERT_DOUBLE_CHECKED(y, args[1]);
5489
ager@chromium.org3811b432009-10-28 14:53:37 +00005490 x = modulo(x, y);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005491 // NumberFromDouble may return a Smi instead of a Number object
5492 return Heap::NumberFromDouble(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005493}
5494
5495
5496static Object* Runtime_StringAdd(Arguments args) {
5497 NoHandleAllocation ha;
5498 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005499 CONVERT_CHECKED(String, str1, args[0]);
5500 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005501 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00005502 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005503}
5504
5505
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005506template <typename sinkchar>
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005507static inline void StringBuilderConcatHelper(String* special,
5508 sinkchar* sink,
5509 FixedArray* fixed_array,
5510 int array_length) {
5511 int position = 0;
5512 for (int i = 0; i < array_length; i++) {
5513 Object* element = fixed_array->get(i);
5514 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005515 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005516 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005517 int pos;
5518 int len;
5519 if (encoded_slice > 0) {
5520 // Position and length encoded in one smi.
5521 pos = StringBuilderSubstringPosition::decode(encoded_slice);
5522 len = StringBuilderSubstringLength::decode(encoded_slice);
5523 } else {
5524 // Position and length encoded in two smis.
5525 Object* obj = fixed_array->get(++i);
5526 ASSERT(obj->IsSmi());
5527 pos = Smi::cast(obj)->value();
5528 len = -encoded_slice;
5529 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00005530 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00005531 sink + position,
5532 pos,
5533 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005534 position += len;
5535 } else {
5536 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005537 int element_length = string->length();
5538 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005539 position += element_length;
5540 }
5541 }
5542}
5543
5544
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005545static Object* Runtime_StringBuilderConcat(Arguments args) {
5546 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005547 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005548 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005549 if (!args[1]->IsSmi()) {
5550 Top::context()->mark_out_of_memory();
5551 return Failure::OutOfMemoryException();
5552 }
5553 int array_length = Smi::cast(args[1])->value();
5554 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005555
5556 // This assumption is used by the slice encoding in one or two smis.
5557 ASSERT(Smi::kMaxValue >= String::kMaxLength);
5558
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005559 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005560 if (!array->HasFastElements()) {
5561 return Top::Throw(Heap::illegal_argument_symbol());
5562 }
5563 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005564 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005565 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005566 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005567
5568 if (array_length == 0) {
5569 return Heap::empty_string();
5570 } else if (array_length == 1) {
5571 Object* first = fixed_array->get(0);
5572 if (first->IsString()) return first;
5573 }
5574
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005575 bool ascii = special->HasOnlyAsciiChars();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005576 int position = 0;
5577 for (int i = 0; i < array_length; i++) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005578 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005579 Object* elt = fixed_array->get(i);
5580 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005581 // Smi encoding of position and length.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005582 int smi_value = Smi::cast(elt)->value();
5583 int pos;
5584 int len;
5585 if (smi_value > 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005586 // Position and length encoded in one smi.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005587 pos = StringBuilderSubstringPosition::decode(smi_value);
5588 len = StringBuilderSubstringLength::decode(smi_value);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005589 } else {
5590 // Position and length encoded in two smis.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005591 len = -smi_value;
5592 // Get the position and check that it is a positive smi.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005593 i++;
5594 if (i >= array_length) {
5595 return Top::Throw(Heap::illegal_argument_symbol());
5596 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005597 Object* next_smi = fixed_array->get(i);
5598 if (!next_smi->IsSmi()) {
5599 return Top::Throw(Heap::illegal_argument_symbol());
5600 }
5601 pos = Smi::cast(next_smi)->value();
5602 if (pos < 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005603 return Top::Throw(Heap::illegal_argument_symbol());
5604 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005605 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005606 ASSERT(pos >= 0);
5607 ASSERT(len >= 0);
5608 if (pos > special_length || len > special_length - pos) {
5609 return Top::Throw(Heap::illegal_argument_symbol());
5610 }
5611 increment = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005612 } else if (elt->IsString()) {
5613 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005614 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005615 increment = element_length;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005616 if (ascii && !element->HasOnlyAsciiChars()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005617 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005618 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005619 } else {
5620 return Top::Throw(Heap::illegal_argument_symbol());
5621 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005622 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005623 Top::context()->mark_out_of_memory();
5624 return Failure::OutOfMemoryException();
5625 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005626 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005627 }
5628
5629 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005630 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005631
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005632 if (ascii) {
5633 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005634 if (object->IsFailure()) return object;
5635 SeqAsciiString* answer = SeqAsciiString::cast(object);
5636 StringBuilderConcatHelper(special,
5637 answer->GetChars(),
5638 fixed_array,
5639 array_length);
5640 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005641 } else {
5642 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005643 if (object->IsFailure()) return object;
5644 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
5645 StringBuilderConcatHelper(special,
5646 answer->GetChars(),
5647 fixed_array,
5648 array_length);
5649 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005650 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005651}
5652
5653
5654static Object* Runtime_NumberOr(Arguments args) {
5655 NoHandleAllocation ha;
5656 ASSERT(args.length() == 2);
5657
5658 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5659 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5660 return Heap::NumberFromInt32(x | y);
5661}
5662
5663
5664static Object* Runtime_NumberAnd(Arguments args) {
5665 NoHandleAllocation ha;
5666 ASSERT(args.length() == 2);
5667
5668 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5669 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5670 return Heap::NumberFromInt32(x & y);
5671}
5672
5673
5674static Object* Runtime_NumberXor(Arguments args) {
5675 NoHandleAllocation ha;
5676 ASSERT(args.length() == 2);
5677
5678 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5679 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5680 return Heap::NumberFromInt32(x ^ y);
5681}
5682
5683
5684static Object* Runtime_NumberNot(Arguments args) {
5685 NoHandleAllocation ha;
5686 ASSERT(args.length() == 1);
5687
5688 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5689 return Heap::NumberFromInt32(~x);
5690}
5691
5692
5693static Object* Runtime_NumberShl(Arguments args) {
5694 NoHandleAllocation ha;
5695 ASSERT(args.length() == 2);
5696
5697 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5698 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5699 return Heap::NumberFromInt32(x << (y & 0x1f));
5700}
5701
5702
5703static Object* Runtime_NumberShr(Arguments args) {
5704 NoHandleAllocation ha;
5705 ASSERT(args.length() == 2);
5706
5707 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
5708 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5709 return Heap::NumberFromUint32(x >> (y & 0x1f));
5710}
5711
5712
5713static Object* Runtime_NumberSar(Arguments args) {
5714 NoHandleAllocation ha;
5715 ASSERT(args.length() == 2);
5716
5717 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5718 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5719 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
5720}
5721
5722
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005723static Object* Runtime_NumberEquals(Arguments args) {
5724 NoHandleAllocation ha;
5725 ASSERT(args.length() == 2);
5726
5727 CONVERT_DOUBLE_CHECKED(x, args[0]);
5728 CONVERT_DOUBLE_CHECKED(y, args[1]);
5729 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
5730 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
5731 if (x == y) return Smi::FromInt(EQUAL);
5732 Object* result;
5733 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
5734 result = Smi::FromInt(EQUAL);
5735 } else {
5736 result = Smi::FromInt(NOT_EQUAL);
5737 }
5738 return result;
5739}
5740
5741
5742static Object* Runtime_StringEquals(Arguments args) {
5743 NoHandleAllocation ha;
5744 ASSERT(args.length() == 2);
5745
5746 CONVERT_CHECKED(String, x, args[0]);
5747 CONVERT_CHECKED(String, y, args[1]);
5748
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005749 bool not_equal = !x->Equals(y);
5750 // This is slightly convoluted because the value that signifies
5751 // equality is 0 and inequality is 1 so we have to negate the result
5752 // from String::Equals.
5753 ASSERT(not_equal == 0 || not_equal == 1);
5754 STATIC_CHECK(EQUAL == 0);
5755 STATIC_CHECK(NOT_EQUAL == 1);
5756 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005757}
5758
5759
5760static Object* Runtime_NumberCompare(Arguments args) {
5761 NoHandleAllocation ha;
5762 ASSERT(args.length() == 3);
5763
5764 CONVERT_DOUBLE_CHECKED(x, args[0]);
5765 CONVERT_DOUBLE_CHECKED(y, args[1]);
5766 if (isnan(x) || isnan(y)) return args[2];
5767 if (x == y) return Smi::FromInt(EQUAL);
5768 if (isless(x, y)) return Smi::FromInt(LESS);
5769 return Smi::FromInt(GREATER);
5770}
5771
5772
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005773// Compare two Smis as if they were converted to strings and then
5774// compared lexicographically.
5775static Object* Runtime_SmiLexicographicCompare(Arguments args) {
5776 NoHandleAllocation ha;
5777 ASSERT(args.length() == 2);
5778
5779 // Arrays for the individual characters of the two Smis. Smis are
5780 // 31 bit integers and 10 decimal digits are therefore enough.
5781 static int x_elms[10];
5782 static int y_elms[10];
5783
5784 // Extract the integer values from the Smis.
5785 CONVERT_CHECKED(Smi, x, args[0]);
5786 CONVERT_CHECKED(Smi, y, args[1]);
5787 int x_value = x->value();
5788 int y_value = y->value();
5789
5790 // If the integers are equal so are the string representations.
5791 if (x_value == y_value) return Smi::FromInt(EQUAL);
5792
5793 // If one of the integers are zero the normal integer order is the
5794 // same as the lexicographic order of the string representations.
5795 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
5796
ager@chromium.org32912102009-01-16 10:38:43 +00005797 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005798 // smallest because the char code of '-' is less than the char code
5799 // of any digit. Otherwise, we make both values positive.
5800 if (x_value < 0 || y_value < 0) {
5801 if (y_value >= 0) return Smi::FromInt(LESS);
5802 if (x_value >= 0) return Smi::FromInt(GREATER);
5803 x_value = -x_value;
5804 y_value = -y_value;
5805 }
5806
5807 // Convert the integers to arrays of their decimal digits.
5808 int x_index = 0;
5809 int y_index = 0;
5810 while (x_value > 0) {
5811 x_elms[x_index++] = x_value % 10;
5812 x_value /= 10;
5813 }
5814 while (y_value > 0) {
5815 y_elms[y_index++] = y_value % 10;
5816 y_value /= 10;
5817 }
5818
5819 // Loop through the arrays of decimal digits finding the first place
5820 // where they differ.
5821 while (--x_index >= 0 && --y_index >= 0) {
5822 int diff = x_elms[x_index] - y_elms[y_index];
5823 if (diff != 0) return Smi::FromInt(diff);
5824 }
5825
5826 // If one array is a suffix of the other array, the longest array is
5827 // the representation of the largest of the Smis in the
5828 // lexicographic ordering.
5829 return Smi::FromInt(x_index - y_index);
5830}
5831
5832
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005833static Object* StringInputBufferCompare(String* x, String* y) {
5834 static StringInputBuffer bufx;
5835 static StringInputBuffer bufy;
5836 bufx.Reset(x);
5837 bufy.Reset(y);
5838 while (bufx.has_more() && bufy.has_more()) {
5839 int d = bufx.GetNext() - bufy.GetNext();
5840 if (d < 0) return Smi::FromInt(LESS);
5841 else if (d > 0) return Smi::FromInt(GREATER);
5842 }
5843
5844 // x is (non-trivial) prefix of y:
5845 if (bufy.has_more()) return Smi::FromInt(LESS);
5846 // y is prefix of x:
5847 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
5848}
5849
5850
5851static Object* FlatStringCompare(String* x, String* y) {
5852 ASSERT(x->IsFlat());
5853 ASSERT(y->IsFlat());
5854 Object* equal_prefix_result = Smi::FromInt(EQUAL);
5855 int prefix_length = x->length();
5856 if (y->length() < prefix_length) {
5857 prefix_length = y->length();
5858 equal_prefix_result = Smi::FromInt(GREATER);
5859 } else if (y->length() > prefix_length) {
5860 equal_prefix_result = Smi::FromInt(LESS);
5861 }
5862 int r;
5863 if (x->IsAsciiRepresentation()) {
5864 Vector<const char> x_chars = x->ToAsciiVector();
5865 if (y->IsAsciiRepresentation()) {
5866 Vector<const char> y_chars = y->ToAsciiVector();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005867 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005868 } else {
5869 Vector<const uc16> y_chars = y->ToUC16Vector();
5870 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5871 }
5872 } else {
5873 Vector<const uc16> x_chars = x->ToUC16Vector();
5874 if (y->IsAsciiRepresentation()) {
5875 Vector<const char> y_chars = y->ToAsciiVector();
5876 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5877 } else {
5878 Vector<const uc16> y_chars = y->ToUC16Vector();
5879 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5880 }
5881 }
5882 Object* result;
5883 if (r == 0) {
5884 result = equal_prefix_result;
5885 } else {
5886 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
5887 }
5888 ASSERT(result == StringInputBufferCompare(x, y));
5889 return result;
5890}
5891
5892
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005893static Object* Runtime_StringCompare(Arguments args) {
5894 NoHandleAllocation ha;
5895 ASSERT(args.length() == 2);
5896
5897 CONVERT_CHECKED(String, x, args[0]);
5898 CONVERT_CHECKED(String, y, args[1]);
5899
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005900 Counters::string_compare_runtime.Increment();
5901
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005902 // A few fast case tests before we flatten.
5903 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005904 if (y->length() == 0) {
5905 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005906 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005907 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005908 return Smi::FromInt(LESS);
5909 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005910
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005911 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005912 if (d < 0) return Smi::FromInt(LESS);
5913 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005914
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005915 Object* obj = Heap::PrepareForCompare(x);
5916 if (obj->IsFailure()) return obj;
5917 obj = Heap::PrepareForCompare(y);
5918 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005919
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005920 return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
5921 : StringInputBufferCompare(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005922}
5923
5924
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005925static Object* Runtime_Math_acos(Arguments args) {
5926 NoHandleAllocation ha;
5927 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005928 Counters::math_acos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005929
5930 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005931 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005932}
5933
5934
5935static Object* Runtime_Math_asin(Arguments args) {
5936 NoHandleAllocation ha;
5937 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005938 Counters::math_asin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005939
5940 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005941 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005942}
5943
5944
5945static Object* Runtime_Math_atan(Arguments args) {
5946 NoHandleAllocation ha;
5947 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005948 Counters::math_atan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005949
5950 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005951 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005952}
5953
5954
5955static Object* Runtime_Math_atan2(Arguments args) {
5956 NoHandleAllocation ha;
5957 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005958 Counters::math_atan2.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005959
5960 CONVERT_DOUBLE_CHECKED(x, args[0]);
5961 CONVERT_DOUBLE_CHECKED(y, args[1]);
5962 double result;
5963 if (isinf(x) && isinf(y)) {
5964 // Make sure that the result in case of two infinite arguments
5965 // is a multiple of Pi / 4. The sign of the result is determined
5966 // by the first argument (x) and the sign of the second argument
5967 // determines the multiplier: one or three.
5968 static double kPiDividedBy4 = 0.78539816339744830962;
5969 int multiplier = (x < 0) ? -1 : 1;
5970 if (y < 0) multiplier *= 3;
5971 result = multiplier * kPiDividedBy4;
5972 } else {
5973 result = atan2(x, y);
5974 }
5975 return Heap::AllocateHeapNumber(result);
5976}
5977
5978
5979static Object* Runtime_Math_ceil(Arguments args) {
5980 NoHandleAllocation ha;
5981 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005982 Counters::math_ceil.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005983
5984 CONVERT_DOUBLE_CHECKED(x, args[0]);
5985 return Heap::NumberFromDouble(ceiling(x));
5986}
5987
5988
5989static Object* Runtime_Math_cos(Arguments args) {
5990 NoHandleAllocation ha;
5991 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005992 Counters::math_cos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005993
5994 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005995 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005996}
5997
5998
5999static Object* Runtime_Math_exp(Arguments args) {
6000 NoHandleAllocation ha;
6001 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006002 Counters::math_exp.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006003
6004 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006005 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006006}
6007
6008
6009static Object* Runtime_Math_floor(Arguments args) {
6010 NoHandleAllocation ha;
6011 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006012 Counters::math_floor.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006013
6014 CONVERT_DOUBLE_CHECKED(x, args[0]);
6015 return Heap::NumberFromDouble(floor(x));
6016}
6017
6018
6019static Object* Runtime_Math_log(Arguments args) {
6020 NoHandleAllocation ha;
6021 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006022 Counters::math_log.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006023
6024 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006025 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006026}
6027
6028
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006029// Helper function to compute x^y, where y is known to be an
6030// integer. Uses binary decomposition to limit the number of
6031// multiplications; see the discussion in "Hacker's Delight" by Henry
6032// S. Warren, Jr., figure 11-6, page 213.
6033static double powi(double x, int y) {
6034 ASSERT(y != kMinInt);
6035 unsigned n = (y < 0) ? -y : y;
6036 double m = x;
6037 double p = 1;
6038 while (true) {
6039 if ((n & 1) != 0) p *= m;
6040 n >>= 1;
6041 if (n == 0) {
6042 if (y < 0) {
6043 // Unfortunately, we have to be careful when p has reached
6044 // infinity in the computation, because sometimes the higher
6045 // internal precision in the pow() implementation would have
6046 // given us a finite p. This happens very rarely.
6047 double result = 1.0 / p;
6048 return (result == 0 && isinf(p))
6049 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
6050 : result;
6051 } else {
6052 return p;
6053 }
6054 }
6055 m *= m;
6056 }
6057}
6058
6059
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006060static Object* Runtime_Math_pow(Arguments args) {
6061 NoHandleAllocation ha;
6062 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006063 Counters::math_pow.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006064
6065 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006066
6067 // If the second argument is a smi, it is much faster to call the
6068 // custom powi() function than the generic pow().
6069 if (args[1]->IsSmi()) {
6070 int y = Smi::cast(args[1])->value();
6071 return Heap::AllocateHeapNumber(powi(x, y));
6072 }
6073
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006074 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00006075
6076 if (!isinf(x)) {
6077 if (y == 0.5) {
6078 // It's not uncommon to use Math.pow(x, 0.5) to compute the
6079 // square root of a number. To speed up such computations, we
6080 // explictly check for this case and use the sqrt() function
6081 // which is faster than pow().
6082 return Heap::AllocateHeapNumber(sqrt(x));
6083 } else if (y == -0.5) {
6084 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
6085 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
6086 }
6087 }
6088
6089 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006090 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006091 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6092 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006093 } else {
6094 return Heap::AllocateHeapNumber(pow(x, y));
6095 }
6096}
6097
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006098// Fast version of Math.pow if we know that y is not an integer and
6099// y is not -0.5 or 0.5. Used as slowcase from codegen.
6100static Object* Runtime_Math_pow_cfunction(Arguments args) {
6101 NoHandleAllocation ha;
6102 ASSERT(args.length() == 2);
6103 CONVERT_DOUBLE_CHECKED(x, args[0]);
6104 CONVERT_DOUBLE_CHECKED(y, args[1]);
6105 if (y == 0) {
6106 return Smi::FromInt(1);
6107 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6108 return Heap::nan_value();
6109 } else {
6110 return Heap::AllocateHeapNumber(pow(x, y));
6111 }
6112}
6113
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006114
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006115static Object* Runtime_RoundNumber(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006116 NoHandleAllocation ha;
6117 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006118 Counters::math_round.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006119
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006120 if (!args[0]->IsHeapNumber()) {
6121 // Must be smi. Return the argument unchanged for all the other types
6122 // to make fuzz-natives test happy.
6123 return args[0];
6124 }
6125
6126 HeapNumber* number = reinterpret_cast<HeapNumber*>(args[0]);
6127
6128 double value = number->value();
6129 int exponent = number->get_exponent();
6130 int sign = number->get_sign();
6131
6132 // We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
6133 // should be rounded to 2^30, which is not smi.
6134 if (!sign && exponent <= kSmiValueSize - 3) {
6135 return Smi::FromInt(static_cast<int>(value + 0.5));
6136 }
6137
6138 // If the magnitude is big enough, there's no place for fraction part. If we
6139 // try to add 0.5 to this number, 1.0 will be added instead.
6140 if (exponent >= 52) {
6141 return number;
6142 }
6143
6144 if (sign && value >= -0.5) return Heap::minus_zero_value();
6145
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006146 // Do not call NumberFromDouble() to avoid extra checks.
6147 return Heap::AllocateHeapNumber(floor(value + 0.5));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006148}
6149
6150
6151static Object* Runtime_Math_sin(Arguments args) {
6152 NoHandleAllocation ha;
6153 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006154 Counters::math_sin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006155
6156 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006157 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006158}
6159
6160
6161static Object* Runtime_Math_sqrt(Arguments args) {
6162 NoHandleAllocation ha;
6163 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006164 Counters::math_sqrt.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006165
6166 CONVERT_DOUBLE_CHECKED(x, args[0]);
6167 return Heap::AllocateHeapNumber(sqrt(x));
6168}
6169
6170
6171static Object* Runtime_Math_tan(Arguments args) {
6172 NoHandleAllocation ha;
6173 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006174 Counters::math_tan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006175
6176 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006177 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006178}
6179
6180
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006181static int MakeDay(int year, int month, int day) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006182 static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
6183 181, 212, 243, 273, 304, 334};
6184 static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
6185 182, 213, 244, 274, 305, 335};
6186
6187 year += month / 12;
6188 month %= 12;
6189 if (month < 0) {
6190 year--;
6191 month += 12;
6192 }
6193
6194 ASSERT(month >= 0);
6195 ASSERT(month < 12);
6196
6197 // year_delta is an arbitrary number such that:
6198 // a) year_delta = -1 (mod 400)
6199 // b) year + year_delta > 0 for years in the range defined by
6200 // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
6201 // Jan 1 1970. This is required so that we don't run into integer
6202 // division of negative numbers.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006203 // c) there shouldn't be an overflow for 32-bit integers in the following
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006204 // operations.
6205 static const int year_delta = 399999;
6206 static const int base_day = 365 * (1970 + year_delta) +
6207 (1970 + year_delta) / 4 -
6208 (1970 + year_delta) / 100 +
6209 (1970 + year_delta) / 400;
6210
6211 int year1 = year + year_delta;
6212 int day_from_year = 365 * year1 +
6213 year1 / 4 -
6214 year1 / 100 +
6215 year1 / 400 -
6216 base_day;
6217
6218 if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006219 return day_from_year + day_from_month[month] + day - 1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006220 }
6221
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006222 return day_from_year + day_from_month_leap[month] + day - 1;
6223}
6224
6225
6226static Object* Runtime_DateMakeDay(Arguments args) {
6227 NoHandleAllocation ha;
6228 ASSERT(args.length() == 3);
6229
6230 CONVERT_SMI_CHECKED(year, args[0]);
6231 CONVERT_SMI_CHECKED(month, args[1]);
6232 CONVERT_SMI_CHECKED(date, args[2]);
6233
6234 return Smi::FromInt(MakeDay(year, month, date));
6235}
6236
6237
6238static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
6239static const int kDaysIn4Years = 4 * 365 + 1;
6240static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
6241static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
6242static const int kDays1970to2000 = 30 * 365 + 7;
6243static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
6244 kDays1970to2000;
6245static const int kYearsOffset = 400000;
6246
6247static const char kDayInYear[] = {
6248 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6249 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6250 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6251 22, 23, 24, 25, 26, 27, 28,
6252 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6253 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6254 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6255 22, 23, 24, 25, 26, 27, 28, 29, 30,
6256 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6257 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6258 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6259 22, 23, 24, 25, 26, 27, 28, 29, 30,
6260 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6261 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6262 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6263 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6264 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6265 22, 23, 24, 25, 26, 27, 28, 29, 30,
6266 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6267 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6268 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6269 22, 23, 24, 25, 26, 27, 28, 29, 30,
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
6273 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6274 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6275 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6276 22, 23, 24, 25, 26, 27, 28,
6277 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6278 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6279 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6280 22, 23, 24, 25, 26, 27, 28, 29, 30,
6281 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6282 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6283 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6284 22, 23, 24, 25, 26, 27, 28, 29, 30,
6285 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6286 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6287 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6288 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6289 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6290 22, 23, 24, 25, 26, 27, 28, 29, 30,
6291 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6292 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6293 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6294 22, 23, 24, 25, 26, 27, 28, 29, 30,
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
6298 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6299 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6300 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6301 22, 23, 24, 25, 26, 27, 28, 29,
6302 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6303 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6304 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6305 22, 23, 24, 25, 26, 27, 28, 29, 30,
6306 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6307 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6308 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6309 22, 23, 24, 25, 26, 27, 28, 29, 30,
6310 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6311 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6312 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6313 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6314 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6315 22, 23, 24, 25, 26, 27, 28, 29, 30,
6316 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6317 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6318 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6319 22, 23, 24, 25, 26, 27, 28, 29, 30,
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
6323 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6324 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6325 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6326 22, 23, 24, 25, 26, 27, 28,
6327 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6328 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6329 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6330 22, 23, 24, 25, 26, 27, 28, 29, 30,
6331 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6332 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6333 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6334 22, 23, 24, 25, 26, 27, 28, 29, 30,
6335 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6336 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6337 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6338 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6339 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6340 22, 23, 24, 25, 26, 27, 28, 29, 30,
6341 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6342 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6343 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6344 22, 23, 24, 25, 26, 27, 28, 29, 30,
6345 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6346 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
6347
6348static const char kMonthInYear[] = {
6349 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,
6350 0, 0, 0, 0, 0, 0,
6351 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,
6352 1, 1, 1,
6353 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,
6354 2, 2, 2, 2, 2, 2,
6355 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,
6356 3, 3, 3, 3, 3,
6357 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,
6358 4, 4, 4, 4, 4, 4,
6359 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,
6360 5, 5, 5, 5, 5,
6361 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,
6362 6, 6, 6, 6, 6, 6,
6363 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,
6364 7, 7, 7, 7, 7, 7,
6365 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,
6366 8, 8, 8, 8, 8,
6367 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,
6368 9, 9, 9, 9, 9, 9,
6369 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6370 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6371 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6372 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6373
6374 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,
6375 0, 0, 0, 0, 0, 0,
6376 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,
6377 1, 1, 1,
6378 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,
6379 2, 2, 2, 2, 2, 2,
6380 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,
6381 3, 3, 3, 3, 3,
6382 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,
6383 4, 4, 4, 4, 4, 4,
6384 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,
6385 5, 5, 5, 5, 5,
6386 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,
6387 6, 6, 6, 6, 6, 6,
6388 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,
6389 7, 7, 7, 7, 7, 7,
6390 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,
6391 8, 8, 8, 8, 8,
6392 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,
6393 9, 9, 9, 9, 9, 9,
6394 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6395 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6396 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6397 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6398
6399 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,
6400 0, 0, 0, 0, 0, 0,
6401 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,
6402 1, 1, 1, 1,
6403 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,
6404 2, 2, 2, 2, 2, 2,
6405 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,
6406 3, 3, 3, 3, 3,
6407 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,
6408 4, 4, 4, 4, 4, 4,
6409 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,
6410 5, 5, 5, 5, 5,
6411 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,
6412 6, 6, 6, 6, 6, 6,
6413 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,
6414 7, 7, 7, 7, 7, 7,
6415 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,
6416 8, 8, 8, 8, 8,
6417 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,
6418 9, 9, 9, 9, 9, 9,
6419 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6420 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6421 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6422 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6423
6424 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,
6425 0, 0, 0, 0, 0, 0,
6426 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,
6427 1, 1, 1,
6428 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,
6429 2, 2, 2, 2, 2, 2,
6430 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,
6431 3, 3, 3, 3, 3,
6432 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,
6433 4, 4, 4, 4, 4, 4,
6434 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,
6435 5, 5, 5, 5, 5,
6436 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,
6437 6, 6, 6, 6, 6, 6,
6438 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,
6439 7, 7, 7, 7, 7, 7,
6440 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,
6441 8, 8, 8, 8, 8,
6442 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,
6443 9, 9, 9, 9, 9, 9,
6444 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6445 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6446 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6447 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
6448
6449
6450// This function works for dates from 1970 to 2099.
6451static inline void DateYMDFromTimeAfter1970(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006452 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006453#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006454 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006455#endif
6456
6457 year = 1970 + (4 * date + 2) / kDaysIn4Years;
6458 date %= kDaysIn4Years;
6459
6460 month = kMonthInYear[date];
6461 day = kDayInYear[date];
6462
6463 ASSERT(MakeDay(year, month, day) == save_date);
6464}
6465
6466
6467static inline void DateYMDFromTimeSlow(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006468 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006469#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006470 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006471#endif
6472
6473 date += kDaysOffset;
6474 year = 400 * (date / kDaysIn400Years) - kYearsOffset;
6475 date %= kDaysIn400Years;
6476
6477 ASSERT(MakeDay(year, 0, 1) + date == save_date);
6478
6479 date--;
6480 int yd1 = date / kDaysIn100Years;
6481 date %= kDaysIn100Years;
6482 year += 100 * yd1;
6483
6484 date++;
6485 int yd2 = date / kDaysIn4Years;
6486 date %= kDaysIn4Years;
6487 year += 4 * yd2;
6488
6489 date--;
6490 int yd3 = date / 365;
6491 date %= 365;
6492 year += yd3;
6493
6494 bool is_leap = (!yd1 || yd2) && !yd3;
6495
6496 ASSERT(date >= -1);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006497 ASSERT(is_leap || (date >= 0));
6498 ASSERT((date < 365) || (is_leap && (date < 366)));
6499 ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0))));
6500 ASSERT(is_leap || ((MakeDay(year, 0, 1) + date) == save_date));
6501 ASSERT(!is_leap || ((MakeDay(year, 0, 1) + date + 1) == save_date));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006502
6503 if (is_leap) {
6504 day = kDayInYear[2*365 + 1 + date];
6505 month = kMonthInYear[2*365 + 1 + date];
6506 } else {
6507 day = kDayInYear[date];
6508 month = kMonthInYear[date];
6509 }
6510
6511 ASSERT(MakeDay(year, month, day) == save_date);
6512}
6513
6514
6515static inline void DateYMDFromTime(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006516 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006517 if (date >= 0 && date < 32 * kDaysIn4Years) {
6518 DateYMDFromTimeAfter1970(date, year, month, day);
6519 } else {
6520 DateYMDFromTimeSlow(date, year, month, day);
6521 }
6522}
6523
6524
6525static Object* Runtime_DateYMDFromTime(Arguments args) {
6526 NoHandleAllocation ha;
6527 ASSERT(args.length() == 2);
6528
6529 CONVERT_DOUBLE_CHECKED(t, args[0]);
6530 CONVERT_CHECKED(JSArray, res_array, args[1]);
6531
6532 int year, month, day;
6533 DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
6534
6535 res_array->SetElement(0, Smi::FromInt(year));
6536 res_array->SetElement(1, Smi::FromInt(month));
6537 res_array->SetElement(2, Smi::FromInt(day));
6538
6539 return Heap::undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006540}
6541
6542
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006543static Object* Runtime_NewArgumentsFast(Arguments args) {
6544 NoHandleAllocation ha;
6545 ASSERT(args.length() == 3);
6546
6547 JSFunction* callee = JSFunction::cast(args[0]);
6548 Object** parameters = reinterpret_cast<Object**>(args[1]);
6549 const int length = Smi::cast(args[2])->value();
6550
6551 Object* result = Heap::AllocateArgumentsObject(callee, length);
6552 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006553 // Allocate the elements if needed.
6554 if (length > 0) {
6555 // Allocate the fixed array.
6556 Object* obj = Heap::AllocateRawFixedArray(length);
6557 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006558
6559 AssertNoAllocation no_gc;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006560 FixedArray* array = reinterpret_cast<FixedArray*>(obj);
6561 array->set_map(Heap::fixed_array_map());
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006562 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006563
6564 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006565 for (int i = 0; i < length; i++) {
6566 array->set(i, *--parameters, mode);
6567 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006568 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006569 }
6570 return result;
6571}
6572
6573
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006574static Object* Runtime_NewClosure(Arguments args) {
6575 HandleScope scope;
6576 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00006577 CONVERT_ARG_CHECKED(Context, context, 0);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006578 CONVERT_ARG_CHECKED(SharedFunctionInfo, shared, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006579
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00006580 PretenureFlag pretenure = (context->global_context() == *context)
6581 ? TENURED // Allocate global closures in old space.
6582 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006583 Handle<JSFunction> result =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006584 Factory::NewFunctionFromSharedFunctionInfo(shared, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006585 return *result;
6586}
6587
6588
ager@chromium.org5c838252010-02-19 08:53:10 +00006589static Code* ComputeConstructStub(Handle<JSFunction> function) {
6590 Handle<Object> prototype = Factory::null_value();
6591 if (function->has_instance_prototype()) {
6592 prototype = Handle<Object>(function->instance_prototype());
6593 }
6594 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006595 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00006596 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006597 if (code->IsFailure()) {
6598 return Builtins::builtin(Builtins::JSConstructStubGeneric);
6599 }
6600 return Code::cast(code);
6601 }
6602
ager@chromium.org5c838252010-02-19 08:53:10 +00006603 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006604}
6605
6606
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006607static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006608 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006609 ASSERT(args.length() == 1);
6610
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006611 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006612
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006613 // If the constructor isn't a proper function we throw a type error.
6614 if (!constructor->IsJSFunction()) {
6615 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6616 Handle<Object> type_error =
6617 Factory::NewTypeError("not_constructor", arguments);
6618 return Top::Throw(*type_error);
6619 }
6620
6621 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006622
6623 // If function should not have prototype, construction is not allowed. In this
6624 // case generated code bailouts here, since function has no initial_map.
6625 if (!function->should_have_prototype()) {
6626 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6627 Handle<Object> type_error =
6628 Factory::NewTypeError("not_constructor", arguments);
6629 return Top::Throw(*type_error);
6630 }
6631
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006632#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006633 // Handle stepping into constructors if step into is active.
6634 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006635 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006636 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006637#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006638
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006639 if (function->has_initial_map()) {
6640 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006641 // The 'Function' function ignores the receiver object when
6642 // called using 'new' and creates a new JSFunction object that
6643 // is returned. The receiver object is only used for error
6644 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006645 // JSFunction. Factory::NewJSObject() should not be used to
6646 // allocate JSFunctions since it does not properly initialize
6647 // the shared part of the function. Since the receiver is
6648 // ignored anyway, we use the global object as the receiver
6649 // instead of a new JSFunction object. This way, errors are
6650 // reported the same way whether or not 'Function' is called
6651 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006652 return Top::context()->global();
6653 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006654 }
6655
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006656 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006657 Handle<SharedFunctionInfo> shared(function->shared());
6658 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006659
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006660 bool first_allocation = !function->has_initial_map();
6661 Handle<JSObject> result = Factory::NewJSObject(function);
6662 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006663 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00006664 ComputeConstructStub(Handle<JSFunction>(function)));
6665 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006666 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006667
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006668 Counters::constructed_objects.Increment();
6669 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006670
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006671 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006672}
6673
6674
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006675static Object* Runtime_LazyCompile(Arguments args) {
6676 HandleScope scope;
6677 ASSERT(args.length() == 1);
6678
6679 Handle<JSFunction> function = args.at<JSFunction>(0);
6680#ifdef DEBUG
6681 if (FLAG_trace_lazy) {
6682 PrintF("[lazy: ");
6683 function->shared()->name()->Print();
6684 PrintF("]\n");
6685 }
6686#endif
6687
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006688 // Compile the target function. Here we compile using CompileLazyInLoop in
6689 // order to get the optimized version. This helps code like delta-blue
6690 // that calls performance-critical routines through constructors. A
6691 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
6692 // direct call. Since the in-loop tracking takes place through CallICs
6693 // this means that things called through constructors are never known to
6694 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006695 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006696 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006697 return Failure::Exception();
6698 }
6699
6700 return function->code();
6701}
6702
6703
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006704static Object* Runtime_GetFunctionDelegate(Arguments args) {
6705 HandleScope scope;
6706 ASSERT(args.length() == 1);
6707 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6708 return *Execution::GetFunctionDelegate(args.at<Object>(0));
6709}
6710
6711
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00006712static Object* Runtime_GetConstructorDelegate(Arguments args) {
6713 HandleScope scope;
6714 ASSERT(args.length() == 1);
6715 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6716 return *Execution::GetConstructorDelegate(args.at<Object>(0));
6717}
6718
6719
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006720static Object* Runtime_NewContext(Arguments args) {
6721 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00006722 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006723
kasper.lund7276f142008-07-30 08:49:36 +00006724 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006725 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
6726 Object* result = Heap::AllocateFunctionContext(length, function);
6727 if (result->IsFailure()) return result;
6728
6729 Top::set_context(Context::cast(result));
6730
kasper.lund7276f142008-07-30 08:49:36 +00006731 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006732}
6733
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006734static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006735 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006736 Object* js_object = object;
6737 if (!js_object->IsJSObject()) {
6738 js_object = js_object->ToObject();
6739 if (js_object->IsFailure()) {
6740 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006741 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006742 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006743 Handle<Object> result =
6744 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
6745 return Top::Throw(*result);
6746 }
6747 }
6748
6749 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006750 Heap::AllocateWithContext(Top::context(),
6751 JSObject::cast(js_object),
6752 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006753 if (result->IsFailure()) return result;
6754
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006755 Context* context = Context::cast(result);
6756 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006757
kasper.lund7276f142008-07-30 08:49:36 +00006758 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006759}
6760
6761
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006762static Object* Runtime_PushContext(Arguments args) {
6763 NoHandleAllocation ha;
6764 ASSERT(args.length() == 1);
6765 return PushContextHelper(args[0], false);
6766}
6767
6768
6769static Object* Runtime_PushCatchContext(Arguments args) {
6770 NoHandleAllocation ha;
6771 ASSERT(args.length() == 1);
6772 return PushContextHelper(args[0], true);
6773}
6774
6775
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006776static Object* Runtime_LookupContext(Arguments args) {
6777 HandleScope scope;
6778 ASSERT(args.length() == 2);
6779
6780 CONVERT_ARG_CHECKED(Context, context, 0);
6781 CONVERT_ARG_CHECKED(String, name, 1);
6782
6783 int index;
6784 PropertyAttributes attributes;
6785 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006786 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006787 context->Lookup(name, flags, &index, &attributes);
6788
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006789 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006790 ASSERT(holder->IsJSObject());
6791 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006792 }
6793
6794 // No intermediate context found. Use global object by default.
6795 return Top::context()->global();
6796}
6797
6798
ager@chromium.orga1645e22009-09-09 19:27:10 +00006799// A mechanism to return a pair of Object pointers in registers (if possible).
6800// How this is achieved is calling convention-dependent.
6801// All currently supported x86 compiles uses calling conventions that are cdecl
6802// variants where a 64-bit value is returned in two 32-bit registers
6803// (edx:eax on ia32, r1:r0 on ARM).
6804// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
6805// In Win64 calling convention, a struct of two pointers is returned in memory,
6806// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006807#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006808struct ObjectPair {
6809 Object* x;
6810 Object* y;
6811};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006812
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006813static inline ObjectPair MakePair(Object* x, Object* y) {
6814 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006815 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
6816 // In Win64 they are assigned to a hidden first argument.
6817 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006818}
6819#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006820typedef uint64_t ObjectPair;
6821static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006822 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006823 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006824}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006825#endif
6826
6827
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006828static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006829 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
6830 USE(attributes);
6831 return x->IsTheHole() ? Heap::undefined_value() : x;
6832}
6833
6834
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006835static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
6836 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006837 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006838 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006839 JSFunction* context_extension_function =
6840 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006841 // If the holder isn't a context extension object, we just return it
6842 // as the receiver. This allows arguments objects to be used as
6843 // receivers, but only if they are put in the context scope chain
6844 // explicitly via a with-statement.
6845 Object* constructor = holder->map()->constructor();
6846 if (constructor != context_extension_function) return holder;
6847 // Fall back to using the global object as the receiver if the
6848 // property turns out to be a local variable allocated in a context
6849 // extension object - introduced via eval.
6850 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006851}
6852
6853
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006854static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006855 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00006856 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006857
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006858 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00006859 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006860 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006861 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006862 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006863
6864 int index;
6865 PropertyAttributes attributes;
6866 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006867 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006868 context->Lookup(name, flags, &index, &attributes);
6869
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006870 // If the index is non-negative, the slot has been found in a local
6871 // variable or a parameter. Read it from the context object or the
6872 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006873 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006874 // If the "property" we were looking for is a local variable or an
6875 // argument in a context, the receiver is the global object; see
6876 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
6877 JSObject* receiver = Top::context()->global()->global_receiver();
6878 Object* value = (holder->IsContext())
6879 ? Context::cast(*holder)->get(index)
6880 : JSObject::cast(*holder)->GetElement(index);
6881 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006882 }
6883
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006884 // If the holder is found, we read the property from it.
6885 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006886 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006887 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006888 JSObject* receiver;
6889 if (object->IsGlobalObject()) {
6890 receiver = GlobalObject::cast(object)->global_receiver();
6891 } else if (context->is_exception_holder(*holder)) {
6892 receiver = Top::context()->global()->global_receiver();
6893 } else {
6894 receiver = ComputeReceiverForNonGlobal(object);
6895 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006896 // No need to unhole the value here. This is taken care of by the
6897 // GetProperty function.
6898 Object* value = object->GetProperty(*name);
6899 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006900 }
6901
6902 if (throw_error) {
6903 // The property doesn't exist - throw exception.
6904 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006905 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006906 return MakePair(Top::Throw(*reference_error), NULL);
6907 } else {
6908 // The property doesn't exist - return undefined
6909 return MakePair(Heap::undefined_value(), Heap::undefined_value());
6910 }
6911}
6912
6913
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006914static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006915 return LoadContextSlotHelper(args, true);
6916}
6917
6918
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006919static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006920 return LoadContextSlotHelper(args, false);
6921}
6922
6923
6924static Object* Runtime_StoreContextSlot(Arguments args) {
6925 HandleScope scope;
6926 ASSERT(args.length() == 3);
6927
6928 Handle<Object> value(args[0]);
6929 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006930 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006931
6932 int index;
6933 PropertyAttributes attributes;
6934 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006935 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006936 context->Lookup(name, flags, &index, &attributes);
6937
6938 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006939 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006940 // Ignore if read_only variable.
6941 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006942 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006943 }
6944 } else {
6945 ASSERT((attributes & READ_ONLY) == 0);
6946 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006947 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006948 USE(result);
6949 ASSERT(!result->IsFailure());
6950 }
6951 return *value;
6952 }
6953
6954 // Slow case: The property is not in a FixedArray context.
6955 // It is either in an JSObject extension context or it was not found.
6956 Handle<JSObject> context_ext;
6957
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006958 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006959 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006960 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006961 } else {
6962 // The property was not found. It needs to be stored in the global context.
6963 ASSERT(attributes == ABSENT);
6964 attributes = NONE;
6965 context_ext = Handle<JSObject>(Top::context()->global());
6966 }
6967
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006968 // Set the property, but ignore if read_only variable on the context
6969 // extension object itself.
6970 if ((attributes & READ_ONLY) == 0 ||
6971 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006972 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
6973 if (set.is_null()) {
6974 // Failure::Exception is converted to a null handle in the
6975 // handle-based methods such as SetProperty. We therefore need
6976 // to convert null handles back to exceptions.
6977 ASSERT(Top::has_pending_exception());
6978 return Failure::Exception();
6979 }
6980 }
6981 return *value;
6982}
6983
6984
6985static Object* Runtime_Throw(Arguments args) {
6986 HandleScope scope;
6987 ASSERT(args.length() == 1);
6988
6989 return Top::Throw(args[0]);
6990}
6991
6992
6993static Object* Runtime_ReThrow(Arguments args) {
6994 HandleScope scope;
6995 ASSERT(args.length() == 1);
6996
6997 return Top::ReThrow(args[0]);
6998}
6999
7000
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007001static Object* Runtime_PromoteScheduledException(Arguments args) {
7002 ASSERT_EQ(0, args.length());
7003 return Top::PromoteScheduledException();
7004}
7005
7006
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007007static Object* Runtime_ThrowReferenceError(Arguments args) {
7008 HandleScope scope;
7009 ASSERT(args.length() == 1);
7010
7011 Handle<Object> name(args[0]);
7012 Handle<Object> reference_error =
7013 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
7014 return Top::Throw(*reference_error);
7015}
7016
7017
7018static Object* Runtime_StackOverflow(Arguments args) {
7019 NoHandleAllocation na;
7020 return Top::StackOverflow();
7021}
7022
7023
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007024static Object* Runtime_StackGuard(Arguments args) {
7025 ASSERT(args.length() == 1);
7026
7027 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007028 if (StackGuard::IsStackOverflow()) {
7029 return Runtime_StackOverflow(args);
7030 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007031
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007032 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007033}
7034
7035
7036// NOTE: These PrintXXX functions are defined for all builds (not just
7037// DEBUG builds) because we may want to be able to trace function
7038// calls in all modes.
7039static void PrintString(String* str) {
7040 // not uncommon to have empty strings
7041 if (str->length() > 0) {
7042 SmartPointer<char> s =
7043 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
7044 PrintF("%s", *s);
7045 }
7046}
7047
7048
7049static void PrintObject(Object* obj) {
7050 if (obj->IsSmi()) {
7051 PrintF("%d", Smi::cast(obj)->value());
7052 } else if (obj->IsString() || obj->IsSymbol()) {
7053 PrintString(String::cast(obj));
7054 } else if (obj->IsNumber()) {
7055 PrintF("%g", obj->Number());
7056 } else if (obj->IsFailure()) {
7057 PrintF("<failure>");
7058 } else if (obj->IsUndefined()) {
7059 PrintF("<undefined>");
7060 } else if (obj->IsNull()) {
7061 PrintF("<null>");
7062 } else if (obj->IsTrue()) {
7063 PrintF("<true>");
7064 } else if (obj->IsFalse()) {
7065 PrintF("<false>");
7066 } else {
7067 PrintF("%p", obj);
7068 }
7069}
7070
7071
7072static int StackSize() {
7073 int n = 0;
7074 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
7075 return n;
7076}
7077
7078
7079static void PrintTransition(Object* result) {
7080 // indentation
7081 { const int nmax = 80;
7082 int n = StackSize();
7083 if (n <= nmax)
7084 PrintF("%4d:%*s", n, n, "");
7085 else
7086 PrintF("%4d:%*s", n, nmax, "...");
7087 }
7088
7089 if (result == NULL) {
7090 // constructor calls
7091 JavaScriptFrameIterator it;
7092 JavaScriptFrame* frame = it.frame();
7093 if (frame->IsConstructor()) PrintF("new ");
7094 // function name
7095 Object* fun = frame->function();
7096 if (fun->IsJSFunction()) {
7097 PrintObject(JSFunction::cast(fun)->shared()->name());
7098 } else {
7099 PrintObject(fun);
7100 }
7101 // function arguments
7102 // (we are intentionally only printing the actually
7103 // supplied parameters, not all parameters required)
7104 PrintF("(this=");
7105 PrintObject(frame->receiver());
7106 const int length = frame->GetProvidedParametersCount();
7107 for (int i = 0; i < length; i++) {
7108 PrintF(", ");
7109 PrintObject(frame->GetParameter(i));
7110 }
7111 PrintF(") {\n");
7112
7113 } else {
7114 // function result
7115 PrintF("} -> ");
7116 PrintObject(result);
7117 PrintF("\n");
7118 }
7119}
7120
7121
7122static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007123 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007124 NoHandleAllocation ha;
7125 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007126 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007127}
7128
7129
7130static Object* Runtime_TraceExit(Arguments args) {
7131 NoHandleAllocation ha;
7132 PrintTransition(args[0]);
7133 return args[0]; // return TOS
7134}
7135
7136
7137static Object* Runtime_DebugPrint(Arguments args) {
7138 NoHandleAllocation ha;
7139 ASSERT(args.length() == 1);
7140
7141#ifdef DEBUG
7142 if (args[0]->IsString()) {
7143 // If we have a string, assume it's a code "marker"
7144 // and print some interesting cpu debugging info.
7145 JavaScriptFrameIterator it;
7146 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007147 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
7148 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007149 } else {
7150 PrintF("DebugPrint: ");
7151 }
7152 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007153 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007154 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007155 HeapObject::cast(args[0])->map()->Print();
7156 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007157#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007158 // ShortPrint is available in release mode. Print is not.
7159 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007160#endif
7161 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00007162 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007163
7164 return args[0]; // return TOS
7165}
7166
7167
7168static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007169 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007170 NoHandleAllocation ha;
7171 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007172 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007173}
7174
7175
mads.s.ager31e71382008-08-13 09:32:07 +00007176static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007177 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007178 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007179
7180 // According to ECMA-262, section 15.9.1, page 117, the precision of
7181 // the number in a Date object representing a particular instant in
7182 // time is milliseconds. Therefore, we floor the result of getting
7183 // the OS time.
7184 double millis = floor(OS::TimeCurrentMillis());
7185 return Heap::NumberFromDouble(millis);
7186}
7187
7188
7189static Object* Runtime_DateParseString(Arguments args) {
7190 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007191 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007192
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007193 CONVERT_ARG_CHECKED(String, str, 0);
7194 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007195
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007196 CONVERT_ARG_CHECKED(JSArray, output, 1);
7197 RUNTIME_ASSERT(output->HasFastElements());
7198
7199 AssertNoAllocation no_allocation;
7200
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007201 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007202 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
7203 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007204 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007205 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007206 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007207 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007208 result = DateParser::Parse(str->ToUC16Vector(), output_array);
7209 }
7210
7211 if (result) {
7212 return *output;
7213 } else {
7214 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007215 }
7216}
7217
7218
7219static Object* Runtime_DateLocalTimezone(Arguments args) {
7220 NoHandleAllocation ha;
7221 ASSERT(args.length() == 1);
7222
7223 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007224 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007225 return Heap::AllocateStringFromUtf8(CStrVector(zone));
7226}
7227
7228
7229static Object* Runtime_DateLocalTimeOffset(Arguments args) {
7230 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007231 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007232
7233 return Heap::NumberFromDouble(OS::LocalTimeOffset());
7234}
7235
7236
7237static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
7238 NoHandleAllocation ha;
7239 ASSERT(args.length() == 1);
7240
7241 CONVERT_DOUBLE_CHECKED(x, args[0]);
7242 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
7243}
7244
7245
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007246static Object* Runtime_GlobalReceiver(Arguments args) {
7247 ASSERT(args.length() == 1);
7248 Object* global = args[0];
7249 if (!global->IsJSGlobalObject()) return Heap::null_value();
7250 return JSGlobalObject::cast(global)->global_receiver();
7251}
7252
7253
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007254static Object* Runtime_CompileString(Arguments args) {
7255 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007256 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007257 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007258 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007259
ager@chromium.org381abbb2009-02-25 13:23:22 +00007260 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007261 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007262 Compiler::ValidationState validate = (is_json->IsTrue())
7263 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007264 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
7265 context,
7266 true,
7267 validate);
7268 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007269 Handle<JSFunction> fun =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007270 Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007271 return *fun;
7272}
7273
7274
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007275static ObjectPair CompileGlobalEval(Handle<String> source,
7276 Handle<Object> receiver) {
7277 // Deal with a normal eval call with a string argument. Compile it
7278 // and return the compiled function bound in the local context.
7279 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
7280 source,
7281 Handle<Context>(Top::context()),
7282 Top::context()->IsGlobalContext(),
7283 Compiler::DONT_VALIDATE_JSON);
7284 if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
7285 Handle<JSFunction> compiled = Factory::NewFunctionFromSharedFunctionInfo(
7286 shared,
7287 Handle<Context>(Top::context()),
7288 NOT_TENURED);
7289 return MakePair(*compiled, *receiver);
7290}
7291
7292
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007293static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
7294 ASSERT(args.length() == 3);
7295 if (!args[0]->IsJSFunction()) {
7296 return MakePair(Top::ThrowIllegalOperation(), NULL);
7297 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007298
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007299 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007300 Handle<JSFunction> callee = args.at<JSFunction>(0);
7301 Handle<Object> receiver; // Will be overwritten.
7302
7303 // Compute the calling context.
7304 Handle<Context> context = Handle<Context>(Top::context());
7305#ifdef DEBUG
7306 // Make sure Top::context() agrees with the old code that traversed
7307 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007308 StackFrameLocator locator;
7309 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007310 ASSERT(Context::cast(frame->context()) == *context);
7311#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007312
7313 // Find where the 'eval' symbol is bound. It is unaliased only if
7314 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007315 int index = -1;
7316 PropertyAttributes attributes = ABSENT;
7317 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007318 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
7319 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007320 // Stop search when eval is found or when the global context is
7321 // reached.
7322 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007323 if (context->is_function_context()) {
7324 context = Handle<Context>(Context::cast(context->closure()->context()));
7325 } else {
7326 context = Handle<Context>(context->previous());
7327 }
7328 }
7329
iposva@chromium.org245aa852009-02-10 00:49:54 +00007330 // If eval could not be resolved, it has been deleted and we need to
7331 // throw a reference error.
7332 if (attributes == ABSENT) {
7333 Handle<Object> name = Factory::eval_symbol();
7334 Handle<Object> reference_error =
7335 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007336 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007337 }
7338
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007339 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007340 // 'eval' is not bound in the global context. Just call the function
7341 // with the given arguments. This is not necessarily the global eval.
7342 if (receiver->IsContext()) {
7343 context = Handle<Context>::cast(receiver);
7344 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007345 } else if (receiver->IsJSContextExtensionObject()) {
7346 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007347 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007348 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007349 }
7350
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007351 // 'eval' is bound in the global context, but it may have been overwritten.
7352 // Compare it to the builtin 'GlobalEval' function to make sure.
7353 if (*callee != Top::global_context()->global_eval_fun() ||
7354 !args[1]->IsString()) {
7355 return MakePair(*callee, Top::context()->global()->global_receiver());
7356 }
7357
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007358 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
7359}
7360
7361
7362static ObjectPair Runtime_ResolvePossiblyDirectEvalNoLookup(Arguments args) {
7363 ASSERT(args.length() == 3);
7364 if (!args[0]->IsJSFunction()) {
7365 return MakePair(Top::ThrowIllegalOperation(), NULL);
7366 }
7367
7368 HandleScope scope;
7369 Handle<JSFunction> callee = args.at<JSFunction>(0);
7370
7371 // 'eval' is bound in the global context, but it may have been overwritten.
7372 // Compare it to the builtin 'GlobalEval' function to make sure.
7373 if (*callee != Top::global_context()->global_eval_fun() ||
7374 !args[1]->IsString()) {
7375 return MakePair(*callee, Top::context()->global()->global_receiver());
7376 }
7377
7378 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007379}
7380
7381
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007382static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
7383 // This utility adjusts the property attributes for newly created Function
7384 // object ("new Function(...)") by changing the map.
7385 // All it does is changing the prototype property to enumerable
7386 // as specified in ECMA262, 15.3.5.2.
7387 HandleScope scope;
7388 ASSERT(args.length() == 1);
7389 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7390 ASSERT(func->map()->instance_type() ==
7391 Top::function_instance_map()->instance_type());
7392 ASSERT(func->map()->instance_size() ==
7393 Top::function_instance_map()->instance_size());
7394 func->set_map(*Top::function_instance_map());
7395 return *func;
7396}
7397
7398
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007399// Push an array unto an array of arrays if it is not already in the
7400// array. Returns true if the element was pushed on the stack and
7401// false otherwise.
7402static Object* Runtime_PushIfAbsent(Arguments args) {
7403 ASSERT(args.length() == 2);
7404 CONVERT_CHECKED(JSArray, array, args[0]);
7405 CONVERT_CHECKED(JSArray, element, args[1]);
7406 RUNTIME_ASSERT(array->HasFastElements());
7407 int length = Smi::cast(array->length())->value();
7408 FixedArray* elements = FixedArray::cast(array->elements());
7409 for (int i = 0; i < length; i++) {
7410 if (elements->get(i) == element) return Heap::false_value();
7411 }
7412 Object* obj = array->SetFastElement(length, element);
7413 if (obj->IsFailure()) return obj;
7414 return Heap::true_value();
7415}
7416
7417
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007418/**
7419 * A simple visitor visits every element of Array's.
7420 * The backend storage can be a fixed array for fast elements case,
7421 * or a dictionary for sparse array. Since Dictionary is a subtype
7422 * of FixedArray, the class can be used by both fast and slow cases.
7423 * The second parameter of the constructor, fast_elements, specifies
7424 * whether the storage is a FixedArray or Dictionary.
7425 *
7426 * An index limit is used to deal with the situation that a result array
7427 * length overflows 32-bit non-negative integer.
7428 */
7429class ArrayConcatVisitor {
7430 public:
7431 ArrayConcatVisitor(Handle<FixedArray> storage,
7432 uint32_t index_limit,
7433 bool fast_elements) :
7434 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007435 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007436
7437 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007438 if (i >= index_limit_ - index_offset_) return;
7439 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007440
7441 if (fast_elements_) {
7442 ASSERT(index < static_cast<uint32_t>(storage_->length()));
7443 storage_->set(index, *elm);
7444
7445 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007446 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
7447 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007448 Factory::DictionaryAtNumberPut(dict, index, elm);
7449 if (!result.is_identical_to(dict))
7450 storage_ = result;
7451 }
7452 }
7453
7454 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007455 if (index_limit_ - index_offset_ < delta) {
7456 index_offset_ = index_limit_;
7457 } else {
7458 index_offset_ += delta;
7459 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007460 }
7461
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007462 Handle<FixedArray> storage() { return storage_; }
7463
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007464 private:
7465 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007466 // Limit on the accepted indices. Elements with indices larger than the
7467 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007468 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007469 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007470 uint32_t index_offset_;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007471 const bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007472};
7473
7474
ager@chromium.org3811b432009-10-28 14:53:37 +00007475template<class ExternalArrayClass, class ElementType>
7476static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
7477 bool elements_are_ints,
7478 bool elements_are_guaranteed_smis,
7479 uint32_t range,
7480 ArrayConcatVisitor* visitor) {
7481 Handle<ExternalArrayClass> array(
7482 ExternalArrayClass::cast(receiver->elements()));
7483 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
7484
7485 if (visitor != NULL) {
7486 if (elements_are_ints) {
7487 if (elements_are_guaranteed_smis) {
7488 for (uint32_t j = 0; j < len; j++) {
7489 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
7490 visitor->visit(j, e);
7491 }
7492 } else {
7493 for (uint32_t j = 0; j < len; j++) {
7494 int64_t val = static_cast<int64_t>(array->get(j));
7495 if (Smi::IsValid(static_cast<intptr_t>(val))) {
7496 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
7497 visitor->visit(j, e);
7498 } else {
7499 Handle<Object> e(
7500 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
7501 visitor->visit(j, e);
7502 }
7503 }
7504 }
7505 } else {
7506 for (uint32_t j = 0; j < len; j++) {
7507 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
7508 visitor->visit(j, e);
7509 }
7510 }
7511 }
7512
7513 return len;
7514}
7515
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007516/**
7517 * A helper function that visits elements of a JSObject. Only elements
7518 * whose index between 0 and range (exclusive) are visited.
7519 *
7520 * If the third parameter, visitor, is not NULL, the visitor is called
7521 * with parameters, 'visitor_index_offset + element index' and the element.
7522 *
7523 * It returns the number of visisted elements.
7524 */
7525static uint32_t IterateElements(Handle<JSObject> receiver,
7526 uint32_t range,
7527 ArrayConcatVisitor* visitor) {
7528 uint32_t num_of_elements = 0;
7529
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007530 switch (receiver->GetElementsKind()) {
7531 case JSObject::FAST_ELEMENTS: {
7532 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
7533 uint32_t len = elements->length();
7534 if (range < len) {
7535 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007536 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007537
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007538 for (uint32_t j = 0; j < len; j++) {
7539 Handle<Object> e(elements->get(j));
7540 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007541 num_of_elements++;
7542 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007543 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007544 }
7545 }
7546 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007547 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007548 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007549 case JSObject::PIXEL_ELEMENTS: {
7550 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
7551 uint32_t len = pixels->length();
7552 if (range < len) {
7553 len = range;
7554 }
7555
7556 for (uint32_t j = 0; j < len; j++) {
7557 num_of_elements++;
7558 if (visitor != NULL) {
7559 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
7560 visitor->visit(j, e);
7561 }
7562 }
7563 break;
7564 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007565 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
7566 num_of_elements =
7567 IterateExternalArrayElements<ExternalByteArray, int8_t>(
7568 receiver, true, true, range, visitor);
7569 break;
7570 }
7571 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7572 num_of_elements =
7573 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
7574 receiver, true, true, range, visitor);
7575 break;
7576 }
7577 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
7578 num_of_elements =
7579 IterateExternalArrayElements<ExternalShortArray, int16_t>(
7580 receiver, true, true, range, visitor);
7581 break;
7582 }
7583 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7584 num_of_elements =
7585 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
7586 receiver, true, true, range, visitor);
7587 break;
7588 }
7589 case JSObject::EXTERNAL_INT_ELEMENTS: {
7590 num_of_elements =
7591 IterateExternalArrayElements<ExternalIntArray, int32_t>(
7592 receiver, true, false, range, visitor);
7593 break;
7594 }
7595 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7596 num_of_elements =
7597 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
7598 receiver, true, false, range, visitor);
7599 break;
7600 }
7601 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
7602 num_of_elements =
7603 IterateExternalArrayElements<ExternalFloatArray, float>(
7604 receiver, false, false, range, visitor);
7605 break;
7606 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007607 case JSObject::DICTIONARY_ELEMENTS: {
7608 Handle<NumberDictionary> dict(receiver->element_dictionary());
7609 uint32_t capacity = dict->Capacity();
7610 for (uint32_t j = 0; j < capacity; j++) {
7611 Handle<Object> k(dict->KeyAt(j));
7612 if (dict->IsKey(*k)) {
7613 ASSERT(k->IsNumber());
7614 uint32_t index = static_cast<uint32_t>(k->Number());
7615 if (index < range) {
7616 num_of_elements++;
7617 if (visitor) {
7618 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
7619 }
7620 }
7621 }
7622 }
7623 break;
7624 }
7625 default:
7626 UNREACHABLE();
7627 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007628 }
7629
7630 return num_of_elements;
7631}
7632
7633
7634/**
7635 * A helper function that visits elements of an Array object, and elements
7636 * on its prototypes.
7637 *
7638 * Elements on prototypes are visited first, and only elements whose indices
7639 * less than Array length are visited.
7640 *
7641 * If a ArrayConcatVisitor object is given, the visitor is called with
7642 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007643 *
7644 * The returned number of elements is an upper bound on the actual number
7645 * of elements added. If the same element occurs in more than one object
7646 * in the array's prototype chain, it will be counted more than once, but
7647 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007648 */
7649static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
7650 ArrayConcatVisitor* visitor) {
7651 uint32_t range = static_cast<uint32_t>(array->length()->Number());
7652 Handle<Object> obj = array;
7653
7654 static const int kEstimatedPrototypes = 3;
7655 List< Handle<JSObject> > objects(kEstimatedPrototypes);
7656
7657 // Visit prototype first. If an element on the prototype is shadowed by
7658 // the inheritor using the same index, the ArrayConcatVisitor visits
7659 // the prototype element before the shadowing element.
7660 // The visitor can simply overwrite the old value by new value using
7661 // the same index. This follows Array::concat semantics.
7662 while (!obj->IsNull()) {
7663 objects.Add(Handle<JSObject>::cast(obj));
7664 obj = Handle<Object>(obj->GetPrototype());
7665 }
7666
7667 uint32_t nof_elements = 0;
7668 for (int i = objects.length() - 1; i >= 0; i--) {
7669 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007670 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007671 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007672
7673 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
7674 nof_elements = JSObject::kMaxElementCount;
7675 } else {
7676 nof_elements += encountered_elements;
7677 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007678 }
7679
7680 return nof_elements;
7681}
7682
7683
7684/**
7685 * A helper function of Runtime_ArrayConcat.
7686 *
7687 * The first argument is an Array of arrays and objects. It is the
7688 * same as the arguments array of Array::concat JS function.
7689 *
7690 * If an argument is an Array object, the function visits array
7691 * elements. If an argument is not an Array object, the function
7692 * visits the object as if it is an one-element array.
7693 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007694 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007695 * non-negative number is used as new length. For example, if one
7696 * array length is 2^32 - 1, second array length is 1, the
7697 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007698 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
7699 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007700 */
7701static uint32_t IterateArguments(Handle<JSArray> arguments,
7702 ArrayConcatVisitor* visitor) {
7703 uint32_t visited_elements = 0;
7704 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7705
7706 for (uint32_t i = 0; i < num_of_args; i++) {
7707 Handle<Object> obj(arguments->GetElement(i));
7708 if (obj->IsJSArray()) {
7709 Handle<JSArray> array = Handle<JSArray>::cast(obj);
7710 uint32_t len = static_cast<uint32_t>(array->length()->Number());
7711 uint32_t nof_elements =
7712 IterateArrayAndPrototypeElements(array, visitor);
7713 // Total elements of array and its prototype chain can be more than
7714 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007715 // the array length number of elements. We use the length as an estimate
7716 // for the actual number of elements added.
7717 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
7718 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
7719 visited_elements = JSArray::kMaxElementCount;
7720 } else {
7721 visited_elements += added_elements;
7722 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007723 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007724 } else {
7725 if (visitor) {
7726 visitor->visit(0, obj);
7727 visitor->increase_index_offset(1);
7728 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007729 if (visited_elements < JSArray::kMaxElementCount) {
7730 visited_elements++;
7731 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007732 }
7733 }
7734 return visited_elements;
7735}
7736
7737
7738/**
7739 * Array::concat implementation.
7740 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007741 * TODO(lrn): Fix non-compliance for very large concatenations and update to
7742 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007743 */
7744static Object* Runtime_ArrayConcat(Arguments args) {
7745 ASSERT(args.length() == 1);
7746 HandleScope handle_scope;
7747
7748 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
7749 Handle<JSArray> arguments(arg_arrays);
7750
7751 // Pass 1: estimate the number of elements of the result
7752 // (it could be more than real numbers if prototype has elements).
7753 uint32_t result_length = 0;
7754 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7755
7756 { AssertNoAllocation nogc;
7757 for (uint32_t i = 0; i < num_of_args; i++) {
7758 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007759 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007760 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007761 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007762 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
7763 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007764 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007765 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007766 if (JSObject::kMaxElementCount - result_length < length_estimate) {
7767 result_length = JSObject::kMaxElementCount;
7768 break;
7769 }
7770 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007771 }
7772 }
7773
7774 // Allocate an empty array, will set length and content later.
7775 Handle<JSArray> result = Factory::NewJSArray(0);
7776
7777 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
7778 // If estimated number of elements is more than half of length, a
7779 // fixed array (fast case) is more time and space-efficient than a
7780 // dictionary.
7781 bool fast_case = (estimate_nof_elements * 2) >= result_length;
7782
7783 Handle<FixedArray> storage;
7784 if (fast_case) {
7785 // The backing storage array must have non-existing elements to
7786 // preserve holes across concat operations.
7787 storage = Factory::NewFixedArrayWithHoles(result_length);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007788 result->set_map(*Factory::GetFastElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007789 } else {
7790 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
7791 uint32_t at_least_space_for = estimate_nof_elements +
7792 (estimate_nof_elements >> 2);
7793 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007794 Factory::NewNumberDictionary(at_least_space_for));
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007795 result->set_map(*Factory::GetSlowElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007796 }
7797
7798 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
7799
7800 ArrayConcatVisitor visitor(storage, result_length, fast_case);
7801
7802 IterateArguments(arguments, &visitor);
7803
7804 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007805 // Please note the storage might have changed in the visitor.
7806 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007807
7808 return *result;
7809}
7810
7811
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007812// This will not allocate (flatten the string), but it may run
7813// very slowly for very deeply nested ConsStrings. For debugging use only.
7814static Object* Runtime_GlobalPrint(Arguments args) {
7815 NoHandleAllocation ha;
7816 ASSERT(args.length() == 1);
7817
7818 CONVERT_CHECKED(String, string, args[0]);
7819 StringInputBuffer buffer(string);
7820 while (buffer.has_more()) {
7821 uint16_t character = buffer.GetNext();
7822 PrintF("%c", character);
7823 }
7824 return string;
7825}
7826
ager@chromium.org5ec48922009-05-05 07:25:34 +00007827// Moves all own elements of an object, that are below a limit, to positions
7828// starting at zero. All undefined values are placed after non-undefined values,
7829// and are followed by non-existing element. Does not change the length
7830// property.
7831// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007832static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007833 ASSERT(args.length() == 2);
7834 CONVERT_CHECKED(JSObject, object, args[0]);
7835 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
7836 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007837}
7838
7839
7840// Move contents of argument 0 (an array) to argument 1 (an array)
7841static Object* Runtime_MoveArrayContents(Arguments args) {
7842 ASSERT(args.length() == 2);
7843 CONVERT_CHECKED(JSArray, from, args[0]);
7844 CONVERT_CHECKED(JSArray, to, args[1]);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007845 HeapObject* new_elements = from->elements();
7846 Object* new_map;
7847 if (new_elements->map() == Heap::fixed_array_map()) {
7848 new_map = to->map()->GetFastElementsMap();
7849 } else {
7850 new_map = to->map()->GetSlowElementsMap();
7851 }
7852 if (new_map->IsFailure()) return new_map;
7853 to->set_map(Map::cast(new_map));
7854 to->set_elements(new_elements);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007855 to->set_length(from->length());
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007856 Object* obj = from->ResetElements();
7857 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007858 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007859 return to;
7860}
7861
7862
7863// How many elements does this array have?
7864static Object* Runtime_EstimateNumberOfElements(Arguments args) {
7865 ASSERT(args.length() == 1);
7866 CONVERT_CHECKED(JSArray, array, args[0]);
7867 HeapObject* elements = array->elements();
7868 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007869 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007870 } else {
7871 return array->length();
7872 }
7873}
7874
7875
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007876static Object* Runtime_SwapElements(Arguments args) {
7877 HandleScope handle_scope;
7878
7879 ASSERT_EQ(3, args.length());
7880
ager@chromium.orgac091b72010-05-05 07:34:42 +00007881 CONVERT_ARG_CHECKED(JSObject, object, 0);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007882 Handle<Object> key1 = args.at<Object>(1);
7883 Handle<Object> key2 = args.at<Object>(2);
7884
7885 uint32_t index1, index2;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007886 if (!key1->ToArrayIndex(&index1)
7887 || !key2->ToArrayIndex(&index2)) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00007888 return Top::ThrowIllegalOperation();
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007889 }
7890
ager@chromium.orgac091b72010-05-05 07:34:42 +00007891 Handle<JSObject> jsobject = Handle<JSObject>::cast(object);
7892 Handle<Object> tmp1 = GetElement(jsobject, index1);
7893 Handle<Object> tmp2 = GetElement(jsobject, index2);
7894
7895 SetElement(jsobject, index1, tmp2);
7896 SetElement(jsobject, index2, tmp1);
7897
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007898 return Heap::undefined_value();
7899}
7900
7901
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007902// Returns an array that tells you where in the [0, length) interval an array
7903// might have elements. Can either return keys or intervals. Keys can have
7904// gaps in (undefined). Intervals can also span over some undefined keys.
7905static Object* Runtime_GetArrayKeys(Arguments args) {
7906 ASSERT(args.length() == 2);
7907 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007908 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007909 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007910 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007911 // Create an array and get all the keys into it, then remove all the
7912 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007913 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007914 int keys_length = keys->length();
7915 for (int i = 0; i < keys_length; i++) {
7916 Object* key = keys->get(i);
7917 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007918 if (!key->ToArrayIndex(&index) || index >= length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007919 // Zap invalid keys.
7920 keys->set_undefined(i);
7921 }
7922 }
7923 return *Factory::NewJSArrayWithElements(keys);
7924 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007925 ASSERT(array->HasFastElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007926 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
7927 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007928 single_interval->set(0, Smi::FromInt(-1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007929 uint32_t actual_length =
7930 static_cast<uint32_t>(FixedArray::cast(array->elements())->length());
ager@chromium.org5ec48922009-05-05 07:25:34 +00007931 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007932 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00007933 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007934 single_interval->set(1, *length_object);
7935 return *Factory::NewJSArrayWithElements(single_interval);
7936 }
7937}
7938
7939
7940// DefineAccessor takes an optional final argument which is the
7941// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
7942// to the way accessors are implemented, it is set for both the getter
7943// and setter on the first call to DefineAccessor and ignored on
7944// subsequent calls.
7945static Object* Runtime_DefineAccessor(Arguments args) {
7946 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
7947 // Compute attributes.
7948 PropertyAttributes attributes = NONE;
7949 if (args.length() == 5) {
7950 CONVERT_CHECKED(Smi, attrs, args[4]);
7951 int value = attrs->value();
7952 // Only attribute bits should be set.
7953 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
7954 attributes = static_cast<PropertyAttributes>(value);
7955 }
7956
7957 CONVERT_CHECKED(JSObject, obj, args[0]);
7958 CONVERT_CHECKED(String, name, args[1]);
7959 CONVERT_CHECKED(Smi, flag, args[2]);
7960 CONVERT_CHECKED(JSFunction, fun, args[3]);
7961 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
7962}
7963
7964
7965static Object* Runtime_LookupAccessor(Arguments args) {
7966 ASSERT(args.length() == 3);
7967 CONVERT_CHECKED(JSObject, obj, args[0]);
7968 CONVERT_CHECKED(String, name, args[1]);
7969 CONVERT_CHECKED(Smi, flag, args[2]);
7970 return obj->LookupAccessor(name, flag->value() == 0);
7971}
7972
7973
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007974#ifdef ENABLE_DEBUGGER_SUPPORT
7975static Object* Runtime_DebugBreak(Arguments args) {
7976 ASSERT(args.length() == 0);
7977 return Execution::DebugBreakHelper();
7978}
7979
7980
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007981// Helper functions for wrapping and unwrapping stack frame ids.
7982static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007983 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007984 return Smi::FromInt(id >> 2);
7985}
7986
7987
7988static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
7989 return static_cast<StackFrame::Id>(wrapped->value() << 2);
7990}
7991
7992
7993// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00007994// args[0]: debug event listener function to set or null or undefined for
7995// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007996// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00007997static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007998 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007999 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
8000 args[0]->IsUndefined() ||
8001 args[0]->IsNull());
8002 Handle<Object> callback = args.at<Object>(0);
8003 Handle<Object> data = args.at<Object>(1);
8004 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008005
8006 return Heap::undefined_value();
8007}
8008
8009
8010static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00008011 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008012 StackGuard::DebugBreak();
8013 return Heap::undefined_value();
8014}
8015
8016
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008017static Object* DebugLookupResultValue(Object* receiver, String* name,
8018 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00008019 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008020 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008021 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008022 case NORMAL:
8023 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008024 if (value->IsTheHole()) {
8025 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008026 }
8027 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008028 case FIELD:
8029 value =
8030 JSObject::cast(
8031 result->holder())->FastPropertyAt(result->GetFieldIndex());
8032 if (value->IsTheHole()) {
8033 return Heap::undefined_value();
8034 }
8035 return value;
8036 case CONSTANT_FUNCTION:
8037 return result->GetConstantFunction();
8038 case CALLBACKS: {
8039 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008040 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008041 value = receiver->GetPropertyWithCallback(
8042 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00008043 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008044 value = Top::pending_exception();
8045 Top::clear_pending_exception();
8046 if (caught_exception != NULL) {
8047 *caught_exception = true;
8048 }
8049 }
8050 return value;
8051 } else {
8052 return Heap::undefined_value();
8053 }
8054 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008055 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008056 case MAP_TRANSITION:
8057 case CONSTANT_TRANSITION:
8058 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008059 return Heap::undefined_value();
8060 default:
8061 UNREACHABLE();
8062 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008063 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008064 return Heap::undefined_value();
8065}
8066
8067
ager@chromium.org32912102009-01-16 10:38:43 +00008068// Get debugger related details for an object property.
8069// args[0]: object holding property
8070// args[1]: name of the property
8071//
8072// The array returned contains the following information:
8073// 0: Property value
8074// 1: Property details
8075// 2: Property value is exception
8076// 3: Getter function if defined
8077// 4: Setter function if defined
8078// Items 2-4 are only filled if the property has either a getter or a setter
8079// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00008080static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008081 HandleScope scope;
8082
8083 ASSERT(args.length() == 2);
8084
8085 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8086 CONVERT_ARG_CHECKED(String, name, 1);
8087
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008088 // Make sure to set the current context to the context before the debugger was
8089 // entered (if the debugger is entered). The reason for switching context here
8090 // is that for some property lookups (accessors and interceptors) callbacks
8091 // into the embedding application can occour, and the embedding application
8092 // could have the assumption that its own global context is the current
8093 // context and not some internal debugger context.
8094 SaveContext save;
8095 if (Debug::InDebugger()) {
8096 Top::set_context(*Debug::debugger_entry()->GetContext());
8097 }
8098
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008099 // Skip the global proxy as it has no properties and always delegates to the
8100 // real global object.
8101 if (obj->IsJSGlobalProxy()) {
8102 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
8103 }
8104
8105
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008106 // Check if the name is trivially convertible to an index and get the element
8107 // if so.
8108 uint32_t index;
8109 if (name->AsArrayIndex(&index)) {
8110 Handle<FixedArray> details = Factory::NewFixedArray(2);
8111 details->set(0, Runtime::GetElementOrCharAt(obj, index));
8112 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
8113 return *Factory::NewJSArrayWithElements(details);
8114 }
8115
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008116 // Find the number of objects making up this.
8117 int length = LocalPrototypeChainLength(*obj);
8118
8119 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008120 Handle<JSObject> jsproto = obj;
8121 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008122 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008123 jsproto->LocalLookup(*name, &result);
8124 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008125 // LookupResult is not GC safe as it holds raw object pointers.
8126 // GC can happen later in this code so put the required fields into
8127 // local variables using handles when required for later use.
8128 PropertyType result_type = result.type();
8129 Handle<Object> result_callback_obj;
8130 if (result_type == CALLBACKS) {
8131 result_callback_obj = Handle<Object>(result.GetCallbackObject());
8132 }
8133 Smi* property_details = result.GetPropertyDetails().AsSmi();
8134 // DebugLookupResultValue can cause GC so details from LookupResult needs
8135 // to be copied to handles before this.
8136 bool caught_exception = false;
8137 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
8138 &caught_exception);
8139 if (raw_value->IsFailure()) return raw_value;
8140 Handle<Object> value(raw_value);
8141
8142 // If the callback object is a fixed array then it contains JavaScript
8143 // getter and/or setter.
8144 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
8145 result_callback_obj->IsFixedArray();
8146 Handle<FixedArray> details =
8147 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
8148 details->set(0, *value);
8149 details->set(1, property_details);
8150 if (hasJavaScriptAccessors) {
8151 details->set(2,
8152 caught_exception ? Heap::true_value()
8153 : Heap::false_value());
8154 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
8155 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
8156 }
8157
8158 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008159 }
8160 if (i < length - 1) {
8161 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
8162 }
8163 }
8164
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008165 return Heap::undefined_value();
8166}
8167
8168
8169static Object* Runtime_DebugGetProperty(Arguments args) {
8170 HandleScope scope;
8171
8172 ASSERT(args.length() == 2);
8173
8174 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8175 CONVERT_ARG_CHECKED(String, name, 1);
8176
8177 LookupResult result;
8178 obj->Lookup(*name, &result);
8179 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008180 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008181 }
8182 return Heap::undefined_value();
8183}
8184
8185
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008186// Return the property type calculated from the property details.
8187// args[0]: smi with property details.
8188static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
8189 ASSERT(args.length() == 1);
8190 CONVERT_CHECKED(Smi, details, args[0]);
8191 PropertyType type = PropertyDetails(details).type();
8192 return Smi::FromInt(static_cast<int>(type));
8193}
8194
8195
8196// Return the property attribute calculated from the property details.
8197// args[0]: smi with property details.
8198static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
8199 ASSERT(args.length() == 1);
8200 CONVERT_CHECKED(Smi, details, args[0]);
8201 PropertyAttributes attributes = PropertyDetails(details).attributes();
8202 return Smi::FromInt(static_cast<int>(attributes));
8203}
8204
8205
8206// Return the property insertion index calculated from the property details.
8207// args[0]: smi with property details.
8208static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
8209 ASSERT(args.length() == 1);
8210 CONVERT_CHECKED(Smi, details, args[0]);
8211 int index = PropertyDetails(details).index();
8212 return Smi::FromInt(index);
8213}
8214
8215
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008216// Return property value from named interceptor.
8217// args[0]: object
8218// args[1]: property name
8219static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
8220 HandleScope scope;
8221 ASSERT(args.length() == 2);
8222 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8223 RUNTIME_ASSERT(obj->HasNamedInterceptor());
8224 CONVERT_ARG_CHECKED(String, name, 1);
8225
8226 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008227 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008228}
8229
8230
8231// Return element value from indexed interceptor.
8232// args[0]: object
8233// args[1]: index
8234static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
8235 HandleScope scope;
8236 ASSERT(args.length() == 2);
8237 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8238 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
8239 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
8240
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008241 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008242}
8243
8244
8245static Object* Runtime_CheckExecutionState(Arguments args) {
8246 ASSERT(args.length() >= 1);
8247 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00008248 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008249 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008250 return Top::Throw(Heap::illegal_execution_state_symbol());
8251 }
8252
8253 return Heap::true_value();
8254}
8255
8256
8257static Object* Runtime_GetFrameCount(Arguments args) {
8258 HandleScope scope;
8259 ASSERT(args.length() == 1);
8260
8261 // Check arguments.
8262 Object* result = Runtime_CheckExecutionState(args);
8263 if (result->IsFailure()) return result;
8264
8265 // Count all frames which are relevant to debugging stack trace.
8266 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008267 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008268 if (id == StackFrame::NO_ID) {
8269 // If there is no JavaScript stack frame count is 0.
8270 return Smi::FromInt(0);
8271 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008272 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
8273 return Smi::FromInt(n);
8274}
8275
8276
8277static const int kFrameDetailsFrameIdIndex = 0;
8278static const int kFrameDetailsReceiverIndex = 1;
8279static const int kFrameDetailsFunctionIndex = 2;
8280static const int kFrameDetailsArgumentCountIndex = 3;
8281static const int kFrameDetailsLocalCountIndex = 4;
8282static const int kFrameDetailsSourcePositionIndex = 5;
8283static const int kFrameDetailsConstructCallIndex = 6;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008284static const int kFrameDetailsAtReturnIndex = 7;
8285static const int kFrameDetailsDebuggerFrameIndex = 8;
8286static const int kFrameDetailsFirstDynamicIndex = 9;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008287
8288// Return an array with frame details
8289// args[0]: number: break id
8290// args[1]: number: frame index
8291//
8292// The array returned contains the following information:
8293// 0: Frame id
8294// 1: Receiver
8295// 2: Function
8296// 3: Argument count
8297// 4: Local count
8298// 5: Source position
8299// 6: Constructor call
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008300// 7: Is at return
8301// 8: Debugger frame
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008302// Arguments name, value
8303// Locals name, value
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008304// Return value if any
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008305static Object* Runtime_GetFrameDetails(Arguments args) {
8306 HandleScope scope;
8307 ASSERT(args.length() == 2);
8308
8309 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008310 Object* check = Runtime_CheckExecutionState(args);
8311 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008312 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8313
8314 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008315 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008316 if (id == StackFrame::NO_ID) {
8317 // If there are no JavaScript stack frames return undefined.
8318 return Heap::undefined_value();
8319 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008320 int count = 0;
8321 JavaScriptFrameIterator it(id);
8322 for (; !it.done(); it.Advance()) {
8323 if (count == index) break;
8324 count++;
8325 }
8326 if (it.done()) return Heap::undefined_value();
8327
8328 // Traverse the saved contexts chain to find the active context for the
8329 // selected frame.
8330 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008331 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008332 save = save->prev();
8333 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008334 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008335
8336 // Get the frame id.
8337 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
8338
8339 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008340 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008341
8342 // Check for constructor frame.
8343 bool constructor = it.frame()->IsConstructor();
8344
8345 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008346 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008347 ScopeInfo<> info(*code);
8348
8349 // Get the context.
8350 Handle<Context> context(Context::cast(it.frame()->context()));
8351
8352 // Get the locals names and values into a temporary array.
8353 //
8354 // TODO(1240907): Hide compiler-introduced stack variables
8355 // (e.g. .result)? For users of the debugger, they will probably be
8356 // confusing.
8357 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
8358 for (int i = 0; i < info.NumberOfLocals(); i++) {
8359 // Name of the local.
8360 locals->set(i * 2, *info.LocalName(i));
8361
8362 // Fetch the value of the local - either from the stack or from a
8363 // heap-allocated context.
8364 if (i < info.number_of_stack_slots()) {
8365 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
8366 } else {
8367 Handle<String> name = info.LocalName(i);
8368 // Traverse the context chain to the function context as all local
8369 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008370 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008371 context = Handle<Context>(context->previous());
8372 }
8373 ASSERT(context->is_function_context());
8374 locals->set(i * 2 + 1,
8375 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
8376 NULL)));
8377 }
8378 }
8379
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008380 // Check whether this frame is positioned at return.
8381 int at_return = (index == 0) ? Debug::IsBreakAtReturn(it.frame()) : false;
8382
8383 // If positioned just before return find the value to be returned and add it
8384 // to the frame information.
8385 Handle<Object> return_value = Factory::undefined_value();
8386 if (at_return) {
8387 StackFrameIterator it2;
8388 Address internal_frame_sp = NULL;
8389 while (!it2.done()) {
8390 if (it2.frame()->is_internal()) {
8391 internal_frame_sp = it2.frame()->sp();
8392 } else {
8393 if (it2.frame()->is_java_script()) {
8394 if (it2.frame()->id() == it.frame()->id()) {
8395 // The internal frame just before the JavaScript frame contains the
8396 // value to return on top. A debug break at return will create an
8397 // internal frame to store the return value (eax/rax/r0) before
8398 // entering the debug break exit frame.
8399 if (internal_frame_sp != NULL) {
8400 return_value =
8401 Handle<Object>(Memory::Object_at(internal_frame_sp));
8402 break;
8403 }
8404 }
8405 }
8406
8407 // Indicate that the previous frame was not an internal frame.
8408 internal_frame_sp = NULL;
8409 }
8410 it2.Advance();
8411 }
8412 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008413
8414 // Now advance to the arguments adapter frame (if any). It contains all
8415 // the provided parameters whereas the function frame always have the number
8416 // of arguments matching the functions parameters. The rest of the
8417 // information (except for what is collected above) is the same.
8418 it.AdvanceToArgumentsFrame();
8419
8420 // Find the number of arguments to fill. At least fill the number of
8421 // parameters for the function and fill more if more parameters are provided.
8422 int argument_count = info.number_of_parameters();
8423 if (argument_count < it.frame()->GetProvidedParametersCount()) {
8424 argument_count = it.frame()->GetProvidedParametersCount();
8425 }
8426
8427 // Calculate the size of the result.
8428 int details_size = kFrameDetailsFirstDynamicIndex +
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008429 2 * (argument_count + info.NumberOfLocals()) +
8430 (at_return ? 1 : 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008431 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8432
8433 // Add the frame id.
8434 details->set(kFrameDetailsFrameIdIndex, *frame_id);
8435
8436 // Add the function (same as in function frame).
8437 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
8438
8439 // Add the arguments count.
8440 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
8441
8442 // Add the locals count
8443 details->set(kFrameDetailsLocalCountIndex,
8444 Smi::FromInt(info.NumberOfLocals()));
8445
8446 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00008447 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008448 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
8449 } else {
8450 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
8451 }
8452
8453 // Add the constructor information.
8454 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
8455
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008456 // Add the at return information.
8457 details->set(kFrameDetailsAtReturnIndex, Heap::ToBoolean(at_return));
8458
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008459 // Add information on whether this frame is invoked in the debugger context.
8460 details->set(kFrameDetailsDebuggerFrameIndex,
8461 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
8462
8463 // Fill the dynamic part.
8464 int details_index = kFrameDetailsFirstDynamicIndex;
8465
8466 // Add arguments name and value.
8467 for (int i = 0; i < argument_count; i++) {
8468 // Name of the argument.
8469 if (i < info.number_of_parameters()) {
8470 details->set(details_index++, *info.parameter_name(i));
8471 } else {
8472 details->set(details_index++, Heap::undefined_value());
8473 }
8474
8475 // Parameter value.
8476 if (i < it.frame()->GetProvidedParametersCount()) {
8477 details->set(details_index++, it.frame()->GetParameter(i));
8478 } else {
8479 details->set(details_index++, Heap::undefined_value());
8480 }
8481 }
8482
8483 // Add locals name and value from the temporary copy from the function frame.
8484 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
8485 details->set(details_index++, locals->get(i));
8486 }
8487
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008488 // Add the value being returned.
8489 if (at_return) {
8490 details->set(details_index++, *return_value);
8491 }
8492
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008493 // Add the receiver (same as in function frame).
8494 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
8495 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
8496 Handle<Object> receiver(it.frame()->receiver());
8497 if (!receiver->IsJSObject()) {
8498 // If the receiver is NOT a JSObject we have hit an optimization
8499 // where a value object is not converted into a wrapped JS objects.
8500 // To hide this optimization from the debugger, we wrap the receiver
8501 // by creating correct wrapper object based on the calling frame's
8502 // global context.
8503 it.Advance();
8504 Handle<Context> calling_frames_global_context(
8505 Context::cast(Context::cast(it.frame()->context())->global_context()));
8506 receiver = Factory::ToObject(receiver, calling_frames_global_context);
8507 }
8508 details->set(kFrameDetailsReceiverIndex, *receiver);
8509
8510 ASSERT_EQ(details_size, details_index);
8511 return *Factory::NewJSArrayWithElements(details);
8512}
8513
8514
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008515// Copy all the context locals into an object used to materialize a scope.
8516static void CopyContextLocalsToScopeObject(Handle<Code> code,
8517 ScopeInfo<>& scope_info,
8518 Handle<Context> context,
8519 Handle<JSObject> scope_object) {
8520 // Fill all context locals to the context extension.
8521 for (int i = Context::MIN_CONTEXT_SLOTS;
8522 i < scope_info.number_of_context_slots();
8523 i++) {
8524 int context_index =
8525 ScopeInfo<>::ContextSlotIndex(*code,
8526 *scope_info.context_slot_name(i),
8527 NULL);
8528
8529 // Don't include the arguments shadow (.arguments) context variable.
8530 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
8531 SetProperty(scope_object,
8532 scope_info.context_slot_name(i),
8533 Handle<Object>(context->get(context_index)), NONE);
8534 }
8535 }
8536}
8537
8538
8539// Create a plain JSObject which materializes the local scope for the specified
8540// frame.
8541static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
8542 Handle<JSFunction> function(JSFunction::cast(frame->function()));
8543 Handle<Code> code(function->code());
8544 ScopeInfo<> scope_info(*code);
8545
8546 // Allocate and initialize a JSObject with all the arguments, stack locals
8547 // heap locals and extension properties of the debugged function.
8548 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
8549
8550 // First fill all parameters.
8551 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8552 SetProperty(local_scope,
8553 scope_info.parameter_name(i),
8554 Handle<Object>(frame->GetParameter(i)), NONE);
8555 }
8556
8557 // Second fill all stack locals.
8558 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
8559 SetProperty(local_scope,
8560 scope_info.stack_slot_name(i),
8561 Handle<Object>(frame->GetExpression(i)), NONE);
8562 }
8563
8564 // Third fill all context locals.
8565 Handle<Context> frame_context(Context::cast(frame->context()));
8566 Handle<Context> function_context(frame_context->fcontext());
8567 CopyContextLocalsToScopeObject(code, scope_info,
8568 function_context, local_scope);
8569
8570 // Finally copy any properties from the function context extension. This will
8571 // be variables introduced by eval.
8572 if (function_context->closure() == *function) {
8573 if (function_context->has_extension() &&
8574 !function_context->IsGlobalContext()) {
8575 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008576 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008577 for (int i = 0; i < keys->length(); i++) {
8578 // Names of variables introduced by eval are strings.
8579 ASSERT(keys->get(i)->IsString());
8580 Handle<String> key(String::cast(keys->get(i)));
8581 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
8582 }
8583 }
8584 }
8585 return local_scope;
8586}
8587
8588
8589// Create a plain JSObject which materializes the closure content for the
8590// context.
8591static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
8592 ASSERT(context->is_function_context());
8593
8594 Handle<Code> code(context->closure()->code());
8595 ScopeInfo<> scope_info(*code);
8596
8597 // Allocate and initialize a JSObject with all the content of theis function
8598 // closure.
8599 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
8600
8601 // Check whether the arguments shadow object exists.
8602 int arguments_shadow_index =
8603 ScopeInfo<>::ContextSlotIndex(*code,
8604 Heap::arguments_shadow_symbol(),
8605 NULL);
8606 if (arguments_shadow_index >= 0) {
8607 // In this case all the arguments are available in the arguments shadow
8608 // object.
8609 Handle<JSObject> arguments_shadow(
8610 JSObject::cast(context->get(arguments_shadow_index)));
8611 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8612 SetProperty(closure_scope,
8613 scope_info.parameter_name(i),
8614 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
8615 }
8616 }
8617
8618 // Fill all context locals to the context extension.
8619 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
8620
8621 // Finally copy any properties from the function context extension. This will
8622 // be variables introduced by eval.
8623 if (context->has_extension()) {
8624 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008625 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008626 for (int i = 0; i < keys->length(); i++) {
8627 // Names of variables introduced by eval are strings.
8628 ASSERT(keys->get(i)->IsString());
8629 Handle<String> key(String::cast(keys->get(i)));
8630 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
8631 }
8632 }
8633
8634 return closure_scope;
8635}
8636
8637
8638// Iterate over the actual scopes visible from a stack frame. All scopes are
8639// backed by an actual context except the local scope, which is inserted
8640// "artifically" in the context chain.
8641class ScopeIterator {
8642 public:
8643 enum ScopeType {
8644 ScopeTypeGlobal = 0,
8645 ScopeTypeLocal,
8646 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00008647 ScopeTypeClosure,
8648 // Every catch block contains an implicit with block (its parameter is
8649 // a JSContextExtensionObject) that extends current scope with a variable
8650 // holding exception object. Such with blocks are treated as scopes of their
8651 // own type.
8652 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008653 };
8654
8655 explicit ScopeIterator(JavaScriptFrame* frame)
8656 : frame_(frame),
8657 function_(JSFunction::cast(frame->function())),
8658 context_(Context::cast(frame->context())),
8659 local_done_(false),
8660 at_local_(false) {
8661
8662 // Check whether the first scope is actually a local scope.
8663 if (context_->IsGlobalContext()) {
8664 // If there is a stack slot for .result then this local scope has been
8665 // created for evaluating top level code and it is not a real local scope.
8666 // Checking for the existence of .result seems fragile, but the scope info
8667 // saved with the code object does not otherwise have that information.
8668 Handle<Code> code(function_->code());
8669 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
8670 at_local_ = index < 0;
8671 } else if (context_->is_function_context()) {
8672 at_local_ = true;
8673 }
8674 }
8675
8676 // More scopes?
8677 bool Done() { return context_.is_null(); }
8678
8679 // Move to the next scope.
8680 void Next() {
8681 // If at a local scope mark the local scope as passed.
8682 if (at_local_) {
8683 at_local_ = false;
8684 local_done_ = true;
8685
8686 // If the current context is not associated with the local scope the
8687 // current context is the next real scope, so don't move to the next
8688 // context in this case.
8689 if (context_->closure() != *function_) {
8690 return;
8691 }
8692 }
8693
8694 // The global scope is always the last in the chain.
8695 if (context_->IsGlobalContext()) {
8696 context_ = Handle<Context>();
8697 return;
8698 }
8699
8700 // Move to the next context.
8701 if (context_->is_function_context()) {
8702 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
8703 } else {
8704 context_ = Handle<Context>(context_->previous());
8705 }
8706
8707 // If passing the local scope indicate that the current scope is now the
8708 // local scope.
8709 if (!local_done_ &&
8710 (context_->IsGlobalContext() || (context_->is_function_context()))) {
8711 at_local_ = true;
8712 }
8713 }
8714
8715 // Return the type of the current scope.
8716 int Type() {
8717 if (at_local_) {
8718 return ScopeTypeLocal;
8719 }
8720 if (context_->IsGlobalContext()) {
8721 ASSERT(context_->global()->IsGlobalObject());
8722 return ScopeTypeGlobal;
8723 }
8724 if (context_->is_function_context()) {
8725 return ScopeTypeClosure;
8726 }
8727 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00008728 // Current scope is either an explicit with statement or a with statement
8729 // implicitely generated for a catch block.
8730 // If the extension object here is a JSContextExtensionObject then
8731 // current with statement is one frome a catch block otherwise it's a
8732 // regular with statement.
8733 if (context_->extension()->IsJSContextExtensionObject()) {
8734 return ScopeTypeCatch;
8735 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008736 return ScopeTypeWith;
8737 }
8738
8739 // Return the JavaScript object with the content of the current scope.
8740 Handle<JSObject> ScopeObject() {
8741 switch (Type()) {
8742 case ScopeIterator::ScopeTypeGlobal:
8743 return Handle<JSObject>(CurrentContext()->global());
8744 break;
8745 case ScopeIterator::ScopeTypeLocal:
8746 // Materialize the content of the local scope into a JSObject.
8747 return MaterializeLocalScope(frame_);
8748 break;
8749 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00008750 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008751 // Return the with object.
8752 return Handle<JSObject>(CurrentContext()->extension());
8753 break;
8754 case ScopeIterator::ScopeTypeClosure:
8755 // Materialize the content of the closure scope into a JSObject.
8756 return MaterializeClosure(CurrentContext());
8757 break;
8758 }
8759 UNREACHABLE();
8760 return Handle<JSObject>();
8761 }
8762
8763 // Return the context for this scope. For the local context there might not
8764 // be an actual context.
8765 Handle<Context> CurrentContext() {
8766 if (at_local_ && context_->closure() != *function_) {
8767 return Handle<Context>();
8768 }
8769 return context_;
8770 }
8771
8772#ifdef DEBUG
8773 // Debug print of the content of the current scope.
8774 void DebugPrint() {
8775 switch (Type()) {
8776 case ScopeIterator::ScopeTypeGlobal:
8777 PrintF("Global:\n");
8778 CurrentContext()->Print();
8779 break;
8780
8781 case ScopeIterator::ScopeTypeLocal: {
8782 PrintF("Local:\n");
8783 Handle<Code> code(function_->code());
8784 ScopeInfo<> scope_info(*code);
8785 scope_info.Print();
8786 if (!CurrentContext().is_null()) {
8787 CurrentContext()->Print();
8788 if (CurrentContext()->has_extension()) {
8789 Handle<JSObject> extension =
8790 Handle<JSObject>(CurrentContext()->extension());
8791 if (extension->IsJSContextExtensionObject()) {
8792 extension->Print();
8793 }
8794 }
8795 }
8796 break;
8797 }
8798
8799 case ScopeIterator::ScopeTypeWith: {
8800 PrintF("With:\n");
8801 Handle<JSObject> extension =
8802 Handle<JSObject>(CurrentContext()->extension());
8803 extension->Print();
8804 break;
8805 }
8806
ager@chromium.orga1645e22009-09-09 19:27:10 +00008807 case ScopeIterator::ScopeTypeCatch: {
8808 PrintF("Catch:\n");
8809 Handle<JSObject> extension =
8810 Handle<JSObject>(CurrentContext()->extension());
8811 extension->Print();
8812 break;
8813 }
8814
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008815 case ScopeIterator::ScopeTypeClosure: {
8816 PrintF("Closure:\n");
8817 CurrentContext()->Print();
8818 if (CurrentContext()->has_extension()) {
8819 Handle<JSObject> extension =
8820 Handle<JSObject>(CurrentContext()->extension());
8821 if (extension->IsJSContextExtensionObject()) {
8822 extension->Print();
8823 }
8824 }
8825 break;
8826 }
8827
8828 default:
8829 UNREACHABLE();
8830 }
8831 PrintF("\n");
8832 }
8833#endif
8834
8835 private:
8836 JavaScriptFrame* frame_;
8837 Handle<JSFunction> function_;
8838 Handle<Context> context_;
8839 bool local_done_;
8840 bool at_local_;
8841
8842 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
8843};
8844
8845
8846static Object* Runtime_GetScopeCount(Arguments args) {
8847 HandleScope scope;
8848 ASSERT(args.length() == 2);
8849
8850 // Check arguments.
8851 Object* check = Runtime_CheckExecutionState(args);
8852 if (check->IsFailure()) return check;
8853 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8854
8855 // Get the frame where the debugging is performed.
8856 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8857 JavaScriptFrameIterator it(id);
8858 JavaScriptFrame* frame = it.frame();
8859
8860 // Count the visible scopes.
8861 int n = 0;
8862 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8863 n++;
8864 }
8865
8866 return Smi::FromInt(n);
8867}
8868
8869
8870static const int kScopeDetailsTypeIndex = 0;
8871static const int kScopeDetailsObjectIndex = 1;
8872static const int kScopeDetailsSize = 2;
8873
8874// Return an array with scope details
8875// args[0]: number: break id
8876// args[1]: number: frame index
8877// args[2]: number: scope index
8878//
8879// The array returned contains the following information:
8880// 0: Scope type
8881// 1: Scope object
8882static Object* Runtime_GetScopeDetails(Arguments args) {
8883 HandleScope scope;
8884 ASSERT(args.length() == 3);
8885
8886 // Check arguments.
8887 Object* check = Runtime_CheckExecutionState(args);
8888 if (check->IsFailure()) return check;
8889 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8890 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
8891
8892 // Get the frame where the debugging is performed.
8893 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8894 JavaScriptFrameIterator frame_it(id);
8895 JavaScriptFrame* frame = frame_it.frame();
8896
8897 // Find the requested scope.
8898 int n = 0;
8899 ScopeIterator it(frame);
8900 for (; !it.Done() && n < index; it.Next()) {
8901 n++;
8902 }
8903 if (it.Done()) {
8904 return Heap::undefined_value();
8905 }
8906
8907 // Calculate the size of the result.
8908 int details_size = kScopeDetailsSize;
8909 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8910
8911 // Fill in scope details.
8912 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
8913 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
8914
8915 return *Factory::NewJSArrayWithElements(details);
8916}
8917
8918
8919static Object* Runtime_DebugPrintScopes(Arguments args) {
8920 HandleScope scope;
8921 ASSERT(args.length() == 0);
8922
8923#ifdef DEBUG
8924 // Print the scopes for the top frame.
8925 StackFrameLocator locator;
8926 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
8927 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8928 it.DebugPrint();
8929 }
8930#endif
8931 return Heap::undefined_value();
8932}
8933
8934
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008935static Object* Runtime_GetCFrames(Arguments args) {
8936 HandleScope scope;
8937 ASSERT(args.length() == 1);
8938 Object* result = Runtime_CheckExecutionState(args);
8939 if (result->IsFailure()) return result;
8940
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008941#if V8_HOST_ARCH_64_BIT
8942 UNIMPLEMENTED();
8943 return Heap::undefined_value();
8944#else
8945
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008946 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008947 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
8948 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008949 if (frames_count == OS::kStackWalkError) {
8950 return Heap::undefined_value();
8951 }
8952
8953 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
8954 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
8955 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
8956 for (int i = 0; i < frames_count; i++) {
8957 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
8958 frame_value->SetProperty(
8959 *address_str,
8960 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
8961 NONE);
8962
8963 // Get the stack walk text for this frame.
8964 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008965 int frame_text_length = StrLength(frames[i].text);
8966 if (frame_text_length > 0) {
8967 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008968 frame_text = Factory::NewStringFromAscii(str);
8969 }
8970
8971 if (!frame_text.is_null()) {
8972 frame_value->SetProperty(*text_str, *frame_text, NONE);
8973 }
8974
8975 frames_array->set(i, *frame_value);
8976 }
8977 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008978#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008979}
8980
8981
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00008982static Object* Runtime_GetThreadCount(Arguments args) {
8983 HandleScope scope;
8984 ASSERT(args.length() == 1);
8985
8986 // Check arguments.
8987 Object* result = Runtime_CheckExecutionState(args);
8988 if (result->IsFailure()) return result;
8989
8990 // Count all archived V8 threads.
8991 int n = 0;
8992 for (ThreadState* thread = ThreadState::FirstInUse();
8993 thread != NULL;
8994 thread = thread->Next()) {
8995 n++;
8996 }
8997
8998 // Total number of threads is current thread and archived threads.
8999 return Smi::FromInt(n + 1);
9000}
9001
9002
9003static const int kThreadDetailsCurrentThreadIndex = 0;
9004static const int kThreadDetailsThreadIdIndex = 1;
9005static const int kThreadDetailsSize = 2;
9006
9007// Return an array with thread details
9008// args[0]: number: break id
9009// args[1]: number: thread index
9010//
9011// The array returned contains the following information:
9012// 0: Is current thread?
9013// 1: Thread id
9014static Object* Runtime_GetThreadDetails(Arguments args) {
9015 HandleScope scope;
9016 ASSERT(args.length() == 2);
9017
9018 // Check arguments.
9019 Object* check = Runtime_CheckExecutionState(args);
9020 if (check->IsFailure()) return check;
9021 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
9022
9023 // Allocate array for result.
9024 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
9025
9026 // Thread index 0 is current thread.
9027 if (index == 0) {
9028 // Fill the details.
9029 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
9030 details->set(kThreadDetailsThreadIdIndex,
9031 Smi::FromInt(ThreadManager::CurrentId()));
9032 } else {
9033 // Find the thread with the requested index.
9034 int n = 1;
9035 ThreadState* thread = ThreadState::FirstInUse();
9036 while (index != n && thread != NULL) {
9037 thread = thread->Next();
9038 n++;
9039 }
9040 if (thread == NULL) {
9041 return Heap::undefined_value();
9042 }
9043
9044 // Fill the details.
9045 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
9046 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
9047 }
9048
9049 // Convert to JS array and return.
9050 return *Factory::NewJSArrayWithElements(details);
9051}
9052
9053
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009054static Object* Runtime_GetBreakLocations(Arguments args) {
9055 HandleScope scope;
9056 ASSERT(args.length() == 1);
9057
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009058 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9059 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009060 // Find the number of break points
9061 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
9062 if (break_locations->IsUndefined()) return Heap::undefined_value();
9063 // Return array as JS array
9064 return *Factory::NewJSArrayWithElements(
9065 Handle<FixedArray>::cast(break_locations));
9066}
9067
9068
9069// Set a break point in a function
9070// args[0]: function
9071// args[1]: number: break source position (within the function source)
9072// args[2]: number: break point object
9073static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
9074 HandleScope scope;
9075 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009076 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9077 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009078 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9079 RUNTIME_ASSERT(source_position >= 0);
9080 Handle<Object> break_point_object_arg = args.at<Object>(2);
9081
9082 // Set break point.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009083 Debug::SetBreakPoint(shared, break_point_object_arg, &source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009084
lrn@chromium.org32d961d2010-06-30 09:09:34 +00009085 return Smi::FromInt(source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009086}
9087
9088
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009089Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
9090 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009091 // Iterate the heap looking for SharedFunctionInfo generated from the
9092 // script. The inner most SharedFunctionInfo containing the source position
9093 // for the requested break point is found.
9094 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
9095 // which is found is not compiled it is compiled and the heap is iterated
9096 // again as the compilation might create inner functions from the newly
9097 // compiled function and the actual requested break point might be in one of
9098 // these functions.
9099 bool done = false;
9100 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00009101 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009102 Handle<SharedFunctionInfo> target;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009103 while (!done) {
9104 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009105 for (HeapObject* obj = iterator.next();
9106 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009107 if (obj->IsSharedFunctionInfo()) {
9108 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
9109 if (shared->script() == *script) {
9110 // If the SharedFunctionInfo found has the requested script data and
9111 // contains the source position it is a candidate.
9112 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00009113 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009114 start_position = shared->start_position();
9115 }
9116 if (start_position <= position &&
9117 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00009118 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009119 // candidate this is the new candidate.
9120 if (target.is_null()) {
9121 target_start_position = start_position;
9122 target = shared;
9123 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00009124 if (target_start_position == start_position &&
9125 shared->end_position() == target->end_position()) {
9126 // If a top-level function contain only one function
9127 // declartion the source for the top-level and the function is
9128 // the same. In that case prefer the non top-level function.
9129 if (!shared->is_toplevel()) {
9130 target_start_position = start_position;
9131 target = shared;
9132 }
9133 } else if (target_start_position <= start_position &&
9134 shared->end_position() <= target->end_position()) {
9135 // This containment check includes equality as a function inside
9136 // a top-level function can share either start or end position
9137 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009138 target_start_position = start_position;
9139 target = shared;
9140 }
9141 }
9142 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009143 }
9144 }
9145 }
9146
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009147 if (target.is_null()) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009148 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009149 }
9150
9151 // If the candidate found is compiled we are done. NOTE: when lazy
9152 // compilation of inner functions is introduced some additional checking
9153 // needs to be done here to compile inner functions.
9154 done = target->is_compiled();
9155 if (!done) {
9156 // If the candidate is not compiled compile it to reveal any inner
9157 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009158 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009159 }
9160 }
9161
9162 return *target;
9163}
9164
9165
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009166// Changes the state of a break point in a script and returns source position
9167// where break point was set. NOTE: Regarding performance see the NOTE for
9168// GetScriptFromScriptData.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009169// args[0]: script to set break point in
9170// args[1]: number: break source position (within the script source)
9171// args[2]: number: break point object
9172static Object* Runtime_SetScriptBreakPoint(Arguments args) {
9173 HandleScope scope;
9174 ASSERT(args.length() == 3);
9175 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
9176 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9177 RUNTIME_ASSERT(source_position >= 0);
9178 Handle<Object> break_point_object_arg = args.at<Object>(2);
9179
9180 // Get the script from the script wrapper.
9181 RUNTIME_ASSERT(wrapper->value()->IsScript());
9182 Handle<Script> script(Script::cast(wrapper->value()));
9183
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009184 Object* result = Runtime::FindSharedFunctionInfoInScript(
9185 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009186 if (!result->IsUndefined()) {
9187 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
9188 // Find position within function. The script position might be before the
9189 // source position of the first function.
9190 int position;
9191 if (shared->start_position() > source_position) {
9192 position = 0;
9193 } else {
9194 position = source_position - shared->start_position();
9195 }
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009196 Debug::SetBreakPoint(shared, break_point_object_arg, &position);
9197 position += shared->start_position();
9198 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009199 }
9200 return Heap::undefined_value();
9201}
9202
9203
9204// Clear a break point
9205// args[0]: number: break point object
9206static Object* Runtime_ClearBreakPoint(Arguments args) {
9207 HandleScope scope;
9208 ASSERT(args.length() == 1);
9209 Handle<Object> break_point_object_arg = args.at<Object>(0);
9210
9211 // Clear break point.
9212 Debug::ClearBreakPoint(break_point_object_arg);
9213
9214 return Heap::undefined_value();
9215}
9216
9217
9218// Change the state of break on exceptions
9219// args[0]: boolean indicating uncaught exceptions
9220// args[1]: boolean indicating on/off
9221static Object* Runtime_ChangeBreakOnException(Arguments args) {
9222 HandleScope scope;
9223 ASSERT(args.length() == 2);
9224 ASSERT(args[0]->IsNumber());
9225 ASSERT(args[1]->IsBoolean());
9226
9227 // Update break point state
9228 ExceptionBreakType type =
9229 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
9230 bool enable = args[1]->ToBoolean()->IsTrue();
9231 Debug::ChangeBreakOnException(type, enable);
9232 return Heap::undefined_value();
9233}
9234
9235
9236// Prepare for stepping
9237// args[0]: break id for checking execution state
9238// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00009239// args[2]: number of times to perform the step, for step out it is the number
9240// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009241static Object* Runtime_PrepareStep(Arguments args) {
9242 HandleScope scope;
9243 ASSERT(args.length() == 3);
9244 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009245 Object* check = Runtime_CheckExecutionState(args);
9246 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009247 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
9248 return Top::Throw(Heap::illegal_argument_symbol());
9249 }
9250
9251 // Get the step action and check validity.
9252 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
9253 if (step_action != StepIn &&
9254 step_action != StepNext &&
9255 step_action != StepOut &&
9256 step_action != StepInMin &&
9257 step_action != StepMin) {
9258 return Top::Throw(Heap::illegal_argument_symbol());
9259 }
9260
9261 // Get the number of steps.
9262 int step_count = NumberToInt32(args[2]);
9263 if (step_count < 1) {
9264 return Top::Throw(Heap::illegal_argument_symbol());
9265 }
9266
ager@chromium.orga1645e22009-09-09 19:27:10 +00009267 // Clear all current stepping setup.
9268 Debug::ClearStepping();
9269
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009270 // Prepare step.
9271 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
9272 return Heap::undefined_value();
9273}
9274
9275
9276// Clear all stepping set by PrepareStep.
9277static Object* Runtime_ClearStepping(Arguments args) {
9278 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009279 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009280 Debug::ClearStepping();
9281 return Heap::undefined_value();
9282}
9283
9284
9285// Creates a copy of the with context chain. The copy of the context chain is
9286// is linked to the function context supplied.
9287static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
9288 Handle<Context> function_context) {
9289 // At the bottom of the chain. Return the function context to link to.
9290 if (context_chain->is_function_context()) {
9291 return function_context;
9292 }
9293
9294 // Recursively copy the with contexts.
9295 Handle<Context> previous(context_chain->previous());
9296 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
9297 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009298 CopyWithContextChain(function_context, previous),
9299 extension,
9300 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009301}
9302
9303
9304// Helper function to find or create the arguments object for
9305// Runtime_DebugEvaluate.
9306static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
9307 Handle<JSFunction> function,
9308 Handle<Code> code,
9309 const ScopeInfo<>* sinfo,
9310 Handle<Context> function_context) {
9311 // Try to find the value of 'arguments' to pass as parameter. If it is not
9312 // found (that is the debugged function does not reference 'arguments' and
9313 // does not support eval) then create an 'arguments' object.
9314 int index;
9315 if (sinfo->number_of_stack_slots() > 0) {
9316 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
9317 if (index != -1) {
9318 return Handle<Object>(frame->GetExpression(index));
9319 }
9320 }
9321
9322 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
9323 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
9324 NULL);
9325 if (index != -1) {
9326 return Handle<Object>(function_context->get(index));
9327 }
9328 }
9329
9330 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009331 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
9332 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009333
9334 AssertNoAllocation no_gc;
9335 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009336 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009337 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009338 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009339 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009340 return arguments;
9341}
9342
9343
9344// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00009345// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009346// extension part has all the parameters and locals of the function on the
9347// stack frame. A function which calls eval with the code to evaluate is then
9348// compiled in this context and called in this context. As this context
9349// replaces the context of the function on the stack frame a new (empty)
9350// function is created as well to be used as the closure for the context.
9351// This function and the context acts as replacements for the function on the
9352// stack frame presenting the same view of the values of parameters and
9353// local variables as if the piece of JavaScript was evaluated at the point
9354// where the function on the stack frame is currently stopped.
9355static Object* Runtime_DebugEvaluate(Arguments args) {
9356 HandleScope scope;
9357
9358 // Check the execution state and decode arguments frame and source to be
9359 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009360 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009361 Object* check_result = Runtime_CheckExecutionState(args);
9362 if (check_result->IsFailure()) return check_result;
9363 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9364 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009365 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
9366
9367 // Handle the processing of break.
9368 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009369
9370 // Get the frame where the debugging is performed.
9371 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9372 JavaScriptFrameIterator it(id);
9373 JavaScriptFrame* frame = it.frame();
9374 Handle<JSFunction> function(JSFunction::cast(frame->function()));
9375 Handle<Code> code(function->code());
9376 ScopeInfo<> sinfo(*code);
9377
9378 // Traverse the saved contexts chain to find the active context for the
9379 // selected frame.
9380 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009381 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009382 save = save->prev();
9383 }
9384 ASSERT(save != NULL);
9385 SaveContext savex;
9386 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009387
9388 // Create the (empty) function replacing the function on the stack frame for
9389 // the purpose of evaluating in the context created below. It is important
9390 // that this function does not describe any parameters and local variables
9391 // in the context. If it does then this will cause problems with the lookup
9392 // in Context::Lookup, where context slots for parameters and local variables
9393 // are looked at before the extension object.
9394 Handle<JSFunction> go_between =
9395 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
9396 go_between->set_context(function->context());
9397#ifdef DEBUG
9398 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
9399 ASSERT(go_between_sinfo.number_of_parameters() == 0);
9400 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
9401#endif
9402
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009403 // Materialize the content of the local scope into a JSObject.
9404 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009405
9406 // Allocate a new context for the debug evaluation and set the extension
9407 // object build.
9408 Handle<Context> context =
9409 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009410 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009411 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009412 Handle<Context> frame_context(Context::cast(frame->context()));
9413 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009414 context = CopyWithContextChain(frame_context, context);
9415
9416 // Wrap the evaluation statement in a new function compiled in the newly
9417 // created context. The function has one parameter which has to be called
9418 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00009419 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009420 // function(arguments,__source__) {return eval(__source__);}
9421 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00009422 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009423 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009424 Handle<String> function_source =
9425 Factory::NewStringFromAscii(Vector<const char>(source_str,
9426 source_str_length));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009427 Handle<SharedFunctionInfo> shared =
ager@chromium.org381abbb2009-02-25 13:23:22 +00009428 Compiler::CompileEval(function_source,
9429 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00009430 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00009431 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009432 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009433 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009434 Factory::NewFunctionFromSharedFunctionInfo(shared, context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009435
9436 // Invoke the result of the compilation to get the evaluation function.
9437 bool has_pending_exception;
9438 Handle<Object> receiver(frame->receiver());
9439 Handle<Object> evaluation_function =
9440 Execution::Call(compiled_function, receiver, 0, NULL,
9441 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009442 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009443
9444 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
9445 function_context);
9446
9447 // Invoke the evaluation function and return the result.
9448 const int argc = 2;
9449 Object** argv[argc] = { arguments.location(),
9450 Handle<Object>::cast(source).location() };
9451 Handle<Object> result =
9452 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
9453 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009454 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009455
9456 // Skip the global proxy as it has no properties and always delegates to the
9457 // real global object.
9458 if (result->IsJSGlobalProxy()) {
9459 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
9460 }
9461
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009462 return *result;
9463}
9464
9465
9466static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
9467 HandleScope scope;
9468
9469 // Check the execution state and decode arguments frame and source to be
9470 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009471 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009472 Object* check_result = Runtime_CheckExecutionState(args);
9473 if (check_result->IsFailure()) return check_result;
9474 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009475 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
9476
9477 // Handle the processing of break.
9478 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009479
9480 // Enter the top context from before the debugger was invoked.
9481 SaveContext save;
9482 SaveContext* top = &save;
9483 while (top != NULL && *top->context() == *Debug::debug_context()) {
9484 top = top->prev();
9485 }
9486 if (top != NULL) {
9487 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009488 }
9489
9490 // Get the global context now set to the top context from before the
9491 // debugger was invoked.
9492 Handle<Context> context = Top::global_context();
9493
9494 // Compile the source to be evaluated.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009495 Handle<SharedFunctionInfo> shared =
9496 Compiler::CompileEval(source,
9497 context,
9498 true,
9499 Compiler::DONT_VALIDATE_JSON);
9500 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009501 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009502 Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
9503 context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009504
9505 // Invoke the result of the compilation to get the evaluation function.
9506 bool has_pending_exception;
9507 Handle<Object> receiver = Top::global();
9508 Handle<Object> result =
9509 Execution::Call(compiled_function, receiver, 0, NULL,
9510 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009511 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009512 return *result;
9513}
9514
9515
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009516static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
9517 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009518 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009519
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009520 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009521 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009522
9523 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009524 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00009525 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
9526 // Get the script wrapper in a local handle before calling GetScriptWrapper,
9527 // because using
9528 // instances->set(i, *GetScriptWrapper(script))
9529 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
9530 // already have deferenced the instances handle.
9531 Handle<JSValue> wrapper = GetScriptWrapper(script);
9532 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009533 }
9534
9535 // Return result as a JS array.
9536 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
9537 Handle<JSArray>::cast(result)->SetContent(*instances);
9538 return *result;
9539}
9540
9541
9542// Helper function used by Runtime_DebugReferencedBy below.
9543static int DebugReferencedBy(JSObject* target,
9544 Object* instance_filter, int max_references,
9545 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009546 JSFunction* arguments_function) {
9547 NoHandleAllocation ha;
9548 AssertNoAllocation no_alloc;
9549
9550 // Iterate the heap.
9551 int count = 0;
9552 JSObject* last = NULL;
9553 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009554 HeapObject* heap_obj = NULL;
9555 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009556 (max_references == 0 || count < max_references)) {
9557 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009558 if (heap_obj->IsJSObject()) {
9559 // Skip context extension objects and argument arrays as these are
9560 // checked in the context of functions using them.
9561 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00009562 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009563 obj->map()->constructor() == arguments_function) {
9564 continue;
9565 }
9566
9567 // Check if the JS object has a reference to the object looked for.
9568 if (obj->ReferencesObject(target)) {
9569 // Check instance filter if supplied. This is normally used to avoid
9570 // references from mirror objects (see Runtime_IsInPrototypeChain).
9571 if (!instance_filter->IsUndefined()) {
9572 Object* V = obj;
9573 while (true) {
9574 Object* prototype = V->GetPrototype();
9575 if (prototype->IsNull()) {
9576 break;
9577 }
9578 if (instance_filter == prototype) {
9579 obj = NULL; // Don't add this object.
9580 break;
9581 }
9582 V = prototype;
9583 }
9584 }
9585
9586 if (obj != NULL) {
9587 // Valid reference found add to instance array if supplied an update
9588 // count.
9589 if (instances != NULL && count < instances_size) {
9590 instances->set(count, obj);
9591 }
9592 last = obj;
9593 count++;
9594 }
9595 }
9596 }
9597 }
9598
9599 // Check for circular reference only. This can happen when the object is only
9600 // referenced from mirrors and has a circular reference in which case the
9601 // object is not really alive and would have been garbage collected if not
9602 // referenced from the mirror.
9603 if (count == 1 && last == target) {
9604 count = 0;
9605 }
9606
9607 // Return the number of referencing objects found.
9608 return count;
9609}
9610
9611
9612// Scan the heap for objects with direct references to an object
9613// args[0]: the object to find references to
9614// args[1]: constructor function for instances to exclude (Mirror)
9615// args[2]: the the maximum number of objects to return
9616static Object* Runtime_DebugReferencedBy(Arguments args) {
9617 ASSERT(args.length() == 3);
9618
9619 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009620 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009621
9622 // Check parameters.
9623 CONVERT_CHECKED(JSObject, target, args[0]);
9624 Object* instance_filter = args[1];
9625 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
9626 instance_filter->IsJSObject());
9627 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
9628 RUNTIME_ASSERT(max_references >= 0);
9629
9630 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009631 JSObject* arguments_boilerplate =
9632 Top::context()->global_context()->arguments_boilerplate();
9633 JSFunction* arguments_function =
9634 JSFunction::cast(arguments_boilerplate->map()->constructor());
9635
9636 // Get the number of referencing objects.
9637 int count;
9638 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009639 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009640
9641 // Allocate an array to hold the result.
9642 Object* object = Heap::AllocateFixedArray(count);
9643 if (object->IsFailure()) return object;
9644 FixedArray* instances = FixedArray::cast(object);
9645
9646 // Fill the referencing objects.
9647 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009648 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009649
9650 // Return result as JS array.
9651 Object* result =
9652 Heap::AllocateJSObject(
9653 Top::context()->global_context()->array_function());
9654 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9655 return result;
9656}
9657
9658
9659// Helper function used by Runtime_DebugConstructedBy below.
9660static int DebugConstructedBy(JSFunction* constructor, int max_references,
9661 FixedArray* instances, int instances_size) {
9662 AssertNoAllocation no_alloc;
9663
9664 // Iterate the heap.
9665 int count = 0;
9666 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009667 HeapObject* heap_obj = NULL;
9668 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009669 (max_references == 0 || count < max_references)) {
9670 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009671 if (heap_obj->IsJSObject()) {
9672 JSObject* obj = JSObject::cast(heap_obj);
9673 if (obj->map()->constructor() == constructor) {
9674 // Valid reference found add to instance array if supplied an update
9675 // count.
9676 if (instances != NULL && count < instances_size) {
9677 instances->set(count, obj);
9678 }
9679 count++;
9680 }
9681 }
9682 }
9683
9684 // Return the number of referencing objects found.
9685 return count;
9686}
9687
9688
9689// Scan the heap for objects constructed by a specific function.
9690// args[0]: the constructor to find instances of
9691// args[1]: the the maximum number of objects to return
9692static Object* Runtime_DebugConstructedBy(Arguments args) {
9693 ASSERT(args.length() == 2);
9694
9695 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009696 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009697
9698 // Check parameters.
9699 CONVERT_CHECKED(JSFunction, constructor, args[0]);
9700 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
9701 RUNTIME_ASSERT(max_references >= 0);
9702
9703 // Get the number of referencing objects.
9704 int count;
9705 count = DebugConstructedBy(constructor, max_references, NULL, 0);
9706
9707 // Allocate an array to hold the result.
9708 Object* object = Heap::AllocateFixedArray(count);
9709 if (object->IsFailure()) return object;
9710 FixedArray* instances = FixedArray::cast(object);
9711
9712 // Fill the referencing objects.
9713 count = DebugConstructedBy(constructor, max_references, instances, count);
9714
9715 // Return result as JS array.
9716 Object* result =
9717 Heap::AllocateJSObject(
9718 Top::context()->global_context()->array_function());
9719 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9720 return result;
9721}
9722
9723
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009724// Find the effective prototype object as returned by __proto__.
9725// args[0]: the object to find the prototype for.
9726static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009727 ASSERT(args.length() == 1);
9728
9729 CONVERT_CHECKED(JSObject, obj, args[0]);
9730
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009731 // Use the __proto__ accessor.
9732 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009733}
9734
9735
9736static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009737 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009738 CPU::DebugBreak();
9739 return Heap::undefined_value();
9740}
9741
9742
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009743static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009744#ifdef DEBUG
9745 HandleScope scope;
9746 ASSERT(args.length() == 1);
9747 // Get the function and make sure it is compiled.
9748 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009749 Handle<SharedFunctionInfo> shared(func->shared());
9750 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009751 return Failure::Exception();
9752 }
9753 func->code()->PrintLn();
9754#endif // DEBUG
9755 return Heap::undefined_value();
9756}
ager@chromium.org9085a012009-05-11 19:22:57 +00009757
9758
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009759static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
9760#ifdef DEBUG
9761 HandleScope scope;
9762 ASSERT(args.length() == 1);
9763 // Get the function and make sure it is compiled.
9764 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009765 Handle<SharedFunctionInfo> shared(func->shared());
9766 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009767 return Failure::Exception();
9768 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009769 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009770#endif // DEBUG
9771 return Heap::undefined_value();
9772}
9773
9774
ager@chromium.org9085a012009-05-11 19:22:57 +00009775static Object* Runtime_FunctionGetInferredName(Arguments args) {
9776 NoHandleAllocation ha;
9777 ASSERT(args.length() == 1);
9778
9779 CONVERT_CHECKED(JSFunction, f, args[0]);
9780 return f->shared()->inferred_name();
9781}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009782
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009783
9784static int FindSharedFunctionInfosForScript(Script* script,
9785 FixedArray* buffer) {
9786 AssertNoAllocation no_allocations;
9787
9788 int counter = 0;
9789 int buffer_size = buffer->length();
9790 HeapIterator iterator;
9791 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
9792 ASSERT(obj != NULL);
9793 if (!obj->IsSharedFunctionInfo()) {
9794 continue;
9795 }
9796 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
9797 if (shared->script() != script) {
9798 continue;
9799 }
9800 if (counter < buffer_size) {
9801 buffer->set(counter, shared);
9802 }
9803 counter++;
9804 }
9805 return counter;
9806}
9807
9808// For a script finds all SharedFunctionInfo's in the heap that points
9809// to this script. Returns JSArray of SharedFunctionInfo wrapped
9810// in OpaqueReferences.
9811static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
9812 Arguments args) {
9813 ASSERT(args.length() == 1);
9814 HandleScope scope;
9815 CONVERT_CHECKED(JSValue, script_value, args[0]);
9816
9817 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
9818
9819 const int kBufferSize = 32;
9820
9821 Handle<FixedArray> array;
9822 array = Factory::NewFixedArray(kBufferSize);
9823 int number = FindSharedFunctionInfosForScript(*script, *array);
9824 if (number > kBufferSize) {
9825 array = Factory::NewFixedArray(number);
9826 FindSharedFunctionInfosForScript(*script, *array);
9827 }
9828
9829 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
9830 result->set_length(Smi::FromInt(number));
9831
9832 LiveEdit::WrapSharedFunctionInfos(result);
9833
9834 return *result;
9835}
9836
9837// For a script calculates compilation information about all its functions.
9838// The script source is explicitly specified by the second argument.
9839// The source of the actual script is not used, however it is important that
9840// all generated code keeps references to this particular instance of script.
9841// Returns a JSArray of compilation infos. The array is ordered so that
9842// each function with all its descendant is always stored in a continues range
9843// with the function itself going first. The root function is a script function.
9844static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
9845 ASSERT(args.length() == 2);
9846 HandleScope scope;
9847 CONVERT_CHECKED(JSValue, script, args[0]);
9848 CONVERT_ARG_CHECKED(String, source, 1);
9849 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
9850
9851 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
9852
9853 if (Top::has_pending_exception()) {
9854 return Failure::Exception();
9855 }
9856
9857 return result;
9858}
9859
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009860// Changes the source of the script to a new_source.
9861// If old_script_name is provided (i.e. is a String), also creates a copy of
9862// the script with its original source and sends notification to debugger.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009863static Object* Runtime_LiveEditReplaceScript(Arguments args) {
9864 ASSERT(args.length() == 3);
9865 HandleScope scope;
9866 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
9867 CONVERT_ARG_CHECKED(String, new_source, 1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009868 Handle<Object> old_script_name(args[2]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009869
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009870 CONVERT_CHECKED(Script, original_script_pointer,
9871 original_script_value->value());
9872 Handle<Script> original_script(original_script_pointer);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009873
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009874 Object* old_script = LiveEdit::ChangeScriptSource(original_script,
9875 new_source,
9876 old_script_name);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009877
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009878 if (old_script->IsScript()) {
9879 Handle<Script> script_handle(Script::cast(old_script));
9880 return *(GetScriptWrapper(script_handle));
9881 } else {
9882 return Heap::null_value();
9883 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009884}
9885
9886// Replaces code of SharedFunctionInfo with a new one.
9887static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
9888 ASSERT(args.length() == 2);
9889 HandleScope scope;
9890 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
9891 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
9892
ager@chromium.orgac091b72010-05-05 07:34:42 +00009893 return LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009894}
9895
9896// Connects SharedFunctionInfo to another script.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009897static Object* Runtime_LiveEditFunctionSetScript(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009898 ASSERT(args.length() == 2);
9899 HandleScope scope;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009900 Handle<Object> function_object(args[0]);
9901 Handle<Object> script_object(args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009902
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009903 if (function_object->IsJSValue()) {
9904 Handle<JSValue> function_wrapper = Handle<JSValue>::cast(function_object);
9905 if (script_object->IsJSValue()) {
9906 CONVERT_CHECKED(Script, script, JSValue::cast(*script_object)->value());
9907 script_object = Handle<Object>(script);
9908 }
9909
9910 LiveEdit::SetFunctionScript(function_wrapper, script_object);
9911 } else {
9912 // Just ignore this. We may not have a SharedFunctionInfo for some functions
9913 // and we check it in this function.
9914 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009915
9916 return Heap::undefined_value();
9917}
9918
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009919
9920// In a code of a parent function replaces original function as embedded object
9921// with a substitution one.
9922static Object* Runtime_LiveEditReplaceRefToNestedFunction(Arguments args) {
9923 ASSERT(args.length() == 3);
9924 HandleScope scope;
9925
9926 CONVERT_ARG_CHECKED(JSValue, parent_wrapper, 0);
9927 CONVERT_ARG_CHECKED(JSValue, orig_wrapper, 1);
9928 CONVERT_ARG_CHECKED(JSValue, subst_wrapper, 2);
9929
9930 LiveEdit::ReplaceRefToNestedFunction(parent_wrapper, orig_wrapper,
9931 subst_wrapper);
9932
9933 return Heap::undefined_value();
9934}
9935
9936
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009937// Updates positions of a shared function info (first parameter) according
9938// to script source change. Text change is described in second parameter as
9939// array of groups of 3 numbers:
9940// (change_begin, change_end, change_end_new_position).
9941// Each group describes a change in text; groups are sorted by change_begin.
9942static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
9943 ASSERT(args.length() == 2);
9944 HandleScope scope;
9945 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
9946 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
9947
ager@chromium.orgac091b72010-05-05 07:34:42 +00009948 return LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009949}
9950
9951
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009952// For array of SharedFunctionInfo's (each wrapped in JSValue)
9953// checks that none of them have activations on stacks (of any thread).
9954// Returns array of the same length with corresponding results of
9955// LiveEdit::FunctionPatchabilityStatus type.
ager@chromium.org357bf652010-04-12 11:30:10 +00009956static Object* Runtime_LiveEditCheckAndDropActivations(Arguments args) {
9957 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009958 HandleScope scope;
9959 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +00009960 CONVERT_BOOLEAN_CHECKED(do_drop, args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009961
ager@chromium.org357bf652010-04-12 11:30:10 +00009962 return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009963}
9964
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009965// Compares 2 strings line-by-line and returns diff in form of JSArray of
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00009966// triplets (pos1, pos1_end, pos2_end) describing list of diff chunks.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009967static Object* Runtime_LiveEditCompareStringsLinewise(Arguments args) {
9968 ASSERT(args.length() == 2);
9969 HandleScope scope;
9970 CONVERT_ARG_CHECKED(String, s1, 0);
9971 CONVERT_ARG_CHECKED(String, s2, 1);
9972
9973 return *LiveEdit::CompareStringsLinewise(s1, s2);
9974}
9975
9976
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009977
fschneider@chromium.org086aac62010-03-17 13:18:24 +00009978// A testing entry. Returns statement position which is the closest to
9979// source_position.
9980static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
9981 ASSERT(args.length() == 2);
9982 HandleScope scope;
9983 CONVERT_ARG_CHECKED(JSFunction, function, 0);
9984 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9985
9986 Handle<Code> code(function->code());
9987
9988 RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
9989 int closest_pc = 0;
9990 int distance = kMaxInt;
9991 while (!it.done()) {
9992 int statement_position = static_cast<int>(it.rinfo()->data());
9993 // Check if this break point is closer that what was previously found.
9994 if (source_position <= statement_position &&
9995 statement_position - source_position < distance) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00009996 closest_pc =
9997 static_cast<int>(it.rinfo()->pc() - code->instruction_start());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00009998 distance = statement_position - source_position;
9999 // Check whether we can't get any closer.
10000 if (distance == 0) break;
10001 }
10002 it.next();
10003 }
10004
10005 return Smi::FromInt(closest_pc);
10006}
10007
10008
ager@chromium.org357bf652010-04-12 11:30:10 +000010009// Calls specified function with or without entering the debugger.
10010// This is used in unit tests to run code as if debugger is entered or simply
10011// to have a stack with C++ frame in the middle.
10012static Object* Runtime_ExecuteInDebugContext(Arguments args) {
10013 ASSERT(args.length() == 2);
10014 HandleScope scope;
10015 CONVERT_ARG_CHECKED(JSFunction, function, 0);
10016 CONVERT_BOOLEAN_CHECKED(without_debugger, args[1]);
10017
10018 Handle<Object> result;
10019 bool pending_exception;
10020 {
10021 if (without_debugger) {
10022 result = Execution::Call(function, Top::global(), 0, NULL,
10023 &pending_exception);
10024 } else {
10025 EnterDebugger enter_debugger;
10026 result = Execution::Call(function, Top::global(), 0, NULL,
10027 &pending_exception);
10028 }
10029 }
10030 if (!pending_exception) {
10031 return *result;
10032 } else {
10033 return Failure::Exception();
10034 }
10035}
10036
10037
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010038#endif // ENABLE_DEBUGGER_SUPPORT
10039
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010040#ifdef ENABLE_LOGGING_AND_PROFILING
10041
10042static Object* Runtime_ProfilerResume(Arguments args) {
10043 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010044 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010045
10046 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010047 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10048 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010049 return Heap::undefined_value();
10050}
10051
10052
10053static Object* Runtime_ProfilerPause(Arguments args) {
10054 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010055 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010056
10057 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010058 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10059 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010060 return Heap::undefined_value();
10061}
10062
10063#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010064
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010065// Finds the script object from the script data. NOTE: This operation uses
10066// heap traversal to find the function generated for the source position
10067// for the requested break point. For lazily compiled functions several heap
10068// traversals might be required rendering this operation as a rather slow
10069// operation. However for setting break points which is normally done through
10070// some kind of user interaction the performance is not crucial.
10071static Handle<Object> Runtime_GetScriptFromScriptName(
10072 Handle<String> script_name) {
10073 // Scan the heap for Script objects to find the script with the requested
10074 // script data.
10075 Handle<Script> script;
10076 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010077 HeapObject* obj = NULL;
10078 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010079 // If a script is found check if it has the script data requested.
10080 if (obj->IsScript()) {
10081 if (Script::cast(obj)->name()->IsString()) {
10082 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
10083 script = Handle<Script>(Script::cast(obj));
10084 }
10085 }
10086 }
10087 }
10088
10089 // If no script with the requested script data is found return undefined.
10090 if (script.is_null()) return Factory::undefined_value();
10091
10092 // Return the script found.
10093 return GetScriptWrapper(script);
10094}
10095
10096
10097// Get the script object from script data. NOTE: Regarding performance
10098// see the NOTE for GetScriptFromScriptData.
10099// args[0]: script data for the script to find the source for
10100static Object* Runtime_GetScript(Arguments args) {
10101 HandleScope scope;
10102
10103 ASSERT(args.length() == 1);
10104
10105 CONVERT_CHECKED(String, script_name, args[0]);
10106
10107 // Find the requested script.
10108 Handle<Object> result =
10109 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
10110 return *result;
10111}
10112
10113
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010114// Determines whether the given stack frame should be displayed in
10115// a stack trace. The caller is the error constructor that asked
10116// for the stack trace to be collected. The first time a construct
10117// call to this function is encountered it is skipped. The seen_caller
10118// in/out parameter is used to remember if the caller has been seen
10119// yet.
10120static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
10121 bool* seen_caller) {
10122 // Only display JS frames.
10123 if (!raw_frame->is_java_script())
10124 return false;
10125 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
10126 Object* raw_fun = frame->function();
10127 // Not sure when this can happen but skip it just in case.
10128 if (!raw_fun->IsJSFunction())
10129 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010130 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010131 *seen_caller = true;
10132 return false;
10133 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010134 // Skip all frames until we've seen the caller. Also, skip the most
10135 // obvious builtin calls. Some builtin calls (such as Number.ADD
10136 // which is invoked using 'call') are very difficult to recognize
10137 // so we're leaving them in for now.
10138 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010139}
10140
10141
10142// Collect the raw data for a stack trace. Returns an array of three
10143// element segments each containing a receiver, function and native
10144// code offset.
10145static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010146 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010147 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010148 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
10149
10150 HandleScope scope;
10151
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000010152 limit = Max(limit, 0); // Ensure that limit is not negative.
10153 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010154 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010155
10156 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010157 // If the caller parameter is a function we skip frames until we're
10158 // under it before starting to collect.
10159 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010160 int cursor = 0;
10161 int frames_seen = 0;
10162 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010163 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010164 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010165 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010166 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010167 Object* recv = frame->receiver();
10168 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010169 Address pc = frame->pc();
10170 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010171 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010172 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010173 if (cursor + 2 < elements->length()) {
10174 elements->set(cursor++, recv);
10175 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010176 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010177 } else {
10178 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010179 Handle<Object> recv_handle(recv);
10180 Handle<Object> fun_handle(fun);
10181 SetElement(result, cursor++, recv_handle);
10182 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010183 SetElement(result, cursor++, Handle<Smi>(offset));
10184 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010185 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010186 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010187 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010188
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010189 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010190 return *result;
10191}
10192
10193
ager@chromium.org3811b432009-10-28 14:53:37 +000010194// Returns V8 version as a string.
10195static Object* Runtime_GetV8Version(Arguments args) {
10196 ASSERT_EQ(args.length(), 0);
10197
10198 NoHandleAllocation ha;
10199
10200 const char* version_string = v8::V8::GetVersion();
10201
10202 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
10203}
10204
10205
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010206static Object* Runtime_Abort(Arguments args) {
10207 ASSERT(args.length() == 2);
10208 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
10209 Smi::cast(args[1])->value());
10210 Top::PrintStack();
10211 OS::Abort();
10212 UNREACHABLE();
10213 return NULL;
10214}
10215
10216
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010217static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
10218 ASSERT(args.length() == 0);
10219 HandleScope::DeleteExtensions();
10220 return Heap::undefined_value();
10221}
10222
10223
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010224static Object* CacheMiss(FixedArray* cache_obj, int index, Object* key_obj) {
10225 ASSERT(index % 2 == 0); // index of the key
10226 ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
10227 ASSERT(index < cache_obj->length());
10228
10229 HandleScope scope;
10230
10231 Handle<FixedArray> cache(cache_obj);
10232 Handle<Object> key(key_obj);
10233 Handle<JSFunction> factory(JSFunction::cast(
10234 cache->get(JSFunctionResultCache::kFactoryIndex)));
10235 // TODO(antonm): consider passing a receiver when constructing a cache.
10236 Handle<Object> receiver(Top::global_context()->global());
10237
10238 Handle<Object> value;
10239 {
10240 // This handle is nor shared, nor used later, so it's safe.
10241 Object** argv[] = { key.location() };
10242 bool pending_exception = false;
10243 value = Execution::Call(factory,
10244 receiver,
10245 1,
10246 argv,
10247 &pending_exception);
10248 if (pending_exception) return Failure::Exception();
10249 }
10250
10251 cache->set(index, *key);
10252 cache->set(index + 1, *value);
10253 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(index));
10254
10255 return *value;
10256}
10257
10258
10259static Object* Runtime_GetFromCache(Arguments args) {
10260 // This is only called from codegen, so checks might be more lax.
10261 CONVERT_CHECKED(FixedArray, cache, args[0]);
10262 Object* key = args[1];
10263
10264 const int finger_index =
10265 Smi::cast(cache->get(JSFunctionResultCache::kFingerIndex))->value();
10266
10267 Object* o = cache->get(finger_index);
10268 if (o == key) {
10269 // The fastest case: hit the same place again.
10270 return cache->get(finger_index + 1);
10271 }
10272
10273 for (int i = finger_index - 2;
10274 i >= JSFunctionResultCache::kEntriesIndex;
10275 i -= 2) {
10276 o = cache->get(i);
10277 if (o == key) {
10278 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10279 return cache->get(i + 1);
10280 }
10281 }
10282
10283 const int size =
10284 Smi::cast(cache->get(JSFunctionResultCache::kCacheSizeIndex))->value();
10285 ASSERT(size <= cache->length());
10286
10287 for (int i = size - 2; i > finger_index; i -= 2) {
10288 o = cache->get(i);
10289 if (o == key) {
10290 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10291 return cache->get(i + 1);
10292 }
10293 }
10294
10295 // Cache miss. If we have spare room, put new data into it, otherwise
10296 // evict post finger entry which must be least recently used.
10297 if (size < cache->length()) {
10298 cache->set(JSFunctionResultCache::kCacheSizeIndex, Smi::FromInt(size + 2));
10299 return CacheMiss(cache, size, key);
10300 } else {
antonm@chromium.org397e23c2010-04-21 12:00:05 +000010301 int target_index = finger_index + JSFunctionResultCache::kEntrySize;
10302 if (target_index == cache->length()) {
10303 target_index = JSFunctionResultCache::kEntriesIndex;
10304 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010305 return CacheMiss(cache, target_index, key);
10306 }
10307}
10308
kasper.lund44510672008-07-25 07:37:58 +000010309#ifdef DEBUG
10310// ListNatives is ONLY used by the fuzz-natives.js in debug mode
10311// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010312static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +000010313 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010314 HandleScope scope;
10315 Handle<JSArray> result = Factory::NewJSArray(0);
10316 int index = 0;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010317 bool inline_runtime_functions = false;
ager@chromium.orga1645e22009-09-09 19:27:10 +000010318#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010319 { \
10320 HandleScope inner; \
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010321 Handle<String> name; \
10322 /* Inline runtime functions have an underscore in front of the name. */ \
10323 if (inline_runtime_functions) { \
10324 name = Factory::NewStringFromAscii( \
10325 Vector<const char>("_" #Name, StrLength("_" #Name))); \
10326 } else { \
10327 name = Factory::NewStringFromAscii( \
10328 Vector<const char>(#Name, StrLength(#Name))); \
10329 } \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010330 Handle<JSArray> pair = Factory::NewJSArray(0); \
10331 SetElement(pair, 0, name); \
10332 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
10333 SetElement(result, index++, pair); \
10334 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010335 inline_runtime_functions = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010336 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010337 inline_runtime_functions = true;
10338 INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010339#undef ADD_ENTRY
10340 return *result;
10341}
kasper.lund44510672008-07-25 07:37:58 +000010342#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010343
10344
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010345static Object* Runtime_Log(Arguments args) {
10346 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +000010347 CONVERT_CHECKED(String, format, args[0]);
10348 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010349 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010350 Logger::LogRuntime(chars, elms);
10351 return Heap::undefined_value();
10352}
10353
10354
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010355static Object* Runtime_IS_VAR(Arguments args) {
10356 UNREACHABLE(); // implemented as macro in the parser
10357 return NULL;
10358}
10359
10360
10361// ----------------------------------------------------------------------------
10362// Implementation of Runtime
10363
ager@chromium.orga1645e22009-09-09 19:27:10 +000010364#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010365 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +000010366 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010367
10368static Runtime::Function Runtime_functions[] = {
10369 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010370 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010371};
10372
10373#undef F
10374
10375
10376Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
10377 ASSERT(0 <= fid && fid < kNofFunctions);
10378 return &Runtime_functions[fid];
10379}
10380
10381
10382Runtime::Function* Runtime::FunctionForName(const char* name) {
10383 for (Function* f = Runtime_functions; f->name != NULL; f++) {
10384 if (strcmp(f->name, name) == 0) {
10385 return f;
10386 }
10387 }
10388 return NULL;
10389}
10390
10391
10392void Runtime::PerformGC(Object* result) {
10393 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010394 if (failure->IsRetryAfterGC()) {
10395 // Try to do a garbage collection; ignore it if it fails. The C
10396 // entry stub will throw an out-of-memory exception in that case.
10397 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
10398 } else {
10399 // Handle last resort GC and make sure to allow future allocations
10400 // to grow the heap without causing GCs (if possible).
10401 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +000010402 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010403 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010404}
10405
10406
10407} } // namespace v8::internal