blob: ab7701835032820360f01d86ece1e2bcf366b101 [file] [log] [blame]
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001// Copyright 2006-2009 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include <stdlib.h>
29
30#include "v8.h"
31
32#include "accessors.h"
33#include "api.h"
34#include "arguments.h"
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000035#include "codegen.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000036#include "compiler.h"
37#include "cpu.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000038#include "dateparser-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000039#include "debug.h"
40#include "execution.h"
41#include "jsregexp.h"
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000042#include "liveedit.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000043#include "parser.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000044#include "platform.h"
45#include "runtime.h"
46#include "scopeinfo.h"
ager@chromium.org7c537e22008-10-16 08:43:32 +000047#include "smart-pointer.h"
ager@chromium.org18ad94b2009-09-02 08:22:29 +000048#include "stub-cache.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000049#include "v8threads.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000050
kasperl@chromium.org71affb52009-05-26 05:44:31 +000051namespace v8 {
52namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000053
54
ager@chromium.org3e875802009-06-29 08:26:34 +000055#define RUNTIME_ASSERT(value) \
56 if (!(value)) return Top::ThrowIllegalOperation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000057
58// Cast the given object to a value of the specified type and store
59// it in a variable with the given name. If the object is not of the
60// expected type call IllegalOperation and return.
61#define CONVERT_CHECKED(Type, name, obj) \
62 RUNTIME_ASSERT(obj->Is##Type()); \
63 Type* name = Type::cast(obj);
64
65#define CONVERT_ARG_CHECKED(Type, name, index) \
66 RUNTIME_ASSERT(args[index]->Is##Type()); \
67 Handle<Type> name = args.at<Type>(index);
68
kasper.lundbd3ec4e2008-07-09 11:06:54 +000069// Cast the given object to a boolean and store it in a variable with
70// the given name. If the object is not a boolean call IllegalOperation
71// and return.
72#define CONVERT_BOOLEAN_CHECKED(name, obj) \
73 RUNTIME_ASSERT(obj->IsBoolean()); \
74 bool name = (obj)->IsTrue();
75
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000076// Cast the given object to a Smi and store its value in an int variable
77// with the given name. If the object is not a Smi call IllegalOperation
78// and return.
79#define CONVERT_SMI_CHECKED(name, obj) \
80 RUNTIME_ASSERT(obj->IsSmi()); \
81 int name = Smi::cast(obj)->value();
82
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000083// Cast the given object to a double and store it in a variable with
84// the given name. If the object is not a number (as opposed to
85// the number not-a-number) call IllegalOperation and return.
86#define CONVERT_DOUBLE_CHECKED(name, obj) \
87 RUNTIME_ASSERT(obj->IsNumber()); \
88 double name = (obj)->Number();
89
90// Call the specified converter on the object *comand store the result in
91// a variable of the specified type with the given name. If the
92// object is not a Number call IllegalOperation and return.
93#define CONVERT_NUMBER_CHECKED(type, name, Type, obj) \
94 RUNTIME_ASSERT(obj->IsNumber()); \
95 type name = NumberTo##Type(obj);
96
97// Non-reentrant string buffer for efficient general use in this file.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000098static StaticResource<StringInputBuffer> runtime_string_input_buffer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000099
100
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000101static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
102 StackLimitCheck check;
103 if (check.HasOverflowed()) return Top::StackOverflow();
104
105 Object* result = Heap::CopyJSObject(boilerplate);
106 if (result->IsFailure()) return result;
107 JSObject* copy = JSObject::cast(result);
108
109 // Deep copy local properties.
110 if (copy->HasFastProperties()) {
111 FixedArray* properties = copy->properties();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000112 for (int i = 0; i < properties->length(); i++) {
113 Object* value = properties->get(i);
114 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000115 JSObject* js_object = JSObject::cast(value);
116 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000117 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000118 properties->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000119 }
120 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000121 int nof = copy->map()->inobject_properties();
122 for (int i = 0; i < nof; i++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000123 Object* value = copy->InObjectPropertyAt(i);
124 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000125 JSObject* js_object = JSObject::cast(value);
126 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000127 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000128 copy->InObjectPropertyAtPut(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000129 }
130 }
131 } else {
132 result = Heap::AllocateFixedArray(copy->NumberOfLocalProperties(NONE));
133 if (result->IsFailure()) return result;
134 FixedArray* names = FixedArray::cast(result);
135 copy->GetLocalPropertyNames(names, 0);
136 for (int i = 0; i < names->length(); i++) {
137 ASSERT(names->get(i)->IsString());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000138 String* key_string = String::cast(names->get(i));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000139 PropertyAttributes attributes =
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000140 copy->GetLocalPropertyAttribute(key_string);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000141 // Only deep copy fields from the object literal expression.
142 // In particular, don't try to copy the length attribute of
143 // an array.
144 if (attributes != NONE) continue;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000145 Object* value = copy->GetProperty(key_string, &attributes);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000146 ASSERT(!value->IsFailure());
147 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000148 JSObject* js_object = JSObject::cast(value);
149 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000150 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000151 result = copy->SetProperty(key_string, result, NONE);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000152 if (result->IsFailure()) return result;
153 }
154 }
155 }
156
157 // Deep copy local elements.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000158 // Pixel elements cannot be created using an object literal.
ager@chromium.org3811b432009-10-28 14:53:37 +0000159 ASSERT(!copy->HasPixelElements() && !copy->HasExternalArrayElements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000160 switch (copy->GetElementsKind()) {
161 case JSObject::FAST_ELEMENTS: {
162 FixedArray* elements = FixedArray::cast(copy->elements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000163 for (int i = 0; i < elements->length(); i++) {
164 Object* value = elements->get(i);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000165 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000166 JSObject* js_object = JSObject::cast(value);
167 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000168 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000169 elements->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000170 }
171 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000172 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000173 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000174 case JSObject::DICTIONARY_ELEMENTS: {
175 NumberDictionary* element_dictionary = copy->element_dictionary();
176 int capacity = element_dictionary->Capacity();
177 for (int i = 0; i < capacity; i++) {
178 Object* k = element_dictionary->KeyAt(i);
179 if (element_dictionary->IsKey(k)) {
180 Object* value = element_dictionary->ValueAt(i);
181 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000182 JSObject* js_object = JSObject::cast(value);
183 result = DeepCopyBoilerplate(js_object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000184 if (result->IsFailure()) return result;
185 element_dictionary->ValueAtPut(i, result);
186 }
187 }
188 }
189 break;
190 }
191 default:
192 UNREACHABLE();
193 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000194 }
195 return copy;
196}
197
198
199static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
200 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
201 return DeepCopyBoilerplate(boilerplate);
202}
203
204
205static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000206 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000207 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000208}
209
210
ager@chromium.org236ad962008-09-25 09:45:57 +0000211static Handle<Map> ComputeObjectLiteralMap(
212 Handle<Context> context,
213 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000214 bool* is_result_from_cache) {
ager@chromium.org32912102009-01-16 10:38:43 +0000215 int number_of_properties = constant_properties->length() / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000216 if (FLAG_canonicalize_object_literal_maps) {
217 // First find prefix of consecutive symbol keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000218 int number_of_symbol_keys = 0;
219 while ((number_of_symbol_keys < number_of_properties) &&
220 (constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
221 number_of_symbol_keys++;
222 }
223 // Based on the number of prefix symbols key we decide whether
224 // to use the map cache in the global context.
225 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000226 if ((number_of_symbol_keys == number_of_properties) &&
227 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000228 // Create the fixed array with the key.
229 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
230 for (int i = 0; i < number_of_symbol_keys; i++) {
231 keys->set(i, constant_properties->get(i*2));
232 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000233 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000234 return Factory::ObjectLiteralMapFromCache(context, keys);
235 }
236 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000237 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000238 return Factory::CopyMap(
239 Handle<Map>(context->object_function()->initial_map()),
240 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000241}
242
243
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000244static Handle<Object> CreateLiteralBoilerplate(
245 Handle<FixedArray> literals,
246 Handle<FixedArray> constant_properties);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000247
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000248
249static Handle<Object> CreateObjectLiteralBoilerplate(
250 Handle<FixedArray> literals,
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000251 Handle<FixedArray> constant_properties,
252 bool should_have_fast_elements) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000253 // Get the global context from the literals array. This is the
254 // context in which the function was created and we use the object
255 // function from this context to create the object literal. We do
256 // not use the object function from the current global context
257 // because this might be the object function from another context
258 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000259 Handle<Context> context =
260 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
261
262 bool is_result_from_cache;
263 Handle<Map> map = ComputeObjectLiteralMap(context,
264 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000265 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000266
ager@chromium.org236ad962008-09-25 09:45:57 +0000267 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000268
269 // Normalize the elements of the boilerplate to save space if needed.
270 if (!should_have_fast_elements) NormalizeElements(boilerplate);
271
ager@chromium.org32912102009-01-16 10:38:43 +0000272 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000273 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000274 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000275 length / 2,
ager@chromium.org236ad962008-09-25 09:45:57 +0000276 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000277 for (int index = 0; index < length; index +=2) {
278 Handle<Object> key(constant_properties->get(index+0));
279 Handle<Object> value(constant_properties->get(index+1));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000280 if (value->IsFixedArray()) {
281 // The value contains the constant_properties of a
282 // simple object literal.
283 Handle<FixedArray> array = Handle<FixedArray>::cast(value);
284 value = CreateLiteralBoilerplate(literals, array);
285 if (value.is_null()) return value;
286 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000287 Handle<Object> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000288 uint32_t element_index = 0;
289 if (key->IsSymbol()) {
290 // If key is a symbol it is not an array element.
291 Handle<String> name(String::cast(*key));
292 ASSERT(!name->AsArrayIndex(&element_index));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000293 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000294 } else if (Array::IndexFromObject(*key, &element_index)) {
295 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000296 result = SetElement(boilerplate, element_index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000297 } else {
298 // Non-uint32 number.
299 ASSERT(key->IsNumber());
300 double num = key->Number();
301 char arr[100];
302 Vector<char> buffer(arr, ARRAY_SIZE(arr));
303 const char* str = DoubleToCString(num, buffer);
304 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000305 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000306 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000307 // If setting the property on the boilerplate throws an
308 // exception, the exception is converted to an empty handle in
309 // the handle based operations. In that case, we need to
310 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000311 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000312 }
313 }
314
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000315 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000316}
317
318
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000319static Handle<Object> CreateArrayLiteralBoilerplate(
320 Handle<FixedArray> literals,
321 Handle<FixedArray> elements) {
322 // Create the JSArray.
323 Handle<JSFunction> constructor(
324 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
325 Handle<Object> object = Factory::NewJSObject(constructor);
326
327 Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
328
329 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
330 for (int i = 0; i < content->length(); i++) {
331 if (content->get(i)->IsFixedArray()) {
332 // The value contains the constant_properties of a
333 // simple object literal.
334 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
335 Handle<Object> result =
336 CreateLiteralBoilerplate(literals, fa);
337 if (result.is_null()) return result;
338 content->set(i, *result);
339 }
340 }
341
342 // Set the elements.
343 Handle<JSArray>::cast(object)->SetContent(*content);
344 return object;
345}
346
347
348static Handle<Object> CreateLiteralBoilerplate(
349 Handle<FixedArray> literals,
350 Handle<FixedArray> array) {
351 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
352 switch (CompileTimeValue::GetType(array)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000353 case CompileTimeValue::OBJECT_LITERAL_FAST_ELEMENTS:
354 return CreateObjectLiteralBoilerplate(literals, elements, true);
355 case CompileTimeValue::OBJECT_LITERAL_SLOW_ELEMENTS:
356 return CreateObjectLiteralBoilerplate(literals, elements, false);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000357 case CompileTimeValue::ARRAY_LITERAL:
358 return CreateArrayLiteralBoilerplate(literals, elements);
359 default:
360 UNREACHABLE();
361 return Handle<Object>::null();
362 }
363}
364
365
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000366static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000367 // Takes a FixedArray of elements containing the literal elements of
368 // the array literal and produces JSArray with those elements.
369 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000370 // which contains the context from which to get the Array function
371 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000372 HandleScope scope;
373 ASSERT(args.length() == 3);
374 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
375 CONVERT_SMI_CHECKED(literals_index, args[1]);
376 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000377
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000378 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
379 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000380
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000381 // Update the functions literal and return the boilerplate.
382 literals->set(literals_index, *object);
383 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000384}
385
386
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000387static Object* Runtime_CreateObjectLiteral(Arguments args) {
388 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000389 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000390 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
391 CONVERT_SMI_CHECKED(literals_index, args[1]);
392 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000393 CONVERT_SMI_CHECKED(fast_elements, args[3]);
394 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000395
396 // Check if boilerplate exists. If not, create it first.
397 Handle<Object> boilerplate(literals->get(literals_index));
398 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000399 boilerplate = CreateObjectLiteralBoilerplate(literals,
400 constant_properties,
401 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000402 if (boilerplate.is_null()) return Failure::Exception();
403 // Update the functions literal and return the boilerplate.
404 literals->set(literals_index, *boilerplate);
405 }
406 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
407}
408
409
410static Object* Runtime_CreateObjectLiteralShallow(Arguments args) {
411 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000412 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000413 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
414 CONVERT_SMI_CHECKED(literals_index, args[1]);
415 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000416 CONVERT_SMI_CHECKED(fast_elements, args[3]);
417 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000418
419 // Check if boilerplate exists. If not, create it first.
420 Handle<Object> boilerplate(literals->get(literals_index));
421 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000422 boilerplate = CreateObjectLiteralBoilerplate(literals,
423 constant_properties,
424 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000425 if (boilerplate.is_null()) return Failure::Exception();
426 // Update the functions literal and return the boilerplate.
427 literals->set(literals_index, *boilerplate);
428 }
429 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
430}
431
432
433static Object* Runtime_CreateArrayLiteral(Arguments args) {
434 HandleScope scope;
435 ASSERT(args.length() == 3);
436 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
437 CONVERT_SMI_CHECKED(literals_index, args[1]);
438 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
439
440 // Check if boilerplate exists. If not, create it first.
441 Handle<Object> boilerplate(literals->get(literals_index));
442 if (*boilerplate == Heap::undefined_value()) {
443 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
444 if (boilerplate.is_null()) return Failure::Exception();
445 // Update the functions literal and return the boilerplate.
446 literals->set(literals_index, *boilerplate);
447 }
448 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
449}
450
451
452static Object* Runtime_CreateArrayLiteralShallow(Arguments args) {
453 HandleScope scope;
454 ASSERT(args.length() == 3);
455 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
456 CONVERT_SMI_CHECKED(literals_index, args[1]);
457 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
458
459 // Check if boilerplate exists. If not, create it first.
460 Handle<Object> boilerplate(literals->get(literals_index));
461 if (*boilerplate == Heap::undefined_value()) {
462 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
463 if (boilerplate.is_null()) return Failure::Exception();
464 // Update the functions literal and return the boilerplate.
465 literals->set(literals_index, *boilerplate);
466 }
467 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
468}
469
470
ager@chromium.org32912102009-01-16 10:38:43 +0000471static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
472 ASSERT(args.length() == 2);
473 CONVERT_CHECKED(String, key, args[0]);
474 Object* value = args[1];
475 // Create a catch context extension object.
476 JSFunction* constructor =
477 Top::context()->global_context()->context_extension_function();
478 Object* object = Heap::AllocateJSObject(constructor);
479 if (object->IsFailure()) return object;
480 // Assign the exception value to the catch variable and make sure
481 // that the catch variable is DontDelete.
482 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
483 if (value->IsFailure()) return value;
484 return object;
485}
486
487
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000488static Object* Runtime_ClassOf(Arguments args) {
489 NoHandleAllocation ha;
490 ASSERT(args.length() == 1);
491 Object* obj = args[0];
492 if (!obj->IsJSObject()) return Heap::null_value();
493 return JSObject::cast(obj)->class_name();
494}
495
ager@chromium.org7c537e22008-10-16 08:43:32 +0000496
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000497static Object* Runtime_IsInPrototypeChain(Arguments args) {
498 NoHandleAllocation ha;
499 ASSERT(args.length() == 2);
500 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
501 Object* O = args[0];
502 Object* V = args[1];
503 while (true) {
504 Object* prototype = V->GetPrototype();
505 if (prototype->IsNull()) return Heap::false_value();
506 if (O == prototype) return Heap::true_value();
507 V = prototype;
508 }
509}
510
511
ager@chromium.org9085a012009-05-11 19:22:57 +0000512// Inserts an object as the hidden prototype of another object.
513static Object* Runtime_SetHiddenPrototype(Arguments args) {
514 NoHandleAllocation ha;
515 ASSERT(args.length() == 2);
516 CONVERT_CHECKED(JSObject, jsobject, args[0]);
517 CONVERT_CHECKED(JSObject, proto, args[1]);
518
519 // Sanity checks. The old prototype (that we are replacing) could
520 // theoretically be null, but if it is not null then check that we
521 // didn't already install a hidden prototype here.
522 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
523 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
524 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
525
526 // Allocate up front before we start altering state in case we get a GC.
527 Object* map_or_failure = proto->map()->CopyDropTransitions();
528 if (map_or_failure->IsFailure()) return map_or_failure;
529 Map* new_proto_map = Map::cast(map_or_failure);
530
531 map_or_failure = jsobject->map()->CopyDropTransitions();
532 if (map_or_failure->IsFailure()) return map_or_failure;
533 Map* new_map = Map::cast(map_or_failure);
534
535 // Set proto's prototype to be the old prototype of the object.
536 new_proto_map->set_prototype(jsobject->GetPrototype());
537 proto->set_map(new_proto_map);
538 new_proto_map->set_is_hidden_prototype();
539
540 // Set the object's prototype to proto.
541 new_map->set_prototype(proto);
542 jsobject->set_map(new_map);
543
544 return Heap::undefined_value();
545}
546
547
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000548static Object* Runtime_IsConstructCall(Arguments args) {
549 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000550 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000551 JavaScriptFrameIterator it;
552 return Heap::ToBoolean(it.frame()->IsConstructor());
553}
554
555
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000556// Recursively traverses hidden prototypes if property is not found
557static void GetOwnPropertyImplementation(JSObject* obj,
558 String* name,
559 LookupResult* result) {
560 obj->LocalLookupRealNamedProperty(name, result);
561
562 if (!result->IsProperty()) {
563 Object* proto = obj->GetPrototype();
564 if (proto->IsJSObject() &&
565 JSObject::cast(proto)->map()->is_hidden_prototype())
566 GetOwnPropertyImplementation(JSObject::cast(proto),
567 name, result);
568 }
569}
570
571
572// Returns an array with the property description:
573// if args[1] is not a property on args[0]
574// returns undefined
575// if args[1] is a data property on args[0]
576// [false, value, Writeable, Enumerable, Configurable]
577// if args[1] is an accessor on args[0]
578// [true, GetFunction, SetFunction, Enumerable, Configurable]
579static Object* Runtime_GetOwnProperty(Arguments args) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000580 ASSERT(args.length() == 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000581 HandleScope scope;
582 Handle<FixedArray> elms = Factory::NewFixedArray(5);
583 Handle<JSArray> desc = Factory::NewJSArrayWithElements(elms);
584 LookupResult result;
585 CONVERT_CHECKED(JSObject, obj, args[0]);
586 CONVERT_CHECKED(String, name, args[1]);
587
588 // Use recursive implementation to also traverse hidden prototypes
589 GetOwnPropertyImplementation(obj, name, &result);
590
591 if (!result.IsProperty())
592 return Heap::undefined_value();
593
594 if (result.type() == CALLBACKS) {
595 Object* structure = result.GetCallbackObject();
ager@chromium.org5c838252010-02-19 08:53:10 +0000596 if (structure->IsProxy() || structure->IsAccessorInfo()) {
597 // Property that is internally implemented as a callback or
598 // an API defined callback.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000599 Object* value = obj->GetPropertyWithCallback(
600 obj, structure, name, result.holder());
601 elms->set(0, Heap::false_value());
602 elms->set(1, value);
603 elms->set(2, Heap::ToBoolean(!result.IsReadOnly()));
604 } else if (structure->IsFixedArray()) {
605 // __defineGetter__/__defineSetter__ callback.
606 elms->set(0, Heap::true_value());
607 elms->set(1, FixedArray::cast(structure)->get(0));
608 elms->set(2, FixedArray::cast(structure)->get(1));
609 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000610 return Heap::undefined_value();
611 }
612 } else {
613 elms->set(0, Heap::false_value());
614 elms->set(1, result.GetLazyValue());
615 elms->set(2, Heap::ToBoolean(!result.IsReadOnly()));
616 }
617
618 elms->set(3, Heap::ToBoolean(!result.IsDontEnum()));
ager@chromium.org5c838252010-02-19 08:53:10 +0000619 elms->set(4, Heap::ToBoolean(!result.IsDontDelete()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000620 return *desc;
621}
622
623
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000624static Object* Runtime_IsExtensible(Arguments args) {
625 ASSERT(args.length() == 1);
626 CONVERT_CHECKED(JSObject, obj, args[0]);
627 return obj->map()->is_extensible() ? Heap::true_value()
628 : Heap::false_value();
629}
630
631
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000632static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000633 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000634 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000635 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
636 CONVERT_ARG_CHECKED(String, pattern, 1);
637 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000638 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
639 if (result.is_null()) return Failure::Exception();
640 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000641}
642
643
644static Object* Runtime_CreateApiFunction(Arguments args) {
645 HandleScope scope;
646 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000647 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000648 return *Factory::CreateApiFunction(data);
649}
650
651
652static Object* Runtime_IsTemplate(Arguments args) {
653 ASSERT(args.length() == 1);
654 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000655 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000656 return Heap::ToBoolean(result);
657}
658
659
660static Object* Runtime_GetTemplateField(Arguments args) {
661 ASSERT(args.length() == 2);
662 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000663 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000664 int index = field->value();
665 int offset = index * kPointerSize + HeapObject::kHeaderSize;
666 InstanceType type = templ->map()->instance_type();
667 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
668 type == OBJECT_TEMPLATE_INFO_TYPE);
669 RUNTIME_ASSERT(offset > 0);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000670 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000671 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
672 } else {
673 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
674 }
675 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000676}
677
678
ager@chromium.org870a0b62008-11-04 11:43:05 +0000679static Object* Runtime_DisableAccessChecks(Arguments args) {
680 ASSERT(args.length() == 1);
681 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000682 Map* old_map = object->map();
683 bool needs_access_checks = old_map->is_access_check_needed();
684 if (needs_access_checks) {
685 // Copy map so it won't interfere constructor's initial map.
686 Object* new_map = old_map->CopyDropTransitions();
687 if (new_map->IsFailure()) return new_map;
688
689 Map::cast(new_map)->set_is_access_check_needed(false);
690 object->set_map(Map::cast(new_map));
691 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000692 return needs_access_checks ? Heap::true_value() : Heap::false_value();
693}
694
695
696static Object* Runtime_EnableAccessChecks(Arguments args) {
697 ASSERT(args.length() == 1);
698 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000699 Map* old_map = object->map();
700 if (!old_map->is_access_check_needed()) {
701 // Copy map so it won't interfere constructor's initial map.
702 Object* new_map = old_map->CopyDropTransitions();
703 if (new_map->IsFailure()) return new_map;
704
705 Map::cast(new_map)->set_is_access_check_needed(true);
706 object->set_map(Map::cast(new_map));
707 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000708 return Heap::undefined_value();
709}
710
711
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000712static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
713 HandleScope scope;
714 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
715 Handle<Object> args[2] = { type_handle, name };
716 Handle<Object> error =
717 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
718 return Top::Throw(*error);
719}
720
721
722static Object* Runtime_DeclareGlobals(Arguments args) {
723 HandleScope scope;
724 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
725
ager@chromium.org3811b432009-10-28 14:53:37 +0000726 Handle<Context> context = args.at<Context>(0);
727 CONVERT_ARG_CHECKED(FixedArray, pairs, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000728 bool is_eval = Smi::cast(args[2])->value() == 1;
729
730 // Compute the property attributes. According to ECMA-262, section
731 // 13, page 71, the property must be read-only and
732 // non-deletable. However, neither SpiderMonkey nor KJS creates the
733 // property as read-only, so we don't either.
734 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
735
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000736 // Traverse the name/value pairs and set the properties.
737 int length = pairs->length();
738 for (int i = 0; i < length; i += 2) {
739 HandleScope scope;
740 Handle<String> name(String::cast(pairs->get(i)));
741 Handle<Object> value(pairs->get(i + 1));
742
743 // We have to declare a global const property. To capture we only
744 // assign to it when evaluating the assignment for "const x =
745 // <expr>" the initial value is the hole.
746 bool is_const_property = value->IsTheHole();
747
748 if (value->IsUndefined() || is_const_property) {
749 // Lookup the property in the global object, and don't set the
750 // value of the variable if the property is already there.
751 LookupResult lookup;
752 global->Lookup(*name, &lookup);
753 if (lookup.IsProperty()) {
754 // Determine if the property is local by comparing the holder
755 // against the global object. The information will be used to
756 // avoid throwing re-declaration errors when declaring
757 // variables or constants that exist in the prototype chain.
758 bool is_local = (*global == lookup.holder());
759 // Get the property attributes and determine if the property is
760 // read-only.
761 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
762 bool is_read_only = (attributes & READ_ONLY) != 0;
763 if (lookup.type() == INTERCEPTOR) {
764 // If the interceptor says the property is there, we
765 // just return undefined without overwriting the property.
766 // Otherwise, we continue to setting the property.
767 if (attributes != ABSENT) {
768 // Check if the existing property conflicts with regards to const.
769 if (is_local && (is_read_only || is_const_property)) {
770 const char* type = (is_read_only) ? "const" : "var";
771 return ThrowRedeclarationError(type, name);
772 };
773 // The property already exists without conflicting: Go to
774 // the next declaration.
775 continue;
776 }
777 // Fall-through and introduce the absent property by using
778 // SetProperty.
779 } else {
780 if (is_local && (is_read_only || is_const_property)) {
781 const char* type = (is_read_only) ? "const" : "var";
782 return ThrowRedeclarationError(type, name);
783 }
784 // The property already exists without conflicting: Go to
785 // the next declaration.
786 continue;
787 }
788 }
789 } else {
790 // Copy the function and update its context. Use it as value.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000791 Handle<SharedFunctionInfo> shared =
792 Handle<SharedFunctionInfo>::cast(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000793 Handle<JSFunction> function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000794 Factory::NewFunctionFromSharedFunctionInfo(shared, context, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000795 value = function;
796 }
797
798 LookupResult lookup;
799 global->LocalLookup(*name, &lookup);
800
801 PropertyAttributes attributes = is_const_property
802 ? static_cast<PropertyAttributes>(base | READ_ONLY)
803 : base;
804
805 if (lookup.IsProperty()) {
806 // There's a local property that we need to overwrite because
807 // we're either declaring a function or there's an interceptor
808 // that claims the property is absent.
809
810 // Check for conflicting re-declarations. We cannot have
811 // conflicting types in case of intercepted properties because
812 // they are absent.
813 if (lookup.type() != INTERCEPTOR &&
814 (lookup.IsReadOnly() || is_const_property)) {
815 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
816 return ThrowRedeclarationError(type, name);
817 }
818 SetProperty(global, name, value, attributes);
819 } else {
820 // If a property with this name does not already exist on the
821 // global object add the property locally. We take special
822 // precautions to always add it as a local property even in case
823 // of callbacks in the prototype chain (this rules out using
824 // SetProperty). Also, we must use the handle-based version to
825 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000826 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000827 }
828 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000829
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000830 return Heap::undefined_value();
831}
832
833
834static Object* Runtime_DeclareContextSlot(Arguments args) {
835 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000836 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000837
ager@chromium.org7c537e22008-10-16 08:43:32 +0000838 CONVERT_ARG_CHECKED(Context, context, 0);
839 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000840 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000841 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000842 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000843 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000844
845 // Declarations are always done in the function context.
846 context = Handle<Context>(context->fcontext());
847
848 int index;
849 PropertyAttributes attributes;
850 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000851 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000852 context->Lookup(name, flags, &index, &attributes);
853
854 if (attributes != ABSENT) {
855 // The name was declared before; check for conflicting
856 // re-declarations: This is similar to the code in parser.cc in
857 // the AstBuildingParser::Declare function.
858 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
859 // Functions are not read-only.
860 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
861 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
862 return ThrowRedeclarationError(type, name);
863 }
864
865 // Initialize it if necessary.
866 if (*initial_value != NULL) {
867 if (index >= 0) {
868 // The variable or constant context slot should always be in
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000869 // the function context or the arguments object.
870 if (holder->IsContext()) {
871 ASSERT(holder.is_identical_to(context));
872 if (((attributes & READ_ONLY) == 0) ||
873 context->get(index)->IsTheHole()) {
874 context->set(index, *initial_value);
875 }
876 } else {
877 Handle<JSObject>::cast(holder)->SetElement(index, *initial_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000878 }
879 } else {
880 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000881 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000882 SetProperty(context_ext, name, initial_value, mode);
883 }
884 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000885
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000886 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000887 // The property is not in the function context. It needs to be
888 // "declared" in the function context's extension context, or in the
889 // global context.
890 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000891 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000892 // The function context's extension context exists - use it.
893 context_ext = Handle<JSObject>(context->extension());
894 } else {
895 // The function context's extension context does not exists - allocate
896 // it.
897 context_ext = Factory::NewJSObject(Top::context_extension_function());
898 // And store it in the extension slot.
899 context->set_extension(*context_ext);
900 }
901 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000902
ager@chromium.org7c537e22008-10-16 08:43:32 +0000903 // Declare the property by setting it to the initial value if provided,
904 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
905 // constant declarations).
906 ASSERT(!context_ext->HasLocalProperty(*name));
907 Handle<Object> value(Heap::undefined_value());
908 if (*initial_value != NULL) value = initial_value;
909 SetProperty(context_ext, name, value, mode);
910 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
911 }
912
913 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000914}
915
916
917static Object* Runtime_InitializeVarGlobal(Arguments args) {
918 NoHandleAllocation nha;
919
920 // Determine if we need to assign to the variable if it already
921 // exists (based on the number of arguments).
922 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
923 bool assign = args.length() == 2;
924
925 CONVERT_ARG_CHECKED(String, name, 0);
926 GlobalObject* global = Top::context()->global();
927
928 // According to ECMA-262, section 12.2, page 62, the property must
929 // not be deletable.
930 PropertyAttributes attributes = DONT_DELETE;
931
932 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000933 // there, there is a property with this name in the prototype chain.
934 // We follow Safari and Firefox behavior and only set the property
935 // locally if there is an explicit initialization value that we have
936 // to assign to the property. When adding the property we take
937 // special precautions to always add it as a local property even in
938 // case of callbacks in the prototype chain (this rules out using
939 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
940 // this.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000941 // Note that objects can have hidden prototypes, so we need to traverse
942 // the whole chain of hidden prototypes to do a 'local' lookup.
943 JSObject* real_holder = global;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000944 LookupResult lookup;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000945 while (true) {
946 real_holder->LocalLookup(*name, &lookup);
947 if (lookup.IsProperty()) {
948 // Determine if this is a redeclaration of something read-only.
949 if (lookup.IsReadOnly()) {
950 // If we found readonly property on one of hidden prototypes,
951 // just shadow it.
952 if (real_holder != Top::context()->global()) break;
953 return ThrowRedeclarationError("const", name);
954 }
955
956 // Determine if this is a redeclaration of an intercepted read-only
957 // property and figure out if the property exists at all.
958 bool found = true;
959 PropertyType type = lookup.type();
960 if (type == INTERCEPTOR) {
961 HandleScope handle_scope;
962 Handle<JSObject> holder(real_holder);
963 PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
964 real_holder = *holder;
965 if (intercepted == ABSENT) {
966 // The interceptor claims the property isn't there. We need to
967 // make sure to introduce it.
968 found = false;
969 } else if ((intercepted & READ_ONLY) != 0) {
970 // The property is present, but read-only. Since we're trying to
971 // overwrite it with a variable declaration we must throw a
972 // re-declaration error. However if we found readonly property
973 // on one of hidden prototypes, just shadow it.
974 if (real_holder != Top::context()->global()) break;
975 return ThrowRedeclarationError("const", name);
976 }
977 }
978
979 if (found && !assign) {
980 // The global property is there and we're not assigning any value
981 // to it. Just return.
982 return Heap::undefined_value();
983 }
984
985 // Assign the value (or undefined) to the property.
986 Object* value = (assign) ? args[1] : Heap::undefined_value();
987 return real_holder->SetProperty(&lookup, *name, value, attributes);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000988 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000989
990 Object* proto = real_holder->GetPrototype();
991 if (!proto->IsJSObject())
992 break;
993
994 if (!JSObject::cast(proto)->map()->is_hidden_prototype())
995 break;
996
997 real_holder = JSObject::cast(proto);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000998 }
999
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001000 global = Top::context()->global();
1001 if (assign) {
1002 return global->IgnoreAttributesAndSetLocalProperty(*name,
1003 args[1],
1004 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001005 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001006 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001007}
1008
1009
1010static Object* Runtime_InitializeConstGlobal(Arguments args) {
1011 // All constants are declared with an initial value. The name
1012 // of the constant is the first argument and the initial value
1013 // is the second.
1014 RUNTIME_ASSERT(args.length() == 2);
1015 CONVERT_ARG_CHECKED(String, name, 0);
1016 Handle<Object> value = args.at<Object>(1);
1017
1018 // Get the current global object from top.
1019 GlobalObject* global = Top::context()->global();
1020
1021 // According to ECMA-262, section 12.2, page 62, the property must
1022 // not be deletable. Since it's a const, it must be READ_ONLY too.
1023 PropertyAttributes attributes =
1024 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
1025
1026 // Lookup the property locally in the global object. If it isn't
1027 // there, we add the property and take special precautions to always
1028 // add it as a local property even in case of callbacks in the
1029 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001030 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001031 LookupResult lookup;
1032 global->LocalLookup(*name, &lookup);
1033 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001034 return global->IgnoreAttributesAndSetLocalProperty(*name,
1035 *value,
1036 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001037 }
1038
1039 // Determine if this is a redeclaration of something not
1040 // read-only. In case the result is hidden behind an interceptor we
1041 // need to ask it for the property attributes.
1042 if (!lookup.IsReadOnly()) {
1043 if (lookup.type() != INTERCEPTOR) {
1044 return ThrowRedeclarationError("var", name);
1045 }
1046
1047 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
1048
1049 // Throw re-declaration error if the intercepted property is present
1050 // but not read-only.
1051 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
1052 return ThrowRedeclarationError("var", name);
1053 }
1054
1055 // Restore global object from context (in case of GC) and continue
1056 // with setting the value because the property is either absent or
1057 // read-only. We also have to do redo the lookup.
1058 global = Top::context()->global();
1059
1060 // BUG 1213579: Handle the case where we have to set a read-only
1061 // property through an interceptor and only do it if it's
1062 // uninitialized, e.g. the hole. Nirk...
1063 global->SetProperty(*name, *value, attributes);
1064 return *value;
1065 }
1066
1067 // Set the value, but only we're assigning the initial value to a
1068 // constant. For now, we determine this by checking if the
1069 // current value is the hole.
1070 PropertyType type = lookup.type();
1071 if (type == FIELD) {
1072 FixedArray* properties = global->properties();
1073 int index = lookup.GetFieldIndex();
1074 if (properties->get(index)->IsTheHole()) {
1075 properties->set(index, *value);
1076 }
1077 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001078 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
1079 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001080 }
1081 } else {
1082 // Ignore re-initialization of constants that have already been
1083 // assigned a function value.
1084 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
1085 }
1086
1087 // Use the set value as the result of the operation.
1088 return *value;
1089}
1090
1091
1092static Object* Runtime_InitializeConstContextSlot(Arguments args) {
1093 HandleScope scope;
1094 ASSERT(args.length() == 3);
1095
1096 Handle<Object> value(args[0]);
1097 ASSERT(!value->IsTheHole());
1098 CONVERT_ARG_CHECKED(Context, context, 1);
1099 Handle<String> name(String::cast(args[2]));
1100
1101 // Initializations are always done in the function context.
1102 context = Handle<Context>(context->fcontext());
1103
1104 int index;
1105 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001106 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001107 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001108 context->Lookup(name, flags, &index, &attributes);
1109
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001110 // In most situations, the property introduced by the const
1111 // declaration should be present in the context extension object.
1112 // However, because declaration and initialization are separate, the
1113 // property might have been deleted (if it was introduced by eval)
1114 // before we reach the initialization point.
1115 //
1116 // Example:
1117 //
1118 // function f() { eval("delete x; const x;"); }
1119 //
1120 // In that case, the initialization behaves like a normal assignment
1121 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001122 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001123 // Property was found in a context.
1124 if (holder->IsContext()) {
1125 // The holder cannot be the function context. If it is, there
1126 // should have been a const redeclaration error when declaring
1127 // the const property.
1128 ASSERT(!holder.is_identical_to(context));
1129 if ((attributes & READ_ONLY) == 0) {
1130 Handle<Context>::cast(holder)->set(index, *value);
1131 }
1132 } else {
1133 // The holder is an arguments object.
1134 ASSERT((attributes & READ_ONLY) == 0);
1135 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001136 }
1137 return *value;
1138 }
1139
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001140 // The property could not be found, we introduce it in the global
1141 // context.
1142 if (attributes == ABSENT) {
1143 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1144 SetProperty(global, name, value, NONE);
1145 return *value;
1146 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001147
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001148 // The property was present in a context extension object.
1149 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001150
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001151 if (*context_ext == context->extension()) {
1152 // This is the property that was introduced by the const
1153 // declaration. Set it if it hasn't been set before. NOTE: We
1154 // cannot use GetProperty() to get the current value as it
1155 // 'unholes' the value.
1156 LookupResult lookup;
1157 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1158 ASSERT(lookup.IsProperty()); // the property was declared
1159 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1160
1161 PropertyType type = lookup.type();
1162 if (type == FIELD) {
1163 FixedArray* properties = context_ext->properties();
1164 int index = lookup.GetFieldIndex();
1165 if (properties->get(index)->IsTheHole()) {
1166 properties->set(index, *value);
1167 }
1168 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001169 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
1170 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001171 }
1172 } else {
1173 // We should not reach here. Any real, named property should be
1174 // either a field or a dictionary slot.
1175 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001176 }
1177 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001178 // The property was found in a different context extension object.
1179 // Set it if it is not a read-only property.
1180 if ((attributes & READ_ONLY) == 0) {
1181 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1182 // Setting a property might throw an exception. Exceptions
1183 // are converted to empty handles in handle operations. We
1184 // need to convert back to exceptions here.
1185 if (set.is_null()) {
1186 ASSERT(Top::has_pending_exception());
1187 return Failure::Exception();
1188 }
1189 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001190 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001191
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001192 return *value;
1193}
1194
1195
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001196static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1197 Arguments args) {
1198 HandleScope scope;
1199 ASSERT(args.length() == 2);
1200 CONVERT_ARG_CHECKED(JSObject, object, 0);
1201 CONVERT_SMI_CHECKED(properties, args[1]);
1202 if (object->HasFastProperties()) {
1203 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1204 }
1205 return *object;
1206}
1207
1208
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001209static Object* Runtime_RegExpExec(Arguments args) {
1210 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001211 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001212 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1213 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001214 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001215 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001216 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001217 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001218 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001219 RUNTIME_ASSERT(index >= 0);
1220 RUNTIME_ASSERT(index <= subject->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001221 Counters::regexp_entry_runtime.Increment();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001222 Handle<Object> result = RegExpImpl::Exec(regexp,
1223 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001224 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001225 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001226 if (result.is_null()) return Failure::Exception();
1227 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001228}
1229
1230
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00001231static Object* Runtime_RegExpConstructResult(Arguments args) {
1232 ASSERT(args.length() == 3);
1233 CONVERT_SMI_CHECKED(elements_count, args[0]);
1234 if (elements_count > JSArray::kMaxFastElementsLength) {
1235 return Top::ThrowIllegalOperation();
1236 }
1237 Object* new_object = Heap::AllocateFixedArrayWithHoles(elements_count);
1238 if (new_object->IsFailure()) return new_object;
1239 FixedArray* elements = FixedArray::cast(new_object);
1240 new_object = Heap::AllocateRaw(JSRegExpResult::kSize,
1241 NEW_SPACE,
1242 OLD_POINTER_SPACE);
1243 if (new_object->IsFailure()) return new_object;
1244 {
1245 AssertNoAllocation no_gc;
1246 HandleScope scope;
1247 reinterpret_cast<HeapObject*>(new_object)->
1248 set_map(Top::global_context()->regexp_result_map());
1249 }
1250 JSArray* array = JSArray::cast(new_object);
1251 array->set_properties(Heap::empty_fixed_array());
1252 array->set_elements(elements);
1253 array->set_length(Smi::FromInt(elements_count));
1254 // Write in-object properties after the length of the array.
1255 array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, args[1]);
1256 array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, args[2]);
1257 return array;
1258}
1259
1260
lrn@chromium.org25156de2010-04-06 13:10:27 +00001261static Object* Runtime_RegExpInitializeObject(Arguments args) {
1262 AssertNoAllocation no_alloc;
1263 ASSERT(args.length() == 5);
1264 CONVERT_CHECKED(JSRegExp, regexp, args[0]);
1265 CONVERT_CHECKED(String, source, args[1]);
1266
1267 Object* global = args[2];
1268 if (!global->IsTrue()) global = Heap::false_value();
1269
1270 Object* ignoreCase = args[3];
1271 if (!ignoreCase->IsTrue()) ignoreCase = Heap::false_value();
1272
1273 Object* multiline = args[4];
1274 if (!multiline->IsTrue()) multiline = Heap::false_value();
1275
1276 Map* map = regexp->map();
1277 Object* constructor = map->constructor();
1278 if (constructor->IsJSFunction() &&
1279 JSFunction::cast(constructor)->initial_map() == map) {
1280 // If we still have the original map, set in-object properties directly.
1281 regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, source);
1282 // TODO(lrn): Consider skipping write barrier on booleans as well.
1283 // Both true and false should be in oldspace at all times.
1284 regexp->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, global);
1285 regexp->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex, ignoreCase);
1286 regexp->InObjectPropertyAtPut(JSRegExp::kMultilineFieldIndex, multiline);
1287 regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex,
1288 Smi::FromInt(0),
1289 SKIP_WRITE_BARRIER);
1290 return regexp;
1291 }
1292
1293 // Map has changed, so use generic, but slower, method.
1294 PropertyAttributes final =
1295 static_cast<PropertyAttributes>(READ_ONLY | DONT_ENUM | DONT_DELETE);
1296 PropertyAttributes writable =
1297 static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
1298 regexp->IgnoreAttributesAndSetLocalProperty(Heap::source_symbol(),
1299 source,
1300 final);
1301 regexp->IgnoreAttributesAndSetLocalProperty(Heap::global_symbol(),
1302 global,
1303 final);
1304 regexp->IgnoreAttributesAndSetLocalProperty(Heap::ignore_case_symbol(),
1305 ignoreCase,
1306 final);
1307 regexp->IgnoreAttributesAndSetLocalProperty(Heap::multiline_symbol(),
1308 multiline,
1309 final);
1310 regexp->IgnoreAttributesAndSetLocalProperty(Heap::last_index_symbol(),
1311 Smi::FromInt(0),
1312 writable);
1313 return regexp;
1314}
1315
1316
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001317static Object* Runtime_FinishArrayPrototypeSetup(Arguments args) {
1318 HandleScope scope;
1319 ASSERT(args.length() == 1);
1320 CONVERT_ARG_CHECKED(JSArray, prototype, 0);
1321 // This is necessary to enable fast checks for absence of elements
1322 // on Array.prototype and below.
1323 prototype->set_elements(Heap::empty_fixed_array());
1324 return Smi::FromInt(0);
1325}
1326
1327
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001328static void SetCustomCallGenerator(Handle<JSFunction> function,
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001329 ExternalReference* generator) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001330 if (function->shared()->function_data()->IsUndefined()) {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001331 function->shared()->set_function_data(*FromCData(generator->address()));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001332 }
1333}
1334
1335
1336static Handle<JSFunction> InstallBuiltin(Handle<JSObject> holder,
1337 const char* name,
1338 Builtins::Name builtin_name,
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001339 ExternalReference* generator = NULL) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001340 Handle<String> key = Factory::LookupAsciiSymbol(name);
1341 Handle<Code> code(Builtins::builtin(builtin_name));
1342 Handle<JSFunction> optimized = Factory::NewFunction(key,
1343 JS_OBJECT_TYPE,
1344 JSObject::kHeaderSize,
1345 code,
1346 false);
1347 optimized->shared()->DontAdaptArguments();
1348 if (generator != NULL) {
1349 SetCustomCallGenerator(optimized, generator);
1350 }
1351 SetProperty(holder, key, optimized, NONE);
1352 return optimized;
1353}
1354
1355
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001356Object* CompileArrayPushCall(CallStubCompiler* compiler,
1357 Object* object,
1358 JSObject* holder,
1359 JSFunction* function,
1360 String* name,
1361 StubCompiler::CheckType check) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001362 return compiler->CompileArrayPushCall(object, holder, function, name, check);
1363}
1364
1365
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001366Object* CompileArrayPopCall(CallStubCompiler* compiler,
1367 Object* object,
1368 JSObject* holder,
1369 JSFunction* function,
1370 String* name,
1371 StubCompiler::CheckType check) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001372 return compiler->CompileArrayPopCall(object, holder, function, name, check);
1373}
1374
1375
1376static Object* Runtime_SpecialArrayFunctions(Arguments args) {
1377 HandleScope scope;
1378 ASSERT(args.length() == 1);
1379 CONVERT_ARG_CHECKED(JSObject, holder, 0);
1380
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001381 ExternalReference pop = ExternalReference::compile_array_pop_call();
1382 ExternalReference push = ExternalReference::compile_array_push_call();
1383
1384 InstallBuiltin(holder, "pop", Builtins::ArrayPop, &pop);
1385 InstallBuiltin(holder, "push", Builtins::ArrayPush, &push);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001386 InstallBuiltin(holder, "shift", Builtins::ArrayShift);
1387 InstallBuiltin(holder, "unshift", Builtins::ArrayUnshift);
1388 InstallBuiltin(holder, "slice", Builtins::ArraySlice);
1389 InstallBuiltin(holder, "splice", Builtins::ArraySplice);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001390 InstallBuiltin(holder, "concat", Builtins::ArrayConcat);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001391
1392 return *holder;
1393}
1394
1395
ager@chromium.org357bf652010-04-12 11:30:10 +00001396static Object* Runtime_GetGlobalReceiver(Arguments args) {
1397 // Returns a real global receiver, not one of builtins object.
1398 Context* global_context = Top::context()->global()->global_context();
1399 return global_context->global()->global_receiver();
1400}
1401
1402
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001403static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1404 HandleScope scope;
1405 ASSERT(args.length() == 4);
1406 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1407 int index = Smi::cast(args[1])->value();
1408 Handle<String> pattern = args.at<String>(2);
1409 Handle<String> flags = args.at<String>(3);
1410
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001411 // Get the RegExp function from the context in the literals array.
1412 // This is the RegExp function from the context in which the
1413 // function was created. We do not use the RegExp function from the
1414 // current global context because this might be the RegExp function
1415 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001416 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001417 Handle<JSFunction>(
1418 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001419 // Compute the regular expression literal.
1420 bool has_pending_exception;
1421 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001422 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1423 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001424 if (has_pending_exception) {
1425 ASSERT(Top::has_pending_exception());
1426 return Failure::Exception();
1427 }
1428 literals->set(index, *regexp);
1429 return *regexp;
1430}
1431
1432
1433static Object* Runtime_FunctionGetName(Arguments args) {
1434 NoHandleAllocation ha;
1435 ASSERT(args.length() == 1);
1436
1437 CONVERT_CHECKED(JSFunction, f, args[0]);
1438 return f->shared()->name();
1439}
1440
1441
ager@chromium.org236ad962008-09-25 09:45:57 +00001442static Object* Runtime_FunctionSetName(Arguments args) {
1443 NoHandleAllocation ha;
1444 ASSERT(args.length() == 2);
1445
1446 CONVERT_CHECKED(JSFunction, f, args[0]);
1447 CONVERT_CHECKED(String, name, args[1]);
1448 f->shared()->set_name(name);
1449 return Heap::undefined_value();
1450}
1451
1452
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001453static Object* Runtime_FunctionGetScript(Arguments args) {
1454 HandleScope scope;
1455 ASSERT(args.length() == 1);
1456
1457 CONVERT_CHECKED(JSFunction, fun, args[0]);
1458 Handle<Object> script = Handle<Object>(fun->shared()->script());
1459 if (!script->IsScript()) return Heap::undefined_value();
1460
1461 return *GetScriptWrapper(Handle<Script>::cast(script));
1462}
1463
1464
1465static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1466 NoHandleAllocation ha;
1467 ASSERT(args.length() == 1);
1468
1469 CONVERT_CHECKED(JSFunction, f, args[0]);
1470 return f->shared()->GetSourceCode();
1471}
1472
1473
1474static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1475 NoHandleAllocation ha;
1476 ASSERT(args.length() == 1);
1477
1478 CONVERT_CHECKED(JSFunction, fun, args[0]);
1479 int pos = fun->shared()->start_position();
1480 return Smi::FromInt(pos);
1481}
1482
1483
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001484static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1485 ASSERT(args.length() == 2);
1486
1487 CONVERT_CHECKED(JSFunction, fun, args[0]);
1488 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1489
1490 Code* code = fun->code();
1491 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1492
1493 Address pc = code->address() + offset;
1494 return Smi::FromInt(fun->code()->SourcePosition(pc));
1495}
1496
1497
1498
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001499static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1500 NoHandleAllocation ha;
1501 ASSERT(args.length() == 2);
1502
1503 CONVERT_CHECKED(JSFunction, fun, args[0]);
1504 CONVERT_CHECKED(String, name, args[1]);
1505 fun->SetInstanceClassName(name);
1506 return Heap::undefined_value();
1507}
1508
1509
1510static Object* Runtime_FunctionSetLength(Arguments args) {
1511 NoHandleAllocation ha;
1512 ASSERT(args.length() == 2);
1513
1514 CONVERT_CHECKED(JSFunction, fun, args[0]);
1515 CONVERT_CHECKED(Smi, length, args[1]);
1516 fun->shared()->set_length(length->value());
1517 return length;
1518}
1519
1520
1521static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001522 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001523 ASSERT(args.length() == 2);
1524
1525 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001526 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1527 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001528 return args[0]; // return TOS
1529}
1530
1531
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001532static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1533 NoHandleAllocation ha;
1534 ASSERT(args.length() == 1);
1535
1536 CONVERT_CHECKED(JSFunction, f, args[0]);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001537 return f->shared()->IsApiFunction() ? Heap::true_value()
1538 : Heap::false_value();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001539}
1540
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001541static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1542 NoHandleAllocation ha;
1543 ASSERT(args.length() == 1);
1544
1545 CONVERT_CHECKED(JSFunction, f, args[0]);
1546 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1547}
1548
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001549
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001550static Object* Runtime_SetCode(Arguments args) {
1551 HandleScope scope;
1552 ASSERT(args.length() == 2);
1553
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001554 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001555 Handle<Object> code = args.at<Object>(1);
1556
1557 Handle<Context> context(target->context());
1558
1559 if (!code->IsNull()) {
1560 RUNTIME_ASSERT(code->IsJSFunction());
1561 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001562 Handle<SharedFunctionInfo> shared(fun->shared());
1563 SetExpectedNofProperties(target, shared->expected_nof_properties());
1564
1565 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001566 return Failure::Exception();
1567 }
1568 // Set the code, formal parameter count, and the length of the target
1569 // function.
1570 target->set_code(fun->code());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001571 target->shared()->set_length(shared->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001572 target->shared()->set_formal_parameter_count(
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001573 shared->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001574 // Set the source code of the target function to undefined.
1575 // SetCode is only used for built-in constructors like String,
1576 // Array, and Object, and some web code
1577 // doesn't like seeing source code for constructors.
1578 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001579 // Clear the optimization hints related to the compiled code as these are no
1580 // longer valid when the code is overwritten.
1581 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001582 context = Handle<Context>(fun->context());
1583
1584 // Make sure we get a fresh copy of the literal vector to avoid
1585 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001586 int number_of_literals = fun->NumberOfLiterals();
1587 Handle<FixedArray> literals =
1588 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001589 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001590 // Insert the object, regexp and array functions in the literals
1591 // array prefix. These are the functions that will be used when
1592 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001593 literals->set(JSFunction::kLiteralGlobalContextIndex,
1594 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001595 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001596 // It's okay to skip the write barrier here because the literals
1597 // are guaranteed to be in old space.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001598 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001599 }
1600
1601 target->set_context(*context);
1602 return *target;
1603}
1604
1605
1606static Object* CharCodeAt(String* subject, Object* index) {
1607 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001608 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001609 // Flatten the string. If someone wants to get a char at an index
1610 // in a cons string, it is likely that more indices will be
1611 // accessed.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001612 Object* flat = subject->TryFlatten();
1613 if (flat->IsFailure()) return flat;
1614 subject = String::cast(flat);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001615 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001616 return Heap::nan_value();
1617 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001618 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001619}
1620
1621
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001622static Object* CharFromCode(Object* char_code) {
1623 uint32_t code;
1624 if (Array::IndexFromObject(char_code, &code)) {
1625 if (code <= 0xffff) {
1626 return Heap::LookupSingleCharacterStringFromCode(code);
1627 }
1628 }
1629 return Heap::empty_string();
1630}
1631
1632
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001633static Object* Runtime_StringCharCodeAt(Arguments args) {
1634 NoHandleAllocation ha;
1635 ASSERT(args.length() == 2);
1636
1637 CONVERT_CHECKED(String, subject, args[0]);
1638 Object* index = args[1];
1639 return CharCodeAt(subject, index);
1640}
1641
1642
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001643static Object* Runtime_StringCharAt(Arguments args) {
1644 NoHandleAllocation ha;
1645 ASSERT(args.length() == 2);
1646
1647 CONVERT_CHECKED(String, subject, args[0]);
1648 Object* index = args[1];
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001649 Object* code = CharCodeAt(subject, index);
1650 if (code == Heap::nan_value()) {
1651 return Heap::undefined_value();
1652 }
1653 return CharFromCode(code);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001654}
1655
1656
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001657static Object* Runtime_CharFromCode(Arguments args) {
1658 NoHandleAllocation ha;
1659 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001660 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001661}
1662
lrn@chromium.org25156de2010-04-06 13:10:27 +00001663
1664class FixedArrayBuilder {
1665 public:
1666 explicit FixedArrayBuilder(int initial_capacity)
1667 : array_(Factory::NewFixedArrayWithHoles(initial_capacity)),
1668 length_(0) {
1669 // Require a non-zero initial size. Ensures that doubling the size to
1670 // extend the array will work.
1671 ASSERT(initial_capacity > 0);
1672 }
1673
1674 explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
1675 : array_(backing_store),
1676 length_(0) {
1677 // Require a non-zero initial size. Ensures that doubling the size to
1678 // extend the array will work.
1679 ASSERT(backing_store->length() > 0);
1680 }
1681
1682 bool HasCapacity(int elements) {
1683 int length = array_->length();
1684 int required_length = length_ + elements;
1685 return (length >= required_length);
1686 }
1687
1688 void EnsureCapacity(int elements) {
1689 int length = array_->length();
1690 int required_length = length_ + elements;
1691 if (length < required_length) {
1692 int new_length = length;
1693 do {
1694 new_length *= 2;
1695 } while (new_length < required_length);
1696 Handle<FixedArray> extended_array =
1697 Factory::NewFixedArrayWithHoles(new_length);
1698 array_->CopyTo(0, *extended_array, 0, length_);
1699 array_ = extended_array;
1700 }
1701 }
1702
1703 void Add(Object* value) {
1704 ASSERT(length_ < capacity());
1705 array_->set(length_, value);
1706 length_++;
1707 }
1708
1709 void Add(Smi* value) {
1710 ASSERT(length_ < capacity());
1711 array_->set(length_, value);
1712 length_++;
1713 }
1714
1715 Handle<FixedArray> array() {
1716 return array_;
1717 }
1718
1719 int length() {
1720 return length_;
1721 }
1722
1723 int capacity() {
1724 return array_->length();
1725 }
1726
1727 Handle<JSArray> ToJSArray() {
1728 Handle<JSArray> result_array = Factory::NewJSArrayWithElements(array_);
1729 result_array->set_length(Smi::FromInt(length_));
1730 return result_array;
1731 }
1732
1733 Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
1734 target_array->set_elements(*array_);
1735 target_array->set_length(Smi::FromInt(length_));
1736 return target_array;
1737 }
1738
1739 private:
1740 Handle<FixedArray> array_;
1741 int length_;
1742};
1743
1744
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001745// Forward declarations.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001746const int kStringBuilderConcatHelperLengthBits = 11;
1747const int kStringBuilderConcatHelperPositionBits = 19;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001748
1749template <typename schar>
1750static inline void StringBuilderConcatHelper(String*,
1751 schar*,
1752 FixedArray*,
1753 int);
1754
lrn@chromium.org25156de2010-04-06 13:10:27 +00001755typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
1756 StringBuilderSubstringLength;
1757typedef BitField<int,
1758 kStringBuilderConcatHelperLengthBits,
1759 kStringBuilderConcatHelperPositionBits>
1760 StringBuilderSubstringPosition;
1761
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001762
1763class ReplacementStringBuilder {
1764 public:
1765 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
lrn@chromium.org25156de2010-04-06 13:10:27 +00001766 : array_builder_(estimated_part_count),
1767 subject_(subject),
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001768 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001769 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001770 // Require a non-zero initial size. Ensures that doubling the size to
1771 // extend the array will work.
1772 ASSERT(estimated_part_count > 0);
1773 }
1774
lrn@chromium.org25156de2010-04-06 13:10:27 +00001775 static inline void AddSubjectSlice(FixedArrayBuilder* builder,
1776 int from,
1777 int to) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001778 ASSERT(from >= 0);
1779 int length = to - from;
1780 ASSERT(length > 0);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001781 if (StringBuilderSubstringLength::is_valid(length) &&
1782 StringBuilderSubstringPosition::is_valid(from)) {
1783 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1784 StringBuilderSubstringPosition::encode(from);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001785 builder->Add(Smi::FromInt(encoded_slice));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001786 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001787 // Otherwise encode as two smis.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001788 builder->Add(Smi::FromInt(-length));
1789 builder->Add(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001790 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00001791 }
1792
1793
1794 void EnsureCapacity(int elements) {
1795 array_builder_.EnsureCapacity(elements);
1796 }
1797
1798
1799 void AddSubjectSlice(int from, int to) {
1800 AddSubjectSlice(&array_builder_, from, to);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001801 IncrementCharacterCount(to - from);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001802 }
1803
1804
1805 void AddString(Handle<String> string) {
1806 int length = string->length();
1807 ASSERT(length > 0);
1808 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001809 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001810 is_ascii_ = false;
1811 }
1812 IncrementCharacterCount(length);
1813 }
1814
1815
1816 Handle<String> ToString() {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001817 if (array_builder_.length() == 0) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001818 return Factory::empty_string();
1819 }
1820
1821 Handle<String> joined_string;
1822 if (is_ascii_) {
1823 joined_string = NewRawAsciiString(character_count_);
1824 AssertNoAllocation no_alloc;
1825 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1826 char* char_buffer = seq->GetChars();
1827 StringBuilderConcatHelper(*subject_,
1828 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001829 *array_builder_.array(),
1830 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001831 } else {
1832 // Non-ASCII.
1833 joined_string = NewRawTwoByteString(character_count_);
1834 AssertNoAllocation no_alloc;
1835 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1836 uc16* char_buffer = seq->GetChars();
1837 StringBuilderConcatHelper(*subject_,
1838 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001839 *array_builder_.array(),
1840 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001841 }
1842 return joined_string;
1843 }
1844
1845
1846 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001847 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001848 V8::FatalProcessOutOfMemory("String.replace result too large.");
1849 }
1850 character_count_ += by;
1851 }
1852
lrn@chromium.org25156de2010-04-06 13:10:27 +00001853 Handle<JSArray> GetParts() {
1854 Handle<JSArray> result =
1855 Factory::NewJSArrayWithElements(array_builder_.array());
1856 result->set_length(Smi::FromInt(array_builder_.length()));
1857 return result;
1858 }
kmillikin@chromium.orgd9825192010-03-30 08:36:16 +00001859
lrn@chromium.org25156de2010-04-06 13:10:27 +00001860 private:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001861 Handle<String> NewRawAsciiString(int size) {
1862 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1863 }
1864
1865
1866 Handle<String> NewRawTwoByteString(int size) {
1867 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1868 }
1869
1870
1871 void AddElement(Object* element) {
1872 ASSERT(element->IsSmi() || element->IsString());
lrn@chromium.org25156de2010-04-06 13:10:27 +00001873 ASSERT(array_builder_.capacity() > array_builder_.length());
1874 array_builder_.Add(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001875 }
1876
lrn@chromium.org25156de2010-04-06 13:10:27 +00001877 FixedArrayBuilder array_builder_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001878 Handle<String> subject_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001879 int character_count_;
1880 bool is_ascii_;
1881};
1882
1883
1884class CompiledReplacement {
1885 public:
1886 CompiledReplacement()
1887 : parts_(1), replacement_substrings_(0) {}
1888
1889 void Compile(Handle<String> replacement,
1890 int capture_count,
1891 int subject_length);
1892
1893 void Apply(ReplacementStringBuilder* builder,
1894 int match_from,
1895 int match_to,
1896 Handle<JSArray> last_match_info);
1897
1898 // Number of distinct parts of the replacement pattern.
1899 int parts() {
1900 return parts_.length();
1901 }
1902 private:
1903 enum PartType {
1904 SUBJECT_PREFIX = 1,
1905 SUBJECT_SUFFIX,
1906 SUBJECT_CAPTURE,
1907 REPLACEMENT_SUBSTRING,
1908 REPLACEMENT_STRING,
1909
1910 NUMBER_OF_PART_TYPES
1911 };
1912
1913 struct ReplacementPart {
1914 static inline ReplacementPart SubjectMatch() {
1915 return ReplacementPart(SUBJECT_CAPTURE, 0);
1916 }
1917 static inline ReplacementPart SubjectCapture(int capture_index) {
1918 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1919 }
1920 static inline ReplacementPart SubjectPrefix() {
1921 return ReplacementPart(SUBJECT_PREFIX, 0);
1922 }
1923 static inline ReplacementPart SubjectSuffix(int subject_length) {
1924 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1925 }
1926 static inline ReplacementPart ReplacementString() {
1927 return ReplacementPart(REPLACEMENT_STRING, 0);
1928 }
1929 static inline ReplacementPart ReplacementSubString(int from, int to) {
1930 ASSERT(from >= 0);
1931 ASSERT(to > from);
1932 return ReplacementPart(-from, to);
1933 }
1934
1935 // If tag <= 0 then it is the negation of a start index of a substring of
1936 // the replacement pattern, otherwise it's a value from PartType.
1937 ReplacementPart(int tag, int data)
1938 : tag(tag), data(data) {
1939 // Must be non-positive or a PartType value.
1940 ASSERT(tag < NUMBER_OF_PART_TYPES);
1941 }
1942 // Either a value of PartType or a non-positive number that is
1943 // the negation of an index into the replacement string.
1944 int tag;
1945 // The data value's interpretation depends on the value of tag:
1946 // tag == SUBJECT_PREFIX ||
1947 // tag == SUBJECT_SUFFIX: data is unused.
1948 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1949 // tag == REPLACEMENT_SUBSTRING ||
1950 // tag == REPLACEMENT_STRING: data is index into array of substrings
1951 // of the replacement string.
1952 // tag <= 0: Temporary representation of the substring of the replacement
1953 // string ranging over -tag .. data.
1954 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1955 // substring objects.
1956 int data;
1957 };
1958
1959 template<typename Char>
1960 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1961 Vector<Char> characters,
1962 int capture_count,
1963 int subject_length) {
1964 int length = characters.length();
1965 int last = 0;
1966 for (int i = 0; i < length; i++) {
1967 Char c = characters[i];
1968 if (c == '$') {
1969 int next_index = i + 1;
1970 if (next_index == length) { // No next character!
1971 break;
1972 }
1973 Char c2 = characters[next_index];
1974 switch (c2) {
1975 case '$':
1976 if (i > last) {
1977 // There is a substring before. Include the first "$".
1978 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1979 last = next_index + 1; // Continue after the second "$".
1980 } else {
1981 // Let the next substring start with the second "$".
1982 last = next_index;
1983 }
1984 i = next_index;
1985 break;
1986 case '`':
1987 if (i > last) {
1988 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1989 }
1990 parts->Add(ReplacementPart::SubjectPrefix());
1991 i = next_index;
1992 last = i + 1;
1993 break;
1994 case '\'':
1995 if (i > last) {
1996 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1997 }
1998 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
1999 i = next_index;
2000 last = i + 1;
2001 break;
2002 case '&':
2003 if (i > last) {
2004 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2005 }
2006 parts->Add(ReplacementPart::SubjectMatch());
2007 i = next_index;
2008 last = i + 1;
2009 break;
2010 case '0':
2011 case '1':
2012 case '2':
2013 case '3':
2014 case '4':
2015 case '5':
2016 case '6':
2017 case '7':
2018 case '8':
2019 case '9': {
2020 int capture_ref = c2 - '0';
2021 if (capture_ref > capture_count) {
2022 i = next_index;
2023 continue;
2024 }
2025 int second_digit_index = next_index + 1;
2026 if (second_digit_index < length) {
2027 // Peek ahead to see if we have two digits.
2028 Char c3 = characters[second_digit_index];
2029 if ('0' <= c3 && c3 <= '9') { // Double digits.
2030 int double_digit_ref = capture_ref * 10 + c3 - '0';
2031 if (double_digit_ref <= capture_count) {
2032 next_index = second_digit_index;
2033 capture_ref = double_digit_ref;
2034 }
2035 }
2036 }
2037 if (capture_ref > 0) {
2038 if (i > last) {
2039 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2040 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002041 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002042 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
2043 last = next_index + 1;
2044 }
2045 i = next_index;
2046 break;
2047 }
2048 default:
2049 i = next_index;
2050 break;
2051 }
2052 }
2053 }
2054 if (length > last) {
2055 if (last == 0) {
2056 parts->Add(ReplacementPart::ReplacementString());
2057 } else {
2058 parts->Add(ReplacementPart::ReplacementSubString(last, length));
2059 }
2060 }
2061 }
2062
2063 ZoneList<ReplacementPart> parts_;
2064 ZoneList<Handle<String> > replacement_substrings_;
2065};
2066
2067
2068void CompiledReplacement::Compile(Handle<String> replacement,
2069 int capture_count,
2070 int subject_length) {
2071 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00002072 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002073 AssertNoAllocation no_alloc;
2074 ParseReplacementPattern(&parts_,
2075 replacement->ToAsciiVector(),
2076 capture_count,
2077 subject_length);
2078 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00002079 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002080 AssertNoAllocation no_alloc;
2081
2082 ParseReplacementPattern(&parts_,
2083 replacement->ToUC16Vector(),
2084 capture_count,
2085 subject_length);
2086 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002087 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002088 int substring_index = 0;
2089 for (int i = 0, n = parts_.length(); i < n; i++) {
2090 int tag = parts_[i].tag;
2091 if (tag <= 0) { // A replacement string slice.
2092 int from = -tag;
2093 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002094 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002095 parts_[i].tag = REPLACEMENT_SUBSTRING;
2096 parts_[i].data = substring_index;
2097 substring_index++;
2098 } else if (tag == REPLACEMENT_STRING) {
2099 replacement_substrings_.Add(replacement);
2100 parts_[i].data = substring_index;
2101 substring_index++;
2102 }
2103 }
2104}
2105
2106
2107void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
2108 int match_from,
2109 int match_to,
2110 Handle<JSArray> last_match_info) {
2111 for (int i = 0, n = parts_.length(); i < n; i++) {
2112 ReplacementPart part = parts_[i];
2113 switch (part.tag) {
2114 case SUBJECT_PREFIX:
2115 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
2116 break;
2117 case SUBJECT_SUFFIX: {
2118 int subject_length = part.data;
2119 if (match_to < subject_length) {
2120 builder->AddSubjectSlice(match_to, subject_length);
2121 }
2122 break;
2123 }
2124 case SUBJECT_CAPTURE: {
2125 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002126 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002127 int from = RegExpImpl::GetCapture(match_info, capture * 2);
2128 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
2129 if (from >= 0 && to > from) {
2130 builder->AddSubjectSlice(from, to);
2131 }
2132 break;
2133 }
2134 case REPLACEMENT_SUBSTRING:
2135 case REPLACEMENT_STRING:
2136 builder->AddString(replacement_substrings_[part.data]);
2137 break;
2138 default:
2139 UNREACHABLE();
2140 }
2141 }
2142}
2143
2144
2145
2146static Object* StringReplaceRegExpWithString(String* subject,
2147 JSRegExp* regexp,
2148 String* replacement,
2149 JSArray* last_match_info) {
2150 ASSERT(subject->IsFlat());
2151 ASSERT(replacement->IsFlat());
2152
2153 HandleScope handles;
2154
2155 int length = subject->length();
2156 Handle<String> subject_handle(subject);
2157 Handle<JSRegExp> regexp_handle(regexp);
2158 Handle<String> replacement_handle(replacement);
2159 Handle<JSArray> last_match_info_handle(last_match_info);
2160 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2161 subject_handle,
2162 0,
2163 last_match_info_handle);
2164 if (match.is_null()) {
2165 return Failure::Exception();
2166 }
2167 if (match->IsNull()) {
2168 return *subject_handle;
2169 }
2170
2171 int capture_count = regexp_handle->CaptureCount();
2172
2173 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002174 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002175 CompiledReplacement compiled_replacement;
2176 compiled_replacement.Compile(replacement_handle,
2177 capture_count,
2178 length);
2179
2180 bool is_global = regexp_handle->GetFlags().is_global();
2181
2182 // Guessing the number of parts that the final result string is built
2183 // from. Global regexps can match any number of times, so we guess
2184 // conservatively.
2185 int expected_parts =
2186 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
2187 ReplacementStringBuilder builder(subject_handle, expected_parts);
2188
2189 // Index of end of last match.
2190 int prev = 0;
2191
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002192 // Number of parts added by compiled replacement plus preceeding
2193 // string and possibly suffix after last match. It is possible for
2194 // all components to use two elements when encoded as two smis.
2195 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002196 bool matched = true;
2197 do {
2198 ASSERT(last_match_info_handle->HasFastElements());
2199 // Increase the capacity of the builder before entering local handle-scope,
2200 // so its internal buffer can safely allocate a new handle if it grows.
2201 builder.EnsureCapacity(parts_added_per_loop);
2202
2203 HandleScope loop_scope;
2204 int start, end;
2205 {
2206 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002207 FixedArray* match_info_array =
2208 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002209
2210 ASSERT_EQ(capture_count * 2 + 2,
2211 RegExpImpl::GetLastCaptureCount(match_info_array));
2212 start = RegExpImpl::GetCapture(match_info_array, 0);
2213 end = RegExpImpl::GetCapture(match_info_array, 1);
2214 }
2215
2216 if (prev < start) {
2217 builder.AddSubjectSlice(prev, start);
2218 }
2219 compiled_replacement.Apply(&builder,
2220 start,
2221 end,
2222 last_match_info_handle);
2223 prev = end;
2224
2225 // Only continue checking for global regexps.
2226 if (!is_global) break;
2227
2228 // Continue from where the match ended, unless it was an empty match.
2229 int next = end;
2230 if (start == end) {
2231 next = end + 1;
2232 if (next > length) break;
2233 }
2234
2235 match = RegExpImpl::Exec(regexp_handle,
2236 subject_handle,
2237 next,
2238 last_match_info_handle);
2239 if (match.is_null()) {
2240 return Failure::Exception();
2241 }
2242 matched = !match->IsNull();
2243 } while (matched);
2244
2245 if (prev < length) {
2246 builder.AddSubjectSlice(prev, length);
2247 }
2248
2249 return *(builder.ToString());
2250}
2251
2252
2253static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2254 ASSERT(args.length() == 4);
2255
2256 CONVERT_CHECKED(String, subject, args[0]);
2257 if (!subject->IsFlat()) {
2258 Object* flat_subject = subject->TryFlatten();
2259 if (flat_subject->IsFailure()) {
2260 return flat_subject;
2261 }
2262 subject = String::cast(flat_subject);
2263 }
2264
2265 CONVERT_CHECKED(String, replacement, args[2]);
2266 if (!replacement->IsFlat()) {
2267 Object* flat_replacement = replacement->TryFlatten();
2268 if (flat_replacement->IsFailure()) {
2269 return flat_replacement;
2270 }
2271 replacement = String::cast(flat_replacement);
2272 }
2273
2274 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2275 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2276
2277 ASSERT(last_match_info->HasFastElements());
2278
2279 return StringReplaceRegExpWithString(subject,
2280 regexp,
2281 replacement,
2282 last_match_info);
2283}
2284
2285
ager@chromium.org7c537e22008-10-16 08:43:32 +00002286// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2287// limit, we can fix the size of tables.
2288static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002289// Reduce alphabet to this size.
2290static const int kBMAlphabetSize = 0x100;
2291// For patterns below this length, the skip length of Boyer-Moore is too short
2292// to compensate for the algorithmic overhead compared to simple brute force.
2293static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002294
ager@chromium.org7c537e22008-10-16 08:43:32 +00002295// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2296// shift. Only allows the last kBMMaxShift characters of the needle
2297// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002298class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002299 public:
2300 BMGoodSuffixBuffers() {}
2301 inline void init(int needle_length) {
2302 ASSERT(needle_length > 1);
2303 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2304 int len = needle_length - start;
2305 biased_suffixes_ = suffixes_ - start;
2306 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2307 for (int i = 0; i <= len; i++) {
2308 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002309 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002310 }
2311 inline int& suffix(int index) {
2312 ASSERT(biased_suffixes_ + index >= suffixes_);
2313 return biased_suffixes_[index];
2314 }
2315 inline int& shift(int index) {
2316 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2317 return biased_good_suffix_shift_[index];
2318 }
2319 private:
2320 int suffixes_[kBMMaxShift + 1];
2321 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002322 int* biased_suffixes_;
2323 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002324 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2325};
2326
2327// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002328static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002329static BMGoodSuffixBuffers bmgs_buffers;
2330
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002331// State of the string match tables.
2332// SIMPLE: No usable content in the buffers.
2333// BOYER_MOORE_HORSPOOL: The bad_char_occurences table has been populated.
2334// BOYER_MOORE: The bmgs_buffers tables have also been populated.
2335// Whenever starting with a new needle, one should call InitializeStringSearch
2336// to determine which search strategy to use, and in the case of a long-needle
2337// strategy, the call also initializes the algorithm to SIMPLE.
2338enum StringSearchAlgorithm { SIMPLE_SEARCH, BOYER_MOORE_HORSPOOL, BOYER_MOORE };
2339static StringSearchAlgorithm algorithm;
2340
2341
ager@chromium.org7c537e22008-10-16 08:43:32 +00002342// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002343template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002344static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern) {
2345 // Only preprocess at most kBMMaxShift last characters of pattern.
2346 int start = pattern.length() < kBMMaxShift ? 0
2347 : pattern.length() - kBMMaxShift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002348 // Run forwards to populate bad_char_table, so that *last* instance
2349 // of character equivalence class is the one registered.
2350 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002351 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2352 : kBMAlphabetSize;
2353 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002354 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002355 } else {
2356 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002357 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002358 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002359 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002360 for (int i = start; i < pattern.length() - 1; i++) {
2361 pchar c = pattern[i];
2362 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002363 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002364 }
2365}
2366
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002367
ager@chromium.org7c537e22008-10-16 08:43:32 +00002368template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002369static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002370 int m = pattern.length();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002371 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002372 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002373 // Compute Good Suffix tables.
2374 bmgs_buffers.init(m);
2375
2376 bmgs_buffers.shift(m-1) = 1;
2377 bmgs_buffers.suffix(m) = m + 1;
2378 pchar last_char = pattern[m - 1];
2379 int suffix = m + 1;
2380 for (int i = m; i > start;) {
2381 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2382 if (bmgs_buffers.shift(suffix) == len) {
2383 bmgs_buffers.shift(suffix) = suffix - i;
2384 }
2385 suffix = bmgs_buffers.suffix(suffix);
2386 }
2387 i--;
2388 suffix--;
2389 bmgs_buffers.suffix(i) = suffix;
2390 if (suffix == m) {
2391 // No suffix to extend, so we check against last_char only.
2392 while (i > start && pattern[i - 1] != last_char) {
2393 if (bmgs_buffers.shift(m) == len) {
2394 bmgs_buffers.shift(m) = m - i;
2395 }
2396 i--;
2397 bmgs_buffers.suffix(i) = m;
2398 }
2399 if (i > start) {
2400 i--;
2401 suffix--;
2402 bmgs_buffers.suffix(i) = suffix;
2403 }
2404 }
2405 }
2406 if (suffix < m) {
2407 for (int i = start; i <= m; i++) {
2408 if (bmgs_buffers.shift(i) == len) {
2409 bmgs_buffers.shift(i) = suffix - start;
2410 }
2411 if (i == suffix) {
2412 suffix = bmgs_buffers.suffix(suffix);
2413 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002414 }
2415 }
2416}
2417
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002418
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002419template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002420static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002421 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002422 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002423 }
2424 if (sizeof(pchar) == 1) {
2425 if (char_code > String::kMaxAsciiCharCode) {
2426 return -1;
2427 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002428 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002429 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002430 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002431}
2432
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002433
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002434// Restricted simplified Boyer-Moore string matching.
2435// Uses only the bad-shift table of Boyer-Moore and only uses it
2436// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002437template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002438static int BoyerMooreHorspool(Vector<const schar> subject,
2439 Vector<const pchar> pattern,
2440 int start_index,
2441 bool* complete) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002442 ASSERT(algorithm <= BOYER_MOORE_HORSPOOL);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002443 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002444 int m = pattern.length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002445
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002446 int badness = -m;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002447
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002448 // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002449 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002450 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002451 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002452 // Perform search
2453 for (idx = start_index; idx <= n - m;) {
2454 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002455 int c;
2456 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002457 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002458 int shift = j - bc_occ;
2459 idx += shift;
2460 badness += 1 - shift; // at most zero, so badness cannot increase.
2461 if (idx > n - m) {
2462 *complete = true;
2463 return -1;
2464 }
2465 }
2466 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002467 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002468 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002469 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002470 return idx;
2471 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002472 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002473 // Badness increases by the number of characters we have
2474 // checked, and decreases by the number of characters we
2475 // can skip by shifting. It's a measure of how we are doing
2476 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002477 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002478 if (badness > 0) {
2479 *complete = false;
2480 return idx;
2481 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002482 }
2483 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002484 *complete = true;
2485 return -1;
2486}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002487
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002488
2489template <typename schar, typename pchar>
2490static int BoyerMooreIndexOf(Vector<const schar> subject,
2491 Vector<const pchar> pattern,
2492 int idx) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002493 ASSERT(algorithm <= BOYER_MOORE);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002494 int n = subject.length();
2495 int m = pattern.length();
2496 // Only preprocess at most kBMMaxShift last characters of pattern.
2497 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2498
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002499 pchar last_char = pattern[m - 1];
2500 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002501 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002502 int j = m - 1;
2503 schar c;
2504 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002505 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002506 idx += shift;
2507 if (idx > n - m) {
2508 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002509 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002510 }
2511 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2512 if (j < 0) {
2513 return idx;
2514 } else if (j < start) {
2515 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002516 // Fall back on BMH shift.
2517 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002518 } else {
2519 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002520 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002521 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002522 if (gs_shift > shift) {
2523 shift = gs_shift;
2524 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002525 idx += shift;
2526 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002527 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002528
2529 return -1;
2530}
2531
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002532
2533template <typename schar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002534static inline int SingleCharIndexOf(Vector<const schar> string,
2535 schar pattern_char,
2536 int start_index) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002537 if (sizeof(schar) == 1) {
2538 const schar* pos = reinterpret_cast<const schar*>(
2539 memchr(string.start() + start_index,
2540 pattern_char,
2541 string.length() - start_index));
2542 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002543 return static_cast<int>(pos - string.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002544 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002545 for (int i = start_index, n = string.length(); i < n; i++) {
2546 if (pattern_char == string[i]) {
2547 return i;
2548 }
2549 }
2550 return -1;
2551}
2552
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002553
2554template <typename schar>
2555static int SingleCharLastIndexOf(Vector<const schar> string,
2556 schar pattern_char,
2557 int start_index) {
2558 for (int i = start_index; i >= 0; i--) {
2559 if (pattern_char == string[i]) {
2560 return i;
2561 }
2562 }
2563 return -1;
2564}
2565
2566
ager@chromium.org7c537e22008-10-16 08:43:32 +00002567// Trivial string search for shorter strings.
2568// On return, if "complete" is set to true, the return value is the
2569// final result of searching for the patter in the subject.
2570// If "complete" is set to false, the return value is the index where
2571// further checking should start, i.e., it's guaranteed that the pattern
2572// does not occur at a position prior to the returned index.
2573template <typename pchar, typename schar>
2574static int SimpleIndexOf(Vector<const schar> subject,
2575 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002576 int idx,
2577 bool* complete) {
2578 // Badness is a count of how much work we have done. When we have
2579 // done enough work we decide it's probably worth switching to a better
2580 // algorithm.
2581 int badness = -10 - (pattern.length() << 2);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002582
ager@chromium.org7c537e22008-10-16 08:43:32 +00002583 // We know our pattern is at least 2 characters, we cache the first so
2584 // the common case of the first character not matching is faster.
2585 pchar pattern_first_char = pattern[0];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002586 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2587 badness++;
2588 if (badness > 0) {
2589 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002590 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002591 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002592 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2593 const schar* pos = reinterpret_cast<const schar*>(
2594 memchr(subject.start() + i,
2595 pattern_first_char,
2596 n - i + 1));
2597 if (pos == NULL) {
2598 *complete = true;
2599 return -1;
2600 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002601 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002602 } else {
2603 if (subject[i] != pattern_first_char) continue;
2604 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002605 int j = 1;
2606 do {
2607 if (pattern[j] != subject[i+j]) {
2608 break;
2609 }
2610 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002611 } while (j < pattern.length());
2612 if (j == pattern.length()) {
2613 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002614 return i;
2615 }
2616 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002617 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002618 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002619 return -1;
2620}
2621
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002622// Simple indexOf that never bails out. For short patterns only.
2623template <typename pchar, typename schar>
2624static int SimpleIndexOf(Vector<const schar> subject,
2625 Vector<const pchar> pattern,
2626 int idx) {
2627 pchar pattern_first_char = pattern[0];
2628 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002629 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2630 const schar* pos = reinterpret_cast<const schar*>(
2631 memchr(subject.start() + i,
2632 pattern_first_char,
2633 n - i + 1));
2634 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002635 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002636 } else {
2637 if (subject[i] != pattern_first_char) continue;
2638 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002639 int j = 1;
2640 do {
2641 if (pattern[j] != subject[i+j]) {
2642 break;
2643 }
2644 j++;
2645 } while (j < pattern.length());
2646 if (j == pattern.length()) {
2647 return i;
2648 }
2649 }
2650 return -1;
2651}
2652
2653
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002654// Strategy for searching for a string in another string.
2655enum StringSearchStrategy { SEARCH_FAIL, SEARCH_SHORT, SEARCH_LONG };
ager@chromium.org7c537e22008-10-16 08:43:32 +00002656
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002657
2658template <typename pchar>
2659static inline StringSearchStrategy InitializeStringSearch(
2660 Vector<const pchar> pat, bool ascii_subject) {
2661 ASSERT(pat.length() > 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002662 // We have an ASCII haystack and a non-ASCII needle. Check if there
2663 // really is a non-ASCII character in the needle and bail out if there
2664 // is.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002665 if (ascii_subject && sizeof(pchar) > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002666 for (int i = 0; i < pat.length(); i++) {
2667 uc16 c = pat[i];
2668 if (c > String::kMaxAsciiCharCode) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002669 return SEARCH_FAIL;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002670 }
2671 }
2672 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002673 if (pat.length() < kBMMinPatternLength) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002674 return SEARCH_SHORT;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002675 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002676 algorithm = SIMPLE_SEARCH;
2677 return SEARCH_LONG;
2678}
2679
2680
2681// Dispatch long needle searches to different algorithms.
2682template <typename schar, typename pchar>
2683static int ComplexIndexOf(Vector<const schar> sub,
2684 Vector<const pchar> pat,
2685 int start_index) {
2686 ASSERT(pat.length() >= kBMMinPatternLength);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002687 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002688 bool complete;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002689 int idx = start_index;
2690 switch (algorithm) {
2691 case SIMPLE_SEARCH:
2692 idx = SimpleIndexOf(sub, pat, idx, &complete);
2693 if (complete) return idx;
2694 BoyerMoorePopulateBadCharTable(pat);
2695 algorithm = BOYER_MOORE_HORSPOOL;
2696 // FALLTHROUGH.
2697 case BOYER_MOORE_HORSPOOL:
2698 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
2699 if (complete) return idx;
2700 // Build the Good Suffix table and continue searching.
2701 BoyerMoorePopulateGoodSuffixTable(pat);
2702 algorithm = BOYER_MOORE;
2703 // FALLTHROUGH.
2704 case BOYER_MOORE:
2705 return BoyerMooreIndexOf(sub, pat, idx);
2706 }
2707 UNREACHABLE();
2708 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002709}
2710
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002711
2712// Dispatch to different search strategies for a single search.
2713// If searching multiple times on the same needle, the search
2714// strategy should only be computed once and then dispatch to different
2715// loops.
2716template <typename schar, typename pchar>
2717static int StringSearch(Vector<const schar> sub,
2718 Vector<const pchar> pat,
2719 int start_index) {
2720 bool ascii_subject = (sizeof(schar) == 1);
2721 StringSearchStrategy strategy = InitializeStringSearch(pat, ascii_subject);
2722 switch (strategy) {
2723 case SEARCH_FAIL: return -1;
2724 case SEARCH_SHORT: return SimpleIndexOf(sub, pat, start_index);
2725 case SEARCH_LONG: return ComplexIndexOf(sub, pat, start_index);
2726 }
2727 UNREACHABLE();
2728 return -1;
2729}
2730
2731
ager@chromium.org7c537e22008-10-16 08:43:32 +00002732// Perform string match of pattern on subject, starting at start index.
2733// Caller must ensure that 0 <= start_index <= sub->length(),
2734// and should check that pat->length() + start_index <= sub->length()
2735int Runtime::StringMatch(Handle<String> sub,
2736 Handle<String> pat,
2737 int start_index) {
2738 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002739 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002740
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002741 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002742 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002743
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002744 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002745 if (start_index + pattern_length > subject_length) return -1;
2746
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002747 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002748 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002749 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002750
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002751 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002752 // character patterns linear search is necessary, so any smart
2753 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002754 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002755 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002756 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002757 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002758 if (pchar > String::kMaxAsciiCharCode) {
2759 return -1;
2760 }
2761 Vector<const char> ascii_vector =
2762 sub->ToAsciiVector().SubVector(start_index, subject_length);
2763 const void* pos = memchr(ascii_vector.start(),
2764 static_cast<const char>(pchar),
2765 static_cast<size_t>(ascii_vector.length()));
2766 if (pos == NULL) {
2767 return -1;
2768 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002769 return static_cast<int>(reinterpret_cast<const char*>(pos)
2770 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002771 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002772 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002773 }
2774
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002775 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002776 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002777 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002778
ager@chromium.org7c537e22008-10-16 08:43:32 +00002779 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2780 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002781 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002782 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002783 if (sub->IsAsciiRepresentation()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002784 return StringSearch(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002785 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002786 return StringSearch(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002787 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002788 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002789 if (sub->IsAsciiRepresentation()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002790 return StringSearch(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002791 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002792 return StringSearch(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002793}
2794
2795
2796static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002797 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002798 ASSERT(args.length() == 3);
2799
ager@chromium.org7c537e22008-10-16 08:43:32 +00002800 CONVERT_ARG_CHECKED(String, sub, 0);
2801 CONVERT_ARG_CHECKED(String, pat, 1);
2802
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002803 Object* index = args[2];
2804 uint32_t start_index;
2805 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2806
ager@chromium.org870a0b62008-11-04 11:43:05 +00002807 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002808 int position = Runtime::StringMatch(sub, pat, start_index);
2809 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002810}
2811
2812
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002813template <typename schar, typename pchar>
2814static int StringMatchBackwards(Vector<const schar> sub,
2815 Vector<const pchar> pat,
2816 int idx) {
2817 ASSERT(pat.length() >= 1);
2818 ASSERT(idx + pat.length() <= sub.length());
2819
2820 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
2821 for (int i = 0; i < pat.length(); i++) {
2822 uc16 c = pat[i];
2823 if (c > String::kMaxAsciiCharCode) {
2824 return -1;
2825 }
2826 }
2827 }
2828
2829 pchar pattern_first_char = pat[0];
2830 for (int i = idx; i >= 0; i--) {
2831 if (sub[i] != pattern_first_char) continue;
2832 int j = 1;
2833 while (j < pat.length()) {
2834 if (pat[j] != sub[i+j]) {
2835 break;
2836 }
2837 j++;
2838 }
2839 if (j == pat.length()) {
2840 return i;
2841 }
2842 }
2843 return -1;
2844}
2845
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002846static Object* Runtime_StringLastIndexOf(Arguments args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002847 HandleScope scope; // create a new handle scope
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002848 ASSERT(args.length() == 3);
2849
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002850 CONVERT_ARG_CHECKED(String, sub, 0);
2851 CONVERT_ARG_CHECKED(String, pat, 1);
2852
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002853 Object* index = args[2];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002854 uint32_t start_index;
2855 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2856
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002857 uint32_t pat_length = pat->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002858 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002859
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002860 if (start_index + pat_length > sub_length) {
2861 start_index = sub_length - pat_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002862 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002863
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002864 if (pat_length == 0) {
2865 return Smi::FromInt(start_index);
2866 }
2867
2868 if (!sub->IsFlat()) {
2869 FlattenString(sub);
2870 }
2871
2872 if (pat_length == 1) {
2873 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2874 if (sub->IsAsciiRepresentation()) {
2875 uc16 pchar = pat->Get(0);
2876 if (pchar > String::kMaxAsciiCharCode) {
2877 return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002878 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002879 return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
2880 static_cast<char>(pat->Get(0)),
2881 start_index));
2882 } else {
2883 return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
2884 pat->Get(0),
2885 start_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002886 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002887 }
2888
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002889 if (!pat->IsFlat()) {
2890 FlattenString(pat);
2891 }
2892
2893 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2894
2895 int position = -1;
2896
2897 if (pat->IsAsciiRepresentation()) {
2898 Vector<const char> pat_vector = pat->ToAsciiVector();
2899 if (sub->IsAsciiRepresentation()) {
2900 position = StringMatchBackwards(sub->ToAsciiVector(),
2901 pat_vector,
2902 start_index);
2903 } else {
2904 position = StringMatchBackwards(sub->ToUC16Vector(),
2905 pat_vector,
2906 start_index);
2907 }
2908 } else {
2909 Vector<const uc16> pat_vector = pat->ToUC16Vector();
2910 if (sub->IsAsciiRepresentation()) {
2911 position = StringMatchBackwards(sub->ToAsciiVector(),
2912 pat_vector,
2913 start_index);
2914 } else {
2915 position = StringMatchBackwards(sub->ToUC16Vector(),
2916 pat_vector,
2917 start_index);
2918 }
2919 }
2920
2921 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002922}
2923
2924
2925static Object* Runtime_StringLocaleCompare(Arguments args) {
2926 NoHandleAllocation ha;
2927 ASSERT(args.length() == 2);
2928
2929 CONVERT_CHECKED(String, str1, args[0]);
2930 CONVERT_CHECKED(String, str2, args[1]);
2931
2932 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002933 int str1_length = str1->length();
2934 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002935
2936 // Decide trivial cases without flattening.
2937 if (str1_length == 0) {
2938 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2939 return Smi::FromInt(-str2_length);
2940 } else {
2941 if (str2_length == 0) return Smi::FromInt(str1_length);
2942 }
2943
2944 int end = str1_length < str2_length ? str1_length : str2_length;
2945
2946 // No need to flatten if we are going to find the answer on the first
2947 // character. At this point we know there is at least one character
2948 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002949 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002950 if (d != 0) return Smi::FromInt(d);
2951
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002952 str1->TryFlatten();
2953 str2->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002954
2955 static StringInputBuffer buf1;
2956 static StringInputBuffer buf2;
2957
2958 buf1.Reset(str1);
2959 buf2.Reset(str2);
2960
2961 for (int i = 0; i < end; i++) {
2962 uint16_t char1 = buf1.GetNext();
2963 uint16_t char2 = buf2.GetNext();
2964 if (char1 != char2) return Smi::FromInt(char1 - char2);
2965 }
2966
2967 return Smi::FromInt(str1_length - str2_length);
2968}
2969
2970
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002971static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002972 NoHandleAllocation ha;
2973 ASSERT(args.length() == 3);
2974
2975 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002976 Object* from = args[1];
2977 Object* to = args[2];
2978 int start, end;
2979 // We have a fast integer-only case here to avoid a conversion to double in
2980 // the common case where from and to are Smis.
2981 if (from->IsSmi() && to->IsSmi()) {
2982 start = Smi::cast(from)->value();
2983 end = Smi::cast(to)->value();
2984 } else {
2985 CONVERT_DOUBLE_CHECKED(from_number, from);
2986 CONVERT_DOUBLE_CHECKED(to_number, to);
2987 start = FastD2I(from_number);
2988 end = FastD2I(to_number);
2989 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002990 RUNTIME_ASSERT(end >= start);
2991 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002992 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002993 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002994 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002995}
2996
2997
ager@chromium.org41826e72009-03-30 13:30:57 +00002998static Object* Runtime_StringMatch(Arguments args) {
2999 ASSERT_EQ(3, args.length());
3000
3001 CONVERT_ARG_CHECKED(String, subject, 0);
3002 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
3003 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
3004 HandleScope handles;
3005
3006 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
3007
3008 if (match.is_null()) {
3009 return Failure::Exception();
3010 }
3011 if (match->IsNull()) {
3012 return Heap::null_value();
3013 }
3014 int length = subject->length();
3015
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00003016 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00003017 ZoneList<int> offsets(8);
3018 do {
3019 int start;
3020 int end;
3021 {
3022 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003023 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00003024 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
3025 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
3026 }
3027 offsets.Add(start);
3028 offsets.Add(end);
3029 int index = start < end ? end : end + 1;
3030 if (index > length) break;
3031 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
3032 if (match.is_null()) {
3033 return Failure::Exception();
3034 }
3035 } while (!match->IsNull());
3036 int matches = offsets.length() / 2;
3037 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
3038 for (int i = 0; i < matches ; i++) {
3039 int from = offsets.at(i * 2);
3040 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003041 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00003042 }
3043 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
3044 result->set_length(Smi::FromInt(matches));
3045 return *result;
3046}
3047
3048
lrn@chromium.org25156de2010-04-06 13:10:27 +00003049// Two smis before and after the match, for very long strings.
3050const int kMaxBuilderEntriesPerRegExpMatch = 5;
3051
3052
3053static void SetLastMatchInfoNoCaptures(Handle<String> subject,
3054 Handle<JSArray> last_match_info,
3055 int match_start,
3056 int match_end) {
3057 // Fill last_match_info with a single capture.
3058 last_match_info->EnsureSize(2 + RegExpImpl::kLastMatchOverhead);
3059 AssertNoAllocation no_gc;
3060 FixedArray* elements = FixedArray::cast(last_match_info->elements());
3061 RegExpImpl::SetLastCaptureCount(elements, 2);
3062 RegExpImpl::SetLastInput(elements, *subject);
3063 RegExpImpl::SetLastSubject(elements, *subject);
3064 RegExpImpl::SetCapture(elements, 0, match_start);
3065 RegExpImpl::SetCapture(elements, 1, match_end);
3066}
3067
3068
3069template <typename schar>
3070static bool SearchCharMultiple(Vector<schar> subject,
3071 String* pattern,
3072 schar pattern_char,
3073 FixedArrayBuilder* builder,
3074 int* match_pos) {
3075 // Position of last match.
3076 int pos = *match_pos;
3077 int subject_length = subject.length();
3078 while (pos < subject_length) {
3079 int match_end = pos + 1;
3080 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3081 *match_pos = pos;
3082 return false;
3083 }
3084 int new_pos = SingleCharIndexOf(subject, pattern_char, match_end);
3085 if (new_pos >= 0) {
3086 // Match has been found.
3087 if (new_pos > match_end) {
3088 ReplacementStringBuilder::AddSubjectSlice(builder, match_end, new_pos);
3089 }
3090 pos = new_pos;
3091 builder->Add(pattern);
3092 } else {
3093 break;
3094 }
3095 }
3096 if (pos + 1 < subject_length) {
3097 ReplacementStringBuilder::AddSubjectSlice(builder, pos + 1, subject_length);
3098 }
3099 *match_pos = pos;
3100 return true;
3101}
3102
3103
3104static bool SearchCharMultiple(Handle<String> subject,
3105 Handle<String> pattern,
3106 Handle<JSArray> last_match_info,
3107 FixedArrayBuilder* builder) {
3108 ASSERT(subject->IsFlat());
3109 ASSERT_EQ(1, pattern->length());
3110 uc16 pattern_char = pattern->Get(0);
3111 // Treating position before first as initial "previous match position".
3112 int match_pos = -1;
3113
3114 for (;;) { // Break when search complete.
3115 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3116 AssertNoAllocation no_gc;
3117 if (subject->IsAsciiRepresentation()) {
3118 if (pattern_char > String::kMaxAsciiCharCode) {
3119 break;
3120 }
3121 Vector<const char> subject_vector = subject->ToAsciiVector();
3122 char pattern_ascii_char = static_cast<char>(pattern_char);
3123 bool complete = SearchCharMultiple<const char>(subject_vector,
3124 *pattern,
3125 pattern_ascii_char,
3126 builder,
3127 &match_pos);
3128 if (complete) break;
3129 } else {
3130 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3131 bool complete = SearchCharMultiple<const uc16>(subject_vector,
3132 *pattern,
3133 pattern_char,
3134 builder,
3135 &match_pos);
3136 if (complete) break;
3137 }
3138 }
3139
3140 if (match_pos >= 0) {
3141 SetLastMatchInfoNoCaptures(subject,
3142 last_match_info,
3143 match_pos,
3144 match_pos + 1);
3145 return true;
3146 }
3147 return false; // No matches at all.
3148}
3149
3150
3151template <typename schar, typename pchar>
3152static bool SearchStringMultiple(Vector<schar> subject,
3153 String* pattern,
3154 Vector<pchar> pattern_string,
3155 FixedArrayBuilder* builder,
3156 int* match_pos) {
3157 int pos = *match_pos;
3158 int subject_length = subject.length();
3159 int pattern_length = pattern_string.length();
3160 int max_search_start = subject_length - pattern_length;
3161 bool is_ascii = (sizeof(schar) == 1);
3162 StringSearchStrategy strategy =
3163 InitializeStringSearch(pattern_string, is_ascii);
3164 switch (strategy) {
3165 case SEARCH_FAIL: return false;
3166 case SEARCH_SHORT:
3167 while (pos <= max_search_start) {
3168 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3169 *match_pos = pos;
3170 return false;
3171 }
3172 // Position of end of previous match.
3173 int match_end = pos + pattern_length;
3174 int new_pos = SimpleIndexOf(subject, pattern_string, match_end);
3175 if (new_pos >= 0) {
3176 // A match.
3177 if (new_pos > match_end) {
3178 ReplacementStringBuilder::AddSubjectSlice(builder,
3179 match_end,
3180 new_pos);
3181 }
3182 pos = new_pos;
3183 builder->Add(pattern);
3184 } else {
3185 break;
3186 }
3187 }
3188 break;
3189 case SEARCH_LONG:
3190 while (pos <= max_search_start) {
3191 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3192 *match_pos = pos;
3193 return false;
3194 }
3195 int new_pos = ComplexIndexOf(subject,
3196 pattern_string,
3197 pos + pattern_length);
3198 if (new_pos >= 0) {
3199 // A match has been found.
3200 if (new_pos > pos) {
3201 ReplacementStringBuilder::AddSubjectSlice(builder, pos, new_pos);
3202 }
3203 pos = new_pos;
3204 builder->Add(pattern);
3205 } else {
3206 break;
3207 }
3208 }
3209 break;
3210 }
3211 if (pos < max_search_start) {
3212 ReplacementStringBuilder::AddSubjectSlice(builder,
3213 pos + pattern_length,
3214 subject_length);
3215 }
3216 *match_pos = pos;
3217 return true;
3218}
3219
3220
3221static bool SearchStringMultiple(Handle<String> subject,
3222 Handle<String> pattern,
3223 Handle<JSArray> last_match_info,
3224 FixedArrayBuilder* builder) {
3225 ASSERT(subject->IsFlat());
3226 ASSERT(pattern->IsFlat());
3227 ASSERT(pattern->length() > 1);
3228
3229 // Treating as if a previous match was before first character.
3230 int match_pos = -pattern->length();
3231
3232 for (;;) { // Break when search complete.
3233 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3234 AssertNoAllocation no_gc;
3235 if (subject->IsAsciiRepresentation()) {
3236 Vector<const char> subject_vector = subject->ToAsciiVector();
3237 if (pattern->IsAsciiRepresentation()) {
3238 if (SearchStringMultiple(subject_vector,
3239 *pattern,
3240 pattern->ToAsciiVector(),
3241 builder,
3242 &match_pos)) break;
3243 } else {
3244 if (SearchStringMultiple(subject_vector,
3245 *pattern,
3246 pattern->ToUC16Vector(),
3247 builder,
3248 &match_pos)) break;
3249 }
3250 } else {
3251 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3252 if (pattern->IsAsciiRepresentation()) {
3253 if (SearchStringMultiple(subject_vector,
3254 *pattern,
3255 pattern->ToAsciiVector(),
3256 builder,
3257 &match_pos)) break;
3258 } else {
3259 if (SearchStringMultiple(subject_vector,
3260 *pattern,
3261 pattern->ToUC16Vector(),
3262 builder,
3263 &match_pos)) break;
3264 }
3265 }
3266 }
3267
3268 if (match_pos >= 0) {
3269 SetLastMatchInfoNoCaptures(subject,
3270 last_match_info,
3271 match_pos,
3272 match_pos + pattern->length());
3273 return true;
3274 }
3275 return false; // No matches at all.
3276}
3277
3278
3279static RegExpImpl::IrregexpResult SearchRegExpNoCaptureMultiple(
3280 Handle<String> subject,
3281 Handle<JSRegExp> regexp,
3282 Handle<JSArray> last_match_array,
3283 FixedArrayBuilder* builder) {
3284 ASSERT(subject->IsFlat());
3285 int match_start = -1;
3286 int match_end = 0;
3287 int pos = 0;
3288 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3289 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3290
3291 OffsetsVector registers(required_registers);
3292 Vector<int> register_vector(registers.vector(), registers.length());
3293 int subject_length = subject->length();
3294
3295 for (;;) { // Break on failure, return on exception.
3296 RegExpImpl::IrregexpResult result =
3297 RegExpImpl::IrregexpExecOnce(regexp,
3298 subject,
3299 pos,
3300 register_vector);
3301 if (result == RegExpImpl::RE_SUCCESS) {
3302 match_start = register_vector[0];
3303 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3304 if (match_end < match_start) {
3305 ReplacementStringBuilder::AddSubjectSlice(builder,
3306 match_end,
3307 match_start);
3308 }
3309 match_end = register_vector[1];
3310 HandleScope loop_scope;
3311 builder->Add(*Factory::NewSubString(subject, match_start, match_end));
3312 if (match_start != match_end) {
3313 pos = match_end;
3314 } else {
3315 pos = match_end + 1;
3316 if (pos > subject_length) break;
3317 }
3318 } else if (result == RegExpImpl::RE_FAILURE) {
3319 break;
3320 } else {
3321 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3322 return result;
3323 }
3324 }
3325
3326 if (match_start >= 0) {
3327 if (match_end < subject_length) {
3328 ReplacementStringBuilder::AddSubjectSlice(builder,
3329 match_end,
3330 subject_length);
3331 }
3332 SetLastMatchInfoNoCaptures(subject,
3333 last_match_array,
3334 match_start,
3335 match_end);
3336 return RegExpImpl::RE_SUCCESS;
3337 } else {
3338 return RegExpImpl::RE_FAILURE; // No matches at all.
3339 }
3340}
3341
3342
3343static RegExpImpl::IrregexpResult SearchRegExpMultiple(
3344 Handle<String> subject,
3345 Handle<JSRegExp> regexp,
3346 Handle<JSArray> last_match_array,
3347 FixedArrayBuilder* builder) {
3348
3349 ASSERT(subject->IsFlat());
3350 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3351 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3352
3353 OffsetsVector registers(required_registers);
3354 Vector<int> register_vector(registers.vector(), registers.length());
3355
3356 RegExpImpl::IrregexpResult result =
3357 RegExpImpl::IrregexpExecOnce(regexp,
3358 subject,
3359 0,
3360 register_vector);
3361
3362 int capture_count = regexp->CaptureCount();
3363 int subject_length = subject->length();
3364
3365 // Position to search from.
3366 int pos = 0;
3367 // End of previous match. Differs from pos if match was empty.
3368 int match_end = 0;
3369 if (result == RegExpImpl::RE_SUCCESS) {
3370 // Need to keep a copy of the previous match for creating last_match_info
3371 // at the end, so we have two vectors that we swap between.
3372 OffsetsVector registers2(required_registers);
3373 Vector<int> prev_register_vector(registers2.vector(), registers2.length());
3374
3375 do {
3376 int match_start = register_vector[0];
3377 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3378 if (match_end < match_start) {
3379 ReplacementStringBuilder::AddSubjectSlice(builder,
3380 match_end,
3381 match_start);
3382 }
3383 match_end = register_vector[1];
3384
3385 {
3386 // Avoid accumulating new handles inside loop.
3387 HandleScope temp_scope;
3388 // Arguments array to replace function is match, captures, index and
3389 // subject, i.e., 3 + capture count in total.
3390 Handle<FixedArray> elements = Factory::NewFixedArray(3 + capture_count);
3391 elements->set(0, *Factory::NewSubString(subject,
3392 match_start,
3393 match_end));
3394 for (int i = 1; i <= capture_count; i++) {
3395 int start = register_vector[i * 2];
3396 if (start >= 0) {
3397 int end = register_vector[i * 2 + 1];
3398 ASSERT(start <= end);
3399 Handle<String> substring = Factory::NewSubString(subject,
3400 start,
3401 end);
3402 elements->set(i, *substring);
3403 } else {
3404 ASSERT(register_vector[i * 2 + 1] < 0);
3405 elements->set(i, Heap::undefined_value());
3406 }
3407 }
3408 elements->set(capture_count + 1, Smi::FromInt(match_start));
3409 elements->set(capture_count + 2, *subject);
3410 builder->Add(*Factory::NewJSArrayWithElements(elements));
3411 }
3412 // Swap register vectors, so the last successful match is in
3413 // prev_register_vector.
3414 Vector<int> tmp = prev_register_vector;
3415 prev_register_vector = register_vector;
3416 register_vector = tmp;
3417
3418 if (match_end > match_start) {
3419 pos = match_end;
3420 } else {
3421 pos = match_end + 1;
3422 if (pos > subject_length) {
3423 break;
3424 }
3425 }
3426
3427 result = RegExpImpl::IrregexpExecOnce(regexp,
3428 subject,
3429 pos,
3430 register_vector);
3431 } while (result == RegExpImpl::RE_SUCCESS);
3432
3433 if (result != RegExpImpl::RE_EXCEPTION) {
3434 // Finished matching, with at least one match.
3435 if (match_end < subject_length) {
3436 ReplacementStringBuilder::AddSubjectSlice(builder,
3437 match_end,
3438 subject_length);
3439 }
3440
3441 int last_match_capture_count = (capture_count + 1) * 2;
3442 int last_match_array_size =
3443 last_match_capture_count + RegExpImpl::kLastMatchOverhead;
3444 last_match_array->EnsureSize(last_match_array_size);
3445 AssertNoAllocation no_gc;
3446 FixedArray* elements = FixedArray::cast(last_match_array->elements());
3447 RegExpImpl::SetLastCaptureCount(elements, last_match_capture_count);
3448 RegExpImpl::SetLastSubject(elements, *subject);
3449 RegExpImpl::SetLastInput(elements, *subject);
3450 for (int i = 0; i < last_match_capture_count; i++) {
3451 RegExpImpl::SetCapture(elements, i, prev_register_vector[i]);
3452 }
3453 return RegExpImpl::RE_SUCCESS;
3454 }
3455 }
3456 // No matches at all, return failure or exception result directly.
3457 return result;
3458}
3459
3460
3461static Object* Runtime_RegExpExecMultiple(Arguments args) {
3462 ASSERT(args.length() == 4);
3463 HandleScope handles;
3464
3465 CONVERT_ARG_CHECKED(String, subject, 1);
3466 if (!subject->IsFlat()) { FlattenString(subject); }
3467 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
3468 CONVERT_ARG_CHECKED(JSArray, last_match_info, 2);
3469 CONVERT_ARG_CHECKED(JSArray, result_array, 3);
3470
3471 ASSERT(last_match_info->HasFastElements());
3472 ASSERT(regexp->GetFlags().is_global());
3473 Handle<FixedArray> result_elements;
3474 if (result_array->HasFastElements()) {
3475 result_elements =
3476 Handle<FixedArray>(FixedArray::cast(result_array->elements()));
3477 } else {
3478 result_elements = Factory::NewFixedArrayWithHoles(16);
3479 }
3480 FixedArrayBuilder builder(result_elements);
3481
3482 if (regexp->TypeTag() == JSRegExp::ATOM) {
3483 Handle<String> pattern(
3484 String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex)));
3485 int pattern_length = pattern->length();
3486 if (pattern_length == 1) {
3487 if (SearchCharMultiple(subject, pattern, last_match_info, &builder)) {
3488 return *builder.ToJSArray(result_array);
3489 }
3490 return Heap::null_value();
3491 }
3492
3493 if (!pattern->IsFlat()) FlattenString(pattern);
3494 if (SearchStringMultiple(subject, pattern, last_match_info, &builder)) {
3495 return *builder.ToJSArray(result_array);
3496 }
3497 return Heap::null_value();
3498 }
3499
3500 ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
3501
3502 RegExpImpl::IrregexpResult result;
3503 if (regexp->CaptureCount() == 0) {
3504 result = SearchRegExpNoCaptureMultiple(subject,
3505 regexp,
3506 last_match_info,
3507 &builder);
3508 } else {
3509 result = SearchRegExpMultiple(subject, regexp, last_match_info, &builder);
3510 }
3511 if (result == RegExpImpl::RE_SUCCESS) return *builder.ToJSArray(result_array);
3512 if (result == RegExpImpl::RE_FAILURE) return Heap::null_value();
3513 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3514 return Failure::Exception();
3515}
3516
3517
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003518static Object* Runtime_NumberToRadixString(Arguments args) {
3519 NoHandleAllocation ha;
3520 ASSERT(args.length() == 2);
3521
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003522 // Fast case where the result is a one character string.
3523 if (args[0]->IsSmi() && args[1]->IsSmi()) {
3524 int value = Smi::cast(args[0])->value();
3525 int radix = Smi::cast(args[1])->value();
3526 if (value >= 0 && value < radix) {
3527 RUNTIME_ASSERT(radix <= 36);
3528 // Character array used for conversion.
3529 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
3530 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
3531 }
3532 }
3533
3534 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003535 CONVERT_DOUBLE_CHECKED(value, args[0]);
3536 if (isnan(value)) {
3537 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3538 }
3539 if (isinf(value)) {
3540 if (value < 0) {
3541 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3542 }
3543 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3544 }
3545 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
3546 int radix = FastD2I(radix_number);
3547 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3548 char* str = DoubleToRadixCString(value, radix);
3549 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3550 DeleteArray(str);
3551 return result;
3552}
3553
3554
3555static Object* Runtime_NumberToFixed(Arguments args) {
3556 NoHandleAllocation ha;
3557 ASSERT(args.length() == 2);
3558
3559 CONVERT_DOUBLE_CHECKED(value, args[0]);
3560 if (isnan(value)) {
3561 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3562 }
3563 if (isinf(value)) {
3564 if (value < 0) {
3565 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3566 }
3567 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3568 }
3569 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3570 int f = FastD2I(f_number);
3571 RUNTIME_ASSERT(f >= 0);
3572 char* str = DoubleToFixedCString(value, f);
3573 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3574 DeleteArray(str);
3575 return res;
3576}
3577
3578
3579static Object* Runtime_NumberToExponential(Arguments args) {
3580 NoHandleAllocation ha;
3581 ASSERT(args.length() == 2);
3582
3583 CONVERT_DOUBLE_CHECKED(value, args[0]);
3584 if (isnan(value)) {
3585 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3586 }
3587 if (isinf(value)) {
3588 if (value < 0) {
3589 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3590 }
3591 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3592 }
3593 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3594 int f = FastD2I(f_number);
3595 RUNTIME_ASSERT(f >= -1 && f <= 20);
3596 char* str = DoubleToExponentialCString(value, f);
3597 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3598 DeleteArray(str);
3599 return res;
3600}
3601
3602
3603static Object* Runtime_NumberToPrecision(Arguments args) {
3604 NoHandleAllocation ha;
3605 ASSERT(args.length() == 2);
3606
3607 CONVERT_DOUBLE_CHECKED(value, args[0]);
3608 if (isnan(value)) {
3609 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3610 }
3611 if (isinf(value)) {
3612 if (value < 0) {
3613 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3614 }
3615 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3616 }
3617 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3618 int f = FastD2I(f_number);
3619 RUNTIME_ASSERT(f >= 1 && f <= 21);
3620 char* str = DoubleToPrecisionCString(value, f);
3621 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3622 DeleteArray(str);
3623 return res;
3624}
3625
3626
3627// Returns a single character string where first character equals
3628// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003629static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003630 if (index < static_cast<uint32_t>(string->length())) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003631 string->TryFlatten();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003632 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003633 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003634 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003635 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003636}
3637
3638
3639Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
3640 // Handle [] indexing on Strings
3641 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003642 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
3643 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003644 }
3645
3646 // Handle [] indexing on String objects
3647 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003648 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
3649 Handle<Object> result =
3650 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
3651 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003652 }
3653
3654 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003655 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003656 return prototype->GetElement(index);
3657 }
3658
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003659 return GetElement(object, index);
3660}
3661
3662
3663Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003664 return object->GetElement(index);
3665}
3666
3667
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003668Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
3669 HandleScope scope;
3670
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003671 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003672 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003673 Handle<Object> error =
3674 Factory::NewTypeError("non_object_property_load",
3675 HandleVector(args, 2));
3676 return Top::Throw(*error);
3677 }
3678
3679 // Check if the given key is an array index.
3680 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003681 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003682 return GetElementOrCharAt(object, index);
3683 }
3684
3685 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003686 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003687 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003688 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003689 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003690 bool has_pending_exception = false;
3691 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003692 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003693 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003694 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003695 }
3696
ager@chromium.org32912102009-01-16 10:38:43 +00003697 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003698 // the element if so.
3699 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003700 return GetElementOrCharAt(object, index);
3701 } else {
3702 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003703 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003704 }
3705}
3706
3707
3708static Object* Runtime_GetProperty(Arguments args) {
3709 NoHandleAllocation ha;
3710 ASSERT(args.length() == 2);
3711
3712 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003713 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003714
3715 return Runtime::GetObjectProperty(object, key);
3716}
3717
3718
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003719// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003720static Object* Runtime_KeyedGetProperty(Arguments args) {
3721 NoHandleAllocation ha;
3722 ASSERT(args.length() == 2);
3723
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003724 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00003725 // itself.
3726 //
3727 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00003728 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00003729 // global proxy object never has properties. This is the case
3730 // because the global proxy object forwards everything to its hidden
3731 // prototype including local lookups.
3732 //
3733 // Additionally, we need to make sure that we do not cache results
3734 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003735 if (args[0]->IsJSObject() &&
3736 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00003737 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003738 args[1]->IsString()) {
3739 JSObject* receiver = JSObject::cast(args[0]);
3740 String* key = String::cast(args[1]);
3741 if (receiver->HasFastProperties()) {
3742 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003743 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003744 int offset = KeyedLookupCache::Lookup(receiver_map, key);
3745 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003746 Object* value = receiver->FastPropertyAt(offset);
3747 return value->IsTheHole() ? Heap::undefined_value() : value;
3748 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003749 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003750 LookupResult result;
3751 receiver->LocalLookup(key, &result);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00003752 if (result.IsProperty() && result.type() == FIELD) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003753 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003754 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003755 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003756 }
3757 } else {
3758 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003759 StringDictionary* dictionary = receiver->property_dictionary();
3760 int entry = dictionary->FindEntry(key);
3761 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003762 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003763 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003764 if (!receiver->IsGlobalObject()) return value;
3765 value = JSGlobalPropertyCell::cast(value)->value();
3766 if (!value->IsTheHole()) return value;
3767 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003768 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003769 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003770 } else if (args[0]->IsString() && args[1]->IsSmi()) {
3771 // Fast case for string indexing using [] with a smi index.
3772 HandleScope scope;
3773 Handle<String> str = args.at<String>(0);
3774 int index = Smi::cast(args[1])->value();
3775 Handle<Object> result = GetCharAt(str, index);
3776 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003777 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003778
3779 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003780 return Runtime::GetObjectProperty(args.at<Object>(0),
3781 args.at<Object>(1));
3782}
3783
3784
ager@chromium.org5c838252010-02-19 08:53:10 +00003785static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
3786 ASSERT(args.length() == 5);
3787 HandleScope scope;
3788 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3789 CONVERT_CHECKED(String, name, args[1]);
3790 CONVERT_CHECKED(Smi, flag_setter, args[2]);
3791 CONVERT_CHECKED(JSFunction, fun, args[3]);
3792 CONVERT_CHECKED(Smi, flag_attr, args[4]);
3793 int unchecked = flag_attr->value();
3794 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3795 RUNTIME_ASSERT(!obj->IsNull());
3796 LookupResult result;
3797 obj->LocalLookupRealNamedProperty(name, &result);
3798
3799 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3800 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
3801 // delete it to avoid running into trouble in DefineAccessor, which
3802 // handles this incorrectly if the property is readonly (does nothing)
3803 if (result.IsProperty() &&
3804 (result.type() == FIELD || result.type() == NORMAL
3805 || result.type() == CONSTANT_FUNCTION)) {
3806 obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
3807 }
3808 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
3809}
3810
3811static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
3812 ASSERT(args.length() == 4);
3813 HandleScope scope;
3814 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
3815 CONVERT_ARG_CHECKED(String, name, 1);
3816 Handle<Object> obj_value = args.at<Object>(2);
3817
3818 CONVERT_CHECKED(Smi, flag, args[3]);
3819 int unchecked = flag->value();
3820 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3821
3822 LookupResult result;
3823 js_object->LocalLookupRealNamedProperty(*name, &result);
3824
3825 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3826
3827 // Take special care when attributes are different and there is already
3828 // a property. For simplicity we normalize the property which enables us
3829 // to not worry about changing the instance_descriptor and creating a new
3830 // map. The current version of SetObjectProperty does not handle attributes
3831 // correctly in the case where a property is a field and is reset with
3832 // new attributes.
3833 if (result.IsProperty() && attr != result.GetAttributes()) {
3834 // New attributes - normalize to avoid writing to instance descriptor
3835 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3836 // Use IgnoreAttributes version since a readonly property may be
3837 // overridden and SetProperty does not allow this.
3838 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
3839 *obj_value,
3840 attr);
3841 }
3842 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
3843}
3844
3845
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003846Object* Runtime::SetObjectProperty(Handle<Object> object,
3847 Handle<Object> key,
3848 Handle<Object> value,
3849 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003850 HandleScope scope;
3851
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003852 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003853 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003854 Handle<Object> error =
3855 Factory::NewTypeError("non_object_property_store",
3856 HandleVector(args, 2));
3857 return Top::Throw(*error);
3858 }
3859
3860 // If the object isn't a JavaScript object, we ignore the store.
3861 if (!object->IsJSObject()) return *value;
3862
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003863 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3864
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003865 // Check if the given key is an array index.
3866 uint32_t index;
3867 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003868 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3869 // of a string using [] notation. We need to support this too in
3870 // JavaScript.
3871 // In the case of a String object we just need to redirect the assignment to
3872 // the underlying string if the index is in range. Since the underlying
3873 // string does nothing with the assignment then we can ignore such
3874 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003875 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003876 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003877 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003878
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003879 Handle<Object> result = SetElement(js_object, index, value);
3880 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003881 return *value;
3882 }
3883
3884 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003885 Handle<Object> result;
3886 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003887 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003888 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003889 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003890 key_string->TryFlatten();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003891 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003892 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003893 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003894 return *value;
3895 }
3896
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003897 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003898 bool has_pending_exception = false;
3899 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3900 if (has_pending_exception) return Failure::Exception();
3901 Handle<String> name = Handle<String>::cast(converted);
3902
3903 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003904 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003905 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003906 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003907 }
3908}
3909
3910
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003911Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
3912 Handle<Object> key,
3913 Handle<Object> value,
3914 PropertyAttributes attr) {
3915 HandleScope scope;
3916
3917 // Check if the given key is an array index.
3918 uint32_t index;
3919 if (Array::IndexFromObject(*key, &index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003920 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3921 // of a string using [] notation. We need to support this too in
3922 // JavaScript.
3923 // In the case of a String object we just need to redirect the assignment to
3924 // the underlying string if the index is in range. Since the underlying
3925 // string does nothing with the assignment then we can ignore such
3926 // assignments.
3927 if (js_object->IsStringObjectWithCharacterAt(index)) {
3928 return *value;
3929 }
3930
3931 return js_object->SetElement(index, *value);
3932 }
3933
3934 if (key->IsString()) {
3935 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003936 return js_object->SetElement(index, *value);
3937 } else {
3938 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003939 key_string->TryFlatten();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003940 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
3941 *value,
3942 attr);
3943 }
3944 }
3945
3946 // Call-back into JavaScript to convert the key to a string.
3947 bool has_pending_exception = false;
3948 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3949 if (has_pending_exception) return Failure::Exception();
3950 Handle<String> name = Handle<String>::cast(converted);
3951
3952 if (name->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003953 return js_object->SetElement(index, *value);
3954 } else {
3955 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
3956 }
3957}
3958
3959
ager@chromium.orge2902be2009-06-08 12:21:35 +00003960Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
3961 Handle<Object> key) {
3962 HandleScope scope;
3963
3964 // Check if the given key is an array index.
3965 uint32_t index;
3966 if (Array::IndexFromObject(*key, &index)) {
3967 // In Firefox/SpiderMonkey, Safari and Opera you can access the
3968 // characters of a string using [] notation. In the case of a
3969 // String object we just need to redirect the deletion to the
3970 // underlying string if the index is in range. Since the
3971 // underlying string does nothing with the deletion, we can ignore
3972 // such deletions.
3973 if (js_object->IsStringObjectWithCharacterAt(index)) {
3974 return Heap::true_value();
3975 }
3976
3977 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
3978 }
3979
3980 Handle<String> key_string;
3981 if (key->IsString()) {
3982 key_string = Handle<String>::cast(key);
3983 } else {
3984 // Call-back into JavaScript to convert the key to a string.
3985 bool has_pending_exception = false;
3986 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3987 if (has_pending_exception) return Failure::Exception();
3988 key_string = Handle<String>::cast(converted);
3989 }
3990
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003991 key_string->TryFlatten();
ager@chromium.orge2902be2009-06-08 12:21:35 +00003992 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
3993}
3994
3995
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003996static Object* Runtime_SetProperty(Arguments args) {
3997 NoHandleAllocation ha;
3998 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
3999
4000 Handle<Object> object = args.at<Object>(0);
4001 Handle<Object> key = args.at<Object>(1);
4002 Handle<Object> value = args.at<Object>(2);
4003
4004 // Compute attributes.
4005 PropertyAttributes attributes = NONE;
4006 if (args.length() == 4) {
4007 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004008 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004009 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004010 RUNTIME_ASSERT(
4011 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4012 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004013 }
4014 return Runtime::SetObjectProperty(object, key, value, attributes);
4015}
4016
4017
4018// Set a local property, even if it is READ_ONLY. If the property does not
4019// exist, it will be added with attributes NONE.
4020static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
4021 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004022 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004023 CONVERT_CHECKED(JSObject, object, args[0]);
4024 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004025 // Compute attributes.
4026 PropertyAttributes attributes = NONE;
4027 if (args.length() == 4) {
4028 CONVERT_CHECKED(Smi, value_obj, args[3]);
4029 int unchecked_value = value_obj->value();
4030 // Only attribute bits should be set.
4031 RUNTIME_ASSERT(
4032 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4033 attributes = static_cast<PropertyAttributes>(unchecked_value);
4034 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004035
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004036 return object->
4037 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004038}
4039
4040
4041static Object* Runtime_DeleteProperty(Arguments args) {
4042 NoHandleAllocation ha;
4043 ASSERT(args.length() == 2);
4044
4045 CONVERT_CHECKED(JSObject, object, args[0]);
4046 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00004047 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004048}
4049
4050
ager@chromium.org9085a012009-05-11 19:22:57 +00004051static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
4052 Handle<String> key) {
4053 if (object->HasLocalProperty(*key)) return Heap::true_value();
4054 // Handle hidden prototypes. If there's a hidden prototype above this thing
4055 // then we have to check it for properties, because they are supposed to
4056 // look like they are on this object.
4057 Handle<Object> proto(object->GetPrototype());
4058 if (proto->IsJSObject() &&
4059 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
4060 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
4061 }
4062 return Heap::false_value();
4063}
4064
4065
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004066static Object* Runtime_HasLocalProperty(Arguments args) {
4067 NoHandleAllocation ha;
4068 ASSERT(args.length() == 2);
4069 CONVERT_CHECKED(String, key, args[1]);
4070
ager@chromium.org9085a012009-05-11 19:22:57 +00004071 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004072 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00004073 if (obj->IsJSObject()) {
4074 JSObject* object = JSObject::cast(obj);
4075 // Fast case - no interceptors.
4076 if (object->HasRealNamedProperty(key)) return Heap::true_value();
4077 // Slow case. Either it's not there or we have an interceptor. We should
4078 // have handles for this kind of deal.
4079 HandleScope scope;
4080 return HasLocalPropertyImplementation(Handle<JSObject>(object),
4081 Handle<String>(key));
4082 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004083 // Well, there is one exception: Handle [] on strings.
4084 uint32_t index;
4085 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00004086 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004087 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004088 return Heap::true_value();
4089 }
4090 }
4091 return Heap::false_value();
4092}
4093
4094
4095static Object* Runtime_HasProperty(Arguments args) {
4096 NoHandleAllocation na;
4097 ASSERT(args.length() == 2);
4098
4099 // Only JS objects can have properties.
4100 if (args[0]->IsJSObject()) {
4101 JSObject* object = JSObject::cast(args[0]);
4102 CONVERT_CHECKED(String, key, args[1]);
4103 if (object->HasProperty(key)) return Heap::true_value();
4104 }
4105 return Heap::false_value();
4106}
4107
4108
4109static Object* Runtime_HasElement(Arguments args) {
4110 NoHandleAllocation na;
4111 ASSERT(args.length() == 2);
4112
4113 // Only JS objects can have elements.
4114 if (args[0]->IsJSObject()) {
4115 JSObject* object = JSObject::cast(args[0]);
4116 CONVERT_CHECKED(Smi, index_obj, args[1]);
4117 uint32_t index = index_obj->value();
4118 if (object->HasElement(index)) return Heap::true_value();
4119 }
4120 return Heap::false_value();
4121}
4122
4123
4124static Object* Runtime_IsPropertyEnumerable(Arguments args) {
4125 NoHandleAllocation ha;
4126 ASSERT(args.length() == 2);
4127
4128 CONVERT_CHECKED(JSObject, object, args[0]);
4129 CONVERT_CHECKED(String, key, args[1]);
4130
4131 uint32_t index;
4132 if (key->AsArrayIndex(&index)) {
4133 return Heap::ToBoolean(object->HasElement(index));
4134 }
4135
ager@chromium.org870a0b62008-11-04 11:43:05 +00004136 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
4137 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004138}
4139
4140
4141static Object* Runtime_GetPropertyNames(Arguments args) {
4142 HandleScope scope;
4143 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004144 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004145 return *GetKeysFor(object);
4146}
4147
4148
4149// Returns either a FixedArray as Runtime_GetPropertyNames,
4150// or, if the given object has an enum cache that contains
4151// all enumerable properties of the object and its prototypes
4152// have none, the map of the object. This is used to speed up
4153// the check for deletions during a for-in.
4154static Object* Runtime_GetPropertyNamesFast(Arguments args) {
4155 ASSERT(args.length() == 1);
4156
4157 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4158
4159 if (raw_object->IsSimpleEnum()) return raw_object->map();
4160
4161 HandleScope scope;
4162 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004163 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
4164 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004165
4166 // Test again, since cache may have been built by preceding call.
4167 if (object->IsSimpleEnum()) return object->map();
4168
4169 return *content;
4170}
4171
4172
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004173// Find the length of the prototype chain that is to to handled as one. If a
4174// prototype object is hidden it is to be viewed as part of the the object it
4175// is prototype for.
4176static int LocalPrototypeChainLength(JSObject* obj) {
4177 int count = 1;
4178 Object* proto = obj->GetPrototype();
4179 while (proto->IsJSObject() &&
4180 JSObject::cast(proto)->map()->is_hidden_prototype()) {
4181 count++;
4182 proto = JSObject::cast(proto)->GetPrototype();
4183 }
4184 return count;
4185}
4186
4187
4188// Return the names of the local named properties.
4189// args[0]: object
4190static Object* Runtime_GetLocalPropertyNames(Arguments args) {
4191 HandleScope scope;
4192 ASSERT(args.length() == 1);
4193 if (!args[0]->IsJSObject()) {
4194 return Heap::undefined_value();
4195 }
4196 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4197
4198 // Skip the global proxy as it has no properties and always delegates to the
4199 // real global object.
4200 if (obj->IsJSGlobalProxy()) {
4201 // Only collect names if access is permitted.
4202 if (obj->IsAccessCheckNeeded() &&
4203 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
4204 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
4205 return *Factory::NewJSArray(0);
4206 }
4207 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
4208 }
4209
4210 // Find the number of objects making up this.
4211 int length = LocalPrototypeChainLength(*obj);
4212
4213 // Find the number of local properties for each of the objects.
4214 int* local_property_count = NewArray<int>(length);
4215 int total_property_count = 0;
4216 Handle<JSObject> jsproto = obj;
4217 for (int i = 0; i < length; i++) {
4218 // Only collect names if access is permitted.
4219 if (jsproto->IsAccessCheckNeeded() &&
4220 !Top::MayNamedAccess(*jsproto,
4221 Heap::undefined_value(),
4222 v8::ACCESS_KEYS)) {
4223 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
4224 return *Factory::NewJSArray(0);
4225 }
4226 int n;
4227 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4228 local_property_count[i] = n;
4229 total_property_count += n;
4230 if (i < length - 1) {
4231 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4232 }
4233 }
4234
4235 // Allocate an array with storage for all the property names.
4236 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
4237
4238 // Get the property names.
4239 jsproto = obj;
4240 int proto_with_hidden_properties = 0;
4241 for (int i = 0; i < length; i++) {
4242 jsproto->GetLocalPropertyNames(*names,
4243 i == 0 ? 0 : local_property_count[i - 1]);
4244 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
4245 proto_with_hidden_properties++;
4246 }
4247 if (i < length - 1) {
4248 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4249 }
4250 }
4251
4252 // Filter out name of hidden propeties object.
4253 if (proto_with_hidden_properties > 0) {
4254 Handle<FixedArray> old_names = names;
4255 names = Factory::NewFixedArray(
4256 names->length() - proto_with_hidden_properties);
4257 int dest_pos = 0;
4258 for (int i = 0; i < total_property_count; i++) {
4259 Object* name = old_names->get(i);
4260 if (name == Heap::hidden_symbol()) {
4261 continue;
4262 }
4263 names->set(dest_pos++, name);
4264 }
4265 }
4266
4267 DeleteArray(local_property_count);
4268 return *Factory::NewJSArrayWithElements(names);
4269}
4270
4271
4272// Return the names of the local indexed properties.
4273// args[0]: object
4274static Object* Runtime_GetLocalElementNames(Arguments args) {
4275 HandleScope scope;
4276 ASSERT(args.length() == 1);
4277 if (!args[0]->IsJSObject()) {
4278 return Heap::undefined_value();
4279 }
4280 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4281
4282 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4283 Handle<FixedArray> names = Factory::NewFixedArray(n);
4284 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4285 return *Factory::NewJSArrayWithElements(names);
4286}
4287
4288
4289// Return information on whether an object has a named or indexed interceptor.
4290// args[0]: object
4291static Object* Runtime_GetInterceptorInfo(Arguments args) {
4292 HandleScope scope;
4293 ASSERT(args.length() == 1);
4294 if (!args[0]->IsJSObject()) {
4295 return Smi::FromInt(0);
4296 }
4297 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4298
4299 int result = 0;
4300 if (obj->HasNamedInterceptor()) result |= 2;
4301 if (obj->HasIndexedInterceptor()) result |= 1;
4302
4303 return Smi::FromInt(result);
4304}
4305
4306
4307// Return property names from named interceptor.
4308// args[0]: object
4309static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
4310 HandleScope scope;
4311 ASSERT(args.length() == 1);
4312 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4313
4314 if (obj->HasNamedInterceptor()) {
4315 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4316 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4317 }
4318 return Heap::undefined_value();
4319}
4320
4321
4322// Return element names from indexed interceptor.
4323// args[0]: object
4324static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
4325 HandleScope scope;
4326 ASSERT(args.length() == 1);
4327 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4328
4329 if (obj->HasIndexedInterceptor()) {
4330 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4331 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4332 }
4333 return Heap::undefined_value();
4334}
4335
4336
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004337static Object* Runtime_LocalKeys(Arguments args) {
4338 ASSERT_EQ(args.length(), 1);
4339 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4340 HandleScope scope;
4341 Handle<JSObject> object(raw_object);
4342 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
4343 LOCAL_ONLY);
4344 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
4345 // property array and since the result is mutable we have to create
4346 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004347 int length = contents->length();
4348 Handle<FixedArray> copy = Factory::NewFixedArray(length);
4349 for (int i = 0; i < length; i++) {
4350 Object* entry = contents->get(i);
4351 if (entry->IsString()) {
4352 copy->set(i, entry);
4353 } else {
4354 ASSERT(entry->IsNumber());
4355 HandleScope scope;
4356 Handle<Object> entry_handle(entry);
4357 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
4358 copy->set(i, *entry_str);
4359 }
4360 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004361 return *Factory::NewJSArrayWithElements(copy);
4362}
4363
4364
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004365static Object* Runtime_GetArgumentsProperty(Arguments args) {
4366 NoHandleAllocation ha;
4367 ASSERT(args.length() == 1);
4368
4369 // Compute the frame holding the arguments.
4370 JavaScriptFrameIterator it;
4371 it.AdvanceToArgumentsFrame();
4372 JavaScriptFrame* frame = it.frame();
4373
4374 // Get the actual number of provided arguments.
4375 const uint32_t n = frame->GetProvidedParametersCount();
4376
4377 // Try to convert the key to an index. If successful and within
4378 // index return the the argument from the frame.
4379 uint32_t index;
4380 if (Array::IndexFromObject(args[0], &index) && index < n) {
4381 return frame->GetParameter(index);
4382 }
4383
4384 // Convert the key to a string.
4385 HandleScope scope;
4386 bool exception = false;
4387 Handle<Object> converted =
4388 Execution::ToString(args.at<Object>(0), &exception);
4389 if (exception) return Failure::Exception();
4390 Handle<String> key = Handle<String>::cast(converted);
4391
4392 // Try to convert the string key into an array index.
4393 if (key->AsArrayIndex(&index)) {
4394 if (index < n) {
4395 return frame->GetParameter(index);
4396 } else {
4397 return Top::initial_object_prototype()->GetElement(index);
4398 }
4399 }
4400
4401 // Handle special arguments properties.
4402 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
4403 if (key->Equals(Heap::callee_symbol())) return frame->function();
4404
4405 // Lookup in the initial Object.prototype object.
4406 return Top::initial_object_prototype()->GetProperty(*key);
4407}
4408
4409
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004410static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004411 HandleScope scope;
4412
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004413 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004414 Handle<Object> object = args.at<Object>(0);
4415 if (object->IsJSObject()) {
4416 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00004417 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
4418 js_object->TransformToFastProperties(0);
4419 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004420 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004421 return *object;
4422}
4423
4424
4425static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004426 HandleScope scope;
4427
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004428 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004429 Handle<Object> object = args.at<Object>(0);
4430 if (object->IsJSObject()) {
4431 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004432 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004433 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004434 return *object;
4435}
4436
4437
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004438static Object* Runtime_ToBool(Arguments args) {
4439 NoHandleAllocation ha;
4440 ASSERT(args.length() == 1);
4441
4442 return args[0]->ToBoolean();
4443}
4444
4445
4446// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
4447// Possible optimizations: put the type string into the oddballs.
4448static Object* Runtime_Typeof(Arguments args) {
4449 NoHandleAllocation ha;
4450
4451 Object* obj = args[0];
4452 if (obj->IsNumber()) return Heap::number_symbol();
4453 HeapObject* heap_obj = HeapObject::cast(obj);
4454
4455 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004456 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004457
4458 InstanceType instance_type = heap_obj->map()->instance_type();
4459 if (instance_type < FIRST_NONSTRING_TYPE) {
4460 return Heap::string_symbol();
4461 }
4462
4463 switch (instance_type) {
4464 case ODDBALL_TYPE:
4465 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
4466 return Heap::boolean_symbol();
4467 }
4468 if (heap_obj->IsNull()) {
4469 return Heap::object_symbol();
4470 }
4471 ASSERT(heap_obj->IsUndefined());
4472 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004473 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004474 return Heap::function_symbol();
4475 default:
4476 // For any kind of object not handled above, the spec rule for
4477 // host objects gives that it is okay to return "object"
4478 return Heap::object_symbol();
4479 }
4480}
4481
4482
lrn@chromium.org25156de2010-04-06 13:10:27 +00004483static bool AreDigits(const char*s, int from, int to) {
4484 for (int i = from; i < to; i++) {
4485 if (s[i] < '0' || s[i] > '9') return false;
4486 }
4487
4488 return true;
4489}
4490
4491
4492static int ParseDecimalInteger(const char*s, int from, int to) {
4493 ASSERT(to - from < 10); // Overflow is not possible.
4494 ASSERT(from < to);
4495 int d = s[from] - '0';
4496
4497 for (int i = from + 1; i < to; i++) {
4498 d = 10 * d + (s[i] - '0');
4499 }
4500
4501 return d;
4502}
4503
4504
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004505static Object* Runtime_StringToNumber(Arguments args) {
4506 NoHandleAllocation ha;
4507 ASSERT(args.length() == 1);
4508 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004509 subject->TryFlatten();
lrn@chromium.org25156de2010-04-06 13:10:27 +00004510
4511 // Fast case: short integer or some sorts of junk values.
4512 int len = subject->length();
4513 if (subject->IsSeqAsciiString()) {
4514 if (len == 0) return Smi::FromInt(0);
4515
4516 char const* data = SeqAsciiString::cast(subject)->GetChars();
4517 bool minus = (data[0] == '-');
4518 int start_pos = (minus ? 1 : 0);
4519
4520 if (start_pos == len) {
4521 return Heap::nan_value();
4522 } else if (data[start_pos] > '9') {
4523 // Fast check for a junk value. A valid string may start from a
4524 // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit or
4525 // the 'I' character ('Infinity'). All of that have codes not greater than
4526 // '9' except 'I'.
4527 if (data[start_pos] != 'I') {
4528 return Heap::nan_value();
4529 }
4530 } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
4531 // The maximal/minimal smi has 10 digits. If the string has less digits we
4532 // know it will fit into the smi-data type.
4533 int d = ParseDecimalInteger(data, start_pos, len);
4534 if (minus) {
4535 if (d == 0) return Heap::minus_zero_value();
4536 d = -d;
4537 }
4538 return Smi::FromInt(d);
4539 }
4540 }
4541
4542 // Slower case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004543 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
4544}
4545
4546
4547static Object* Runtime_StringFromCharCodeArray(Arguments args) {
4548 NoHandleAllocation ha;
4549 ASSERT(args.length() == 1);
4550
4551 CONVERT_CHECKED(JSArray, codes, args[0]);
4552 int length = Smi::cast(codes->length())->value();
4553
4554 // Check if the string can be ASCII.
4555 int i;
4556 for (i = 0; i < length; i++) {
4557 Object* element = codes->GetElement(i);
4558 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
4559 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
4560 break;
4561 }
4562
4563 Object* object = NULL;
4564 if (i == length) { // The string is ASCII.
4565 object = Heap::AllocateRawAsciiString(length);
4566 } else { // The string is not ASCII.
4567 object = Heap::AllocateRawTwoByteString(length);
4568 }
4569
4570 if (object->IsFailure()) return object;
4571 String* result = String::cast(object);
4572 for (int i = 0; i < length; i++) {
4573 Object* element = codes->GetElement(i);
4574 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004575 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004576 }
4577 return result;
4578}
4579
4580
4581// kNotEscaped is generated by the following:
4582//
4583// #!/bin/perl
4584// for (my $i = 0; $i < 256; $i++) {
4585// print "\n" if $i % 16 == 0;
4586// my $c = chr($i);
4587// my $escaped = 1;
4588// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
4589// print $escaped ? "0, " : "1, ";
4590// }
4591
4592
4593static bool IsNotEscaped(uint16_t character) {
4594 // Only for 8 bit characters, the rest are always escaped (in a different way)
4595 ASSERT(character < 256);
4596 static const char kNotEscaped[256] = {
4597 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4598 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4599 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
4600 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
4601 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4602 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
4603 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4604 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
4605 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4606 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4607 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4608 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4609 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4610 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4611 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4612 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4613 };
4614 return kNotEscaped[character] != 0;
4615}
4616
4617
4618static Object* Runtime_URIEscape(Arguments args) {
4619 const char hex_chars[] = "0123456789ABCDEF";
4620 NoHandleAllocation ha;
4621 ASSERT(args.length() == 1);
4622 CONVERT_CHECKED(String, source, args[0]);
4623
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004624 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004625
4626 int escaped_length = 0;
4627 int length = source->length();
4628 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004629 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004630 buffer->Reset(source);
4631 while (buffer->has_more()) {
4632 uint16_t character = buffer->GetNext();
4633 if (character >= 256) {
4634 escaped_length += 6;
4635 } else if (IsNotEscaped(character)) {
4636 escaped_length++;
4637 } else {
4638 escaped_length += 3;
4639 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004640 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004641 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004642 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004643 Top::context()->mark_out_of_memory();
4644 return Failure::OutOfMemoryException();
4645 }
4646 }
4647 }
4648 // No length change implies no change. Return original string if no change.
4649 if (escaped_length == length) {
4650 return source;
4651 }
4652 Object* o = Heap::AllocateRawAsciiString(escaped_length);
4653 if (o->IsFailure()) return o;
4654 String* destination = String::cast(o);
4655 int dest_position = 0;
4656
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004657 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004658 buffer->Rewind();
4659 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00004660 uint16_t chr = buffer->GetNext();
4661 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004662 destination->Set(dest_position, '%');
4663 destination->Set(dest_position+1, 'u');
4664 destination->Set(dest_position+2, hex_chars[chr >> 12]);
4665 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
4666 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
4667 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004668 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00004669 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004670 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004671 dest_position++;
4672 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004673 destination->Set(dest_position, '%');
4674 destination->Set(dest_position+1, hex_chars[chr >> 4]);
4675 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004676 dest_position += 3;
4677 }
4678 }
4679 return destination;
4680}
4681
4682
4683static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
4684 static const signed char kHexValue['g'] = {
4685 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4686 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4687 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4688 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
4689 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4690 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4691 -1, 10, 11, 12, 13, 14, 15 };
4692
4693 if (character1 > 'f') return -1;
4694 int hi = kHexValue[character1];
4695 if (hi == -1) return -1;
4696 if (character2 > 'f') return -1;
4697 int lo = kHexValue[character2];
4698 if (lo == -1) return -1;
4699 return (hi << 4) + lo;
4700}
4701
4702
ager@chromium.org870a0b62008-11-04 11:43:05 +00004703static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004704 int i,
4705 int length,
4706 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004707 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00004708 int32_t hi = 0;
4709 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004710 if (character == '%' &&
4711 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004712 source->Get(i + 1) == 'u' &&
4713 (hi = TwoDigitHex(source->Get(i + 2),
4714 source->Get(i + 3))) != -1 &&
4715 (lo = TwoDigitHex(source->Get(i + 4),
4716 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004717 *step = 6;
4718 return (hi << 8) + lo;
4719 } else if (character == '%' &&
4720 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004721 (lo = TwoDigitHex(source->Get(i + 1),
4722 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004723 *step = 3;
4724 return lo;
4725 } else {
4726 *step = 1;
4727 return character;
4728 }
4729}
4730
4731
4732static Object* Runtime_URIUnescape(Arguments args) {
4733 NoHandleAllocation ha;
4734 ASSERT(args.length() == 1);
4735 CONVERT_CHECKED(String, source, args[0]);
4736
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004737 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004738
4739 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004740 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004741
4742 int unescaped_length = 0;
4743 for (int i = 0; i < length; unescaped_length++) {
4744 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004745 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004746 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004747 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004748 i += step;
4749 }
4750
4751 // No length change implies no change. Return original string if no change.
4752 if (unescaped_length == length)
4753 return source;
4754
4755 Object* o = ascii ?
4756 Heap::AllocateRawAsciiString(unescaped_length) :
4757 Heap::AllocateRawTwoByteString(unescaped_length);
4758 if (o->IsFailure()) return o;
4759 String* destination = String::cast(o);
4760
4761 int dest_position = 0;
4762 for (int i = 0; i < length; dest_position++) {
4763 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004764 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004765 i += step;
4766 }
4767 return destination;
4768}
4769
4770
4771static Object* Runtime_StringParseInt(Arguments args) {
4772 NoHandleAllocation ha;
4773
4774 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004775 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004776
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004777 s->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004778
lrn@chromium.org25156de2010-04-06 13:10:27 +00004779 RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
4780 double value = StringToInt(s, radix);
4781 return Heap::NumberFromDouble(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004782 return Heap::nan_value();
4783}
4784
4785
4786static Object* Runtime_StringParseFloat(Arguments args) {
4787 NoHandleAllocation ha;
4788 CONVERT_CHECKED(String, str, args[0]);
4789
4790 // ECMA-262 section 15.1.2.3, empty string is NaN
4791 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
4792
4793 // Create a number object from the value.
4794 return Heap::NumberFromDouble(value);
4795}
4796
4797
4798static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
4799static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
4800
4801
4802template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004803static Object* ConvertCaseHelper(String* s,
4804 int length,
4805 int input_string_length,
4806 unibrow::Mapping<Converter, 128>* mapping) {
4807 // We try this twice, once with the assumption that the result is no longer
4808 // than the input and, if that assumption breaks, again with the exact
4809 // length. This may not be pretty, but it is nicer than what was here before
4810 // and I hereby claim my vaffel-is.
4811 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004812 // Allocate the resulting string.
4813 //
4814 // NOTE: This assumes that the upper/lower case of an ascii
4815 // character is also ascii. This is currently the case, but it
4816 // might break in the future if we implement more context and locale
4817 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00004818 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004819 ? Heap::AllocateRawAsciiString(length)
4820 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004821 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004822 String* result = String::cast(o);
4823 bool has_changed_character = false;
4824
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004825 // Convert all characters to upper case, assuming that they will fit
4826 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004827 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004828 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004829 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004830 // We can assume that the string is not empty
4831 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004832 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00004833 bool has_next = buffer->has_more();
4834 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004835 int char_length = mapping->get(current, next, chars);
4836 if (char_length == 0) {
4837 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004838 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004839 i++;
4840 } else if (char_length == 1) {
4841 // Common case: converting the letter resulted in one character.
4842 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004843 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004844 has_changed_character = true;
4845 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004846 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004847 // We've assumed that the result would be as long as the
4848 // input but here is a character that converts to several
4849 // characters. No matter, we calculate the exact length
4850 // of the result and try the whole thing again.
4851 //
4852 // Note that this leaves room for optimization. We could just
4853 // memcpy what we already have to the result string. Also,
4854 // the result string is the last object allocated we could
4855 // "realloc" it and probably, in the vast majority of cases,
4856 // extend the existing string to be able to hold the full
4857 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00004858 int next_length = 0;
4859 if (has_next) {
4860 next_length = mapping->get(next, 0, chars);
4861 if (next_length == 0) next_length = 1;
4862 }
4863 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004864 while (buffer->has_more()) {
4865 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00004866 // NOTE: we use 0 as the next character here because, while
4867 // the next character may affect what a character converts to,
4868 // it does not in any case affect the length of what it convert
4869 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004870 int char_length = mapping->get(current, 0, chars);
4871 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00004872 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004873 if (current_length > Smi::kMaxValue) {
4874 Top::context()->mark_out_of_memory();
4875 return Failure::OutOfMemoryException();
4876 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004877 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004878 // Try again with the real length.
4879 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004880 } else {
4881 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004882 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004883 i++;
4884 }
4885 has_changed_character = true;
4886 }
4887 current = next;
4888 }
4889 if (has_changed_character) {
4890 return result;
4891 } else {
4892 // If we didn't actually change anything in doing the conversion
4893 // we simple return the result and let the converted string
4894 // become garbage; there is no reason to keep two identical strings
4895 // alive.
4896 return s;
4897 }
4898}
4899
4900
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004901static inline SeqAsciiString* TryGetSeqAsciiString(String* s) {
4902 if (!s->IsFlat() || !s->IsAsciiRepresentation()) return NULL;
4903 if (s->IsConsString()) {
4904 ASSERT(ConsString::cast(s)->second()->length() == 0);
4905 return SeqAsciiString::cast(ConsString::cast(s)->first());
4906 }
4907 return SeqAsciiString::cast(s);
4908}
4909
4910
4911namespace {
4912
4913struct ToLowerTraits {
4914 typedef unibrow::ToLowercase UnibrowConverter;
4915
4916 static bool ConvertAscii(char* dst, char* src, int length) {
4917 bool changed = false;
4918 for (int i = 0; i < length; ++i) {
4919 char c = src[i];
4920 if ('A' <= c && c <= 'Z') {
4921 c += ('a' - 'A');
4922 changed = true;
4923 }
4924 dst[i] = c;
4925 }
4926 return changed;
4927 }
4928};
4929
4930
4931struct ToUpperTraits {
4932 typedef unibrow::ToUppercase UnibrowConverter;
4933
4934 static bool ConvertAscii(char* dst, char* src, int length) {
4935 bool changed = false;
4936 for (int i = 0; i < length; ++i) {
4937 char c = src[i];
4938 if ('a' <= c && c <= 'z') {
4939 c -= ('a' - 'A');
4940 changed = true;
4941 }
4942 dst[i] = c;
4943 }
4944 return changed;
4945 }
4946};
4947
4948} // namespace
4949
4950
4951template <typename ConvertTraits>
4952static Object* ConvertCase(
4953 Arguments args,
4954 unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004955 NoHandleAllocation ha;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004956 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004957 s->TryFlatten();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004958
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004959 const int length = s->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004960 // Assume that the string is not empty; we need this assumption later
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004961 if (length == 0) return s;
4962
4963 // Simpler handling of ascii strings.
4964 //
4965 // NOTE: This assumes that the upper/lower case of an ascii
4966 // character is also ascii. This is currently the case, but it
4967 // might break in the future if we implement more context and locale
4968 // dependent upper/lower conversions.
4969 SeqAsciiString* seq_ascii = TryGetSeqAsciiString(s);
4970 if (seq_ascii != NULL) {
4971 Object* o = Heap::AllocateRawAsciiString(length);
4972 if (o->IsFailure()) return o;
4973 SeqAsciiString* result = SeqAsciiString::cast(o);
4974 bool has_changed_character = ConvertTraits::ConvertAscii(
4975 result->GetChars(), seq_ascii->GetChars(), length);
4976 return has_changed_character ? result : s;
4977 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004978
4979 Object* answer = ConvertCaseHelper(s, length, length, mapping);
4980 if (answer->IsSmi()) {
4981 // Retry with correct length.
4982 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
4983 }
4984 return answer; // This may be a failure.
4985}
4986
4987
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004988static Object* Runtime_StringToLowerCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004989 return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004990}
4991
4992
4993static Object* Runtime_StringToUpperCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004994 return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004995}
4996
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004997
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004998static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
4999 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
5000}
5001
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005002
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005003static Object* Runtime_StringTrim(Arguments args) {
5004 NoHandleAllocation ha;
5005 ASSERT(args.length() == 3);
5006
5007 CONVERT_CHECKED(String, s, args[0]);
5008 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
5009 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
5010
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005011 s->TryFlatten();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005012 int length = s->length();
5013
5014 int left = 0;
5015 if (trimLeft) {
5016 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
5017 left++;
5018 }
5019 }
5020
5021 int right = length;
5022 if (trimRight) {
5023 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
5024 right--;
5025 }
5026 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005027 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005028}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005029
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005030
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005031template <typename schar, typename pchar>
5032void FindStringIndices(Vector<const schar> subject,
5033 Vector<const pchar> pattern,
5034 ZoneList<int>* indices,
5035 unsigned int limit) {
5036 ASSERT(limit > 0);
5037 // Collect indices of pattern in subject, and the end-of-string index.
5038 // Stop after finding at most limit values.
5039 StringSearchStrategy strategy =
5040 InitializeStringSearch(pattern, sizeof(schar) == 1);
5041 switch (strategy) {
5042 case SEARCH_FAIL: return;
5043 case SEARCH_SHORT: {
5044 int pattern_length = pattern.length();
5045 int index = 0;
5046 while (limit > 0) {
5047 index = SimpleIndexOf(subject, pattern, index);
5048 if (index < 0) return;
5049 indices->Add(index);
5050 index += pattern_length;
5051 limit--;
5052 }
5053 return;
5054 }
5055 case SEARCH_LONG: {
5056 int pattern_length = pattern.length();
5057 int index = 0;
5058 while (limit > 0) {
5059 index = ComplexIndexOf(subject, pattern, index);
5060 if (index < 0) return;
5061 indices->Add(index);
5062 index += pattern_length;
5063 limit--;
5064 }
5065 return;
5066 }
5067 default:
5068 UNREACHABLE();
5069 return;
5070 }
5071}
5072
5073template <typename schar>
5074inline void FindCharIndices(Vector<const schar> subject,
5075 const schar pattern_char,
5076 ZoneList<int>* indices,
5077 unsigned int limit) {
5078 // Collect indices of pattern_char in subject, and the end-of-string index.
5079 // Stop after finding at most limit values.
5080 int index = 0;
5081 while (limit > 0) {
5082 index = SingleCharIndexOf(subject, pattern_char, index);
5083 if (index < 0) return;
5084 indices->Add(index);
5085 index++;
5086 limit--;
5087 }
5088}
5089
5090
5091static Object* Runtime_StringSplit(Arguments args) {
5092 ASSERT(args.length() == 3);
5093 HandleScope handle_scope;
5094 CONVERT_ARG_CHECKED(String, subject, 0);
5095 CONVERT_ARG_CHECKED(String, pattern, 1);
5096 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
5097
5098 int subject_length = subject->length();
5099 int pattern_length = pattern->length();
5100 RUNTIME_ASSERT(pattern_length > 0);
5101
5102 // The limit can be very large (0xffffffffu), but since the pattern
5103 // isn't empty, we can never create more parts than ~half the length
5104 // of the subject.
5105
5106 if (!subject->IsFlat()) FlattenString(subject);
5107
5108 static const int kMaxInitialListCapacity = 16;
5109
5110 ZoneScope scope(DELETE_ON_EXIT);
5111
5112 // Find (up to limit) indices of separator and end-of-string in subject
5113 int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
5114 ZoneList<int> indices(initial_capacity);
5115 if (pattern_length == 1) {
5116 // Special case, go directly to fast single-character split.
5117 AssertNoAllocation nogc;
5118 uc16 pattern_char = pattern->Get(0);
5119 if (subject->IsTwoByteRepresentation()) {
5120 FindCharIndices(subject->ToUC16Vector(), pattern_char,
5121 &indices,
5122 limit);
5123 } else if (pattern_char <= String::kMaxAsciiCharCode) {
5124 FindCharIndices(subject->ToAsciiVector(),
5125 static_cast<char>(pattern_char),
5126 &indices,
5127 limit);
5128 }
5129 } else {
5130 if (!pattern->IsFlat()) FlattenString(pattern);
5131 AssertNoAllocation nogc;
5132 if (subject->IsAsciiRepresentation()) {
5133 Vector<const char> subject_vector = subject->ToAsciiVector();
5134 if (pattern->IsAsciiRepresentation()) {
5135 FindStringIndices(subject_vector,
5136 pattern->ToAsciiVector(),
5137 &indices,
5138 limit);
5139 } else {
5140 FindStringIndices(subject_vector,
5141 pattern->ToUC16Vector(),
5142 &indices,
5143 limit);
5144 }
5145 } else {
5146 Vector<const uc16> subject_vector = subject->ToUC16Vector();
5147 if (pattern->IsAsciiRepresentation()) {
5148 FindStringIndices(subject_vector,
5149 pattern->ToAsciiVector(),
5150 &indices,
5151 limit);
5152 } else {
5153 FindStringIndices(subject_vector,
5154 pattern->ToUC16Vector(),
5155 &indices,
5156 limit);
5157 }
5158 }
5159 }
5160 if (static_cast<uint32_t>(indices.length()) < limit) {
5161 indices.Add(subject_length);
5162 }
5163 // The list indices now contains the end of each part to create.
5164
5165
5166 // Create JSArray of substrings separated by separator.
5167 int part_count = indices.length();
5168
5169 Handle<JSArray> result = Factory::NewJSArray(part_count);
5170 result->set_length(Smi::FromInt(part_count));
5171
5172 ASSERT(result->HasFastElements());
5173
5174 if (part_count == 1 && indices.at(0) == subject_length) {
5175 FixedArray::cast(result->elements())->set(0, *subject);
5176 return *result;
5177 }
5178
5179 Handle<FixedArray> elements(FixedArray::cast(result->elements()));
5180 int part_start = 0;
5181 for (int i = 0; i < part_count; i++) {
5182 HandleScope local_loop_handle;
5183 int part_end = indices.at(i);
5184 Handle<String> substring =
5185 Factory::NewSubString(subject, part_start, part_end);
5186 elements->set(i, *substring);
5187 part_start = part_end + pattern_length;
5188 }
5189
5190 return *result;
5191}
5192
5193
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005194// Copies ascii characters to the given fixed array looking up
5195// one-char strings in the cache. Gives up on the first char that is
5196// not in the cache and fills the remainder with smi zeros. Returns
5197// the length of the successfully copied prefix.
5198static int CopyCachedAsciiCharsToArray(const char* chars,
5199 FixedArray* elements,
5200 int length) {
5201 AssertNoAllocation nogc;
5202 FixedArray* ascii_cache = Heap::single_character_string_cache();
5203 Object* undefined = Heap::undefined_value();
5204 int i;
5205 for (i = 0; i < length; ++i) {
5206 Object* value = ascii_cache->get(chars[i]);
5207 if (value == undefined) break;
5208 ASSERT(!Heap::InNewSpace(value));
5209 elements->set(i, value, SKIP_WRITE_BARRIER);
5210 }
5211 if (i < length) {
5212 ASSERT(Smi::FromInt(0) == 0);
5213 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
5214 }
5215#ifdef DEBUG
5216 for (int j = 0; j < length; ++j) {
5217 Object* element = elements->get(j);
5218 ASSERT(element == Smi::FromInt(0) ||
5219 (element->IsString() && String::cast(element)->LooksValid()));
5220 }
5221#endif
5222 return i;
5223}
5224
5225
5226// Converts a String to JSArray.
5227// For example, "foo" => ["f", "o", "o"].
5228static Object* Runtime_StringToArray(Arguments args) {
5229 HandleScope scope;
5230 ASSERT(args.length() == 1);
5231 CONVERT_ARG_CHECKED(String, s, 0);
5232
5233 s->TryFlatten();
5234 const int length = s->length();
5235
5236 Handle<FixedArray> elements;
5237 if (s->IsFlat() && s->IsAsciiRepresentation()) {
5238 Object* obj = Heap::AllocateUninitializedFixedArray(length);
5239 if (obj->IsFailure()) return obj;
5240 elements = Handle<FixedArray>(FixedArray::cast(obj));
5241
5242 Vector<const char> chars = s->ToAsciiVector();
5243 // Note, this will initialize all elements (not only the prefix)
5244 // to prevent GC from seeing partially initialized array.
5245 int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
5246 *elements,
5247 length);
5248
5249 for (int i = num_copied_from_cache; i < length; ++i) {
5250 elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
5251 }
5252 } else {
5253 elements = Factory::NewFixedArray(length);
5254 for (int i = 0; i < length; ++i) {
5255 elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
5256 }
5257 }
5258
5259#ifdef DEBUG
5260 for (int i = 0; i < length; ++i) {
5261 ASSERT(String::cast(elements->get(i))->length() == 1);
5262 }
5263#endif
5264
5265 return *Factory::NewJSArrayWithElements(elements);
5266}
5267
5268
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00005269bool Runtime::IsUpperCaseChar(uint16_t ch) {
5270 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
5271 int char_length = to_upper_mapping.get(ch, 0, chars);
5272 return char_length == 0;
5273}
5274
5275
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005276static Object* Runtime_NumberToString(Arguments args) {
5277 NoHandleAllocation ha;
5278 ASSERT(args.length() == 1);
5279
5280 Object* number = args[0];
5281 RUNTIME_ASSERT(number->IsNumber());
5282
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005283 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005284}
5285
5286
ager@chromium.org357bf652010-04-12 11:30:10 +00005287static Object* Runtime_NumberToStringSkipCache(Arguments args) {
5288 NoHandleAllocation ha;
5289 ASSERT(args.length() == 1);
5290
5291 Object* number = args[0];
5292 RUNTIME_ASSERT(number->IsNumber());
5293
5294 return Heap::NumberToString(number, false);
5295}
5296
5297
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005298static Object* Runtime_NumberToInteger(Arguments args) {
5299 NoHandleAllocation ha;
5300 ASSERT(args.length() == 1);
5301
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005302 CONVERT_DOUBLE_CHECKED(number, args[0]);
5303
5304 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5305 if (number > 0 && number <= Smi::kMaxValue) {
5306 return Smi::FromInt(static_cast<int>(number));
5307 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005308 return Heap::NumberFromDouble(DoubleToInteger(number));
5309}
5310
5311
5312static Object* Runtime_NumberToJSUint32(Arguments args) {
5313 NoHandleAllocation ha;
5314 ASSERT(args.length() == 1);
5315
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005316 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005317 return Heap::NumberFromUint32(number);
5318}
5319
5320
5321static Object* Runtime_NumberToJSInt32(Arguments args) {
5322 NoHandleAllocation ha;
5323 ASSERT(args.length() == 1);
5324
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005325 CONVERT_DOUBLE_CHECKED(number, args[0]);
5326
5327 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5328 if (number > 0 && number <= Smi::kMaxValue) {
5329 return Smi::FromInt(static_cast<int>(number));
5330 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005331 return Heap::NumberFromInt32(DoubleToInt32(number));
5332}
5333
5334
ager@chromium.org870a0b62008-11-04 11:43:05 +00005335// Converts a Number to a Smi, if possible. Returns NaN if the number is not
5336// a small integer.
5337static Object* Runtime_NumberToSmi(Arguments args) {
5338 NoHandleAllocation ha;
5339 ASSERT(args.length() == 1);
5340
5341 Object* obj = args[0];
5342 if (obj->IsSmi()) {
5343 return obj;
5344 }
5345 if (obj->IsHeapNumber()) {
5346 double value = HeapNumber::cast(obj)->value();
5347 int int_value = FastD2I(value);
5348 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
5349 return Smi::FromInt(int_value);
5350 }
5351 }
5352 return Heap::nan_value();
5353}
5354
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005355
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005356static Object* Runtime_NumberAdd(Arguments args) {
5357 NoHandleAllocation ha;
5358 ASSERT(args.length() == 2);
5359
5360 CONVERT_DOUBLE_CHECKED(x, args[0]);
5361 CONVERT_DOUBLE_CHECKED(y, args[1]);
5362 return Heap::AllocateHeapNumber(x + y);
5363}
5364
5365
5366static Object* Runtime_NumberSub(Arguments args) {
5367 NoHandleAllocation ha;
5368 ASSERT(args.length() == 2);
5369
5370 CONVERT_DOUBLE_CHECKED(x, args[0]);
5371 CONVERT_DOUBLE_CHECKED(y, args[1]);
5372 return Heap::AllocateHeapNumber(x - y);
5373}
5374
5375
5376static Object* Runtime_NumberMul(Arguments args) {
5377 NoHandleAllocation ha;
5378 ASSERT(args.length() == 2);
5379
5380 CONVERT_DOUBLE_CHECKED(x, args[0]);
5381 CONVERT_DOUBLE_CHECKED(y, args[1]);
5382 return Heap::AllocateHeapNumber(x * y);
5383}
5384
5385
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005386static Object* Runtime_NumberUnaryMinus(Arguments args) {
5387 NoHandleAllocation ha;
5388 ASSERT(args.length() == 1);
5389
5390 CONVERT_DOUBLE_CHECKED(x, args[0]);
5391 return Heap::AllocateHeapNumber(-x);
5392}
5393
5394
5395static Object* Runtime_NumberDiv(Arguments args) {
5396 NoHandleAllocation ha;
5397 ASSERT(args.length() == 2);
5398
5399 CONVERT_DOUBLE_CHECKED(x, args[0]);
5400 CONVERT_DOUBLE_CHECKED(y, args[1]);
5401 return Heap::NewNumberFromDouble(x / y);
5402}
5403
5404
5405static Object* Runtime_NumberMod(Arguments args) {
5406 NoHandleAllocation ha;
5407 ASSERT(args.length() == 2);
5408
5409 CONVERT_DOUBLE_CHECKED(x, args[0]);
5410 CONVERT_DOUBLE_CHECKED(y, args[1]);
5411
ager@chromium.org3811b432009-10-28 14:53:37 +00005412 x = modulo(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005413 // NewNumberFromDouble may return a Smi instead of a Number object
5414 return Heap::NewNumberFromDouble(x);
5415}
5416
5417
5418static Object* Runtime_StringAdd(Arguments args) {
5419 NoHandleAllocation ha;
5420 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005421 CONVERT_CHECKED(String, str1, args[0]);
5422 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005423 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00005424 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005425}
5426
5427
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005428template <typename sinkchar>
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005429static inline void StringBuilderConcatHelper(String* special,
5430 sinkchar* sink,
5431 FixedArray* fixed_array,
5432 int array_length) {
5433 int position = 0;
5434 for (int i = 0; i < array_length; i++) {
5435 Object* element = fixed_array->get(i);
5436 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005437 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005438 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005439 int pos;
5440 int len;
5441 if (encoded_slice > 0) {
5442 // Position and length encoded in one smi.
5443 pos = StringBuilderSubstringPosition::decode(encoded_slice);
5444 len = StringBuilderSubstringLength::decode(encoded_slice);
5445 } else {
5446 // Position and length encoded in two smis.
5447 Object* obj = fixed_array->get(++i);
5448 ASSERT(obj->IsSmi());
5449 pos = Smi::cast(obj)->value();
5450 len = -encoded_slice;
5451 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00005452 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00005453 sink + position,
5454 pos,
5455 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005456 position += len;
5457 } else {
5458 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005459 int element_length = string->length();
5460 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005461 position += element_length;
5462 }
5463 }
5464}
5465
5466
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005467static Object* Runtime_StringBuilderConcat(Arguments args) {
5468 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005469 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005470 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005471 if (!args[1]->IsSmi()) {
5472 Top::context()->mark_out_of_memory();
5473 return Failure::OutOfMemoryException();
5474 }
5475 int array_length = Smi::cast(args[1])->value();
5476 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005477
5478 // This assumption is used by the slice encoding in one or two smis.
5479 ASSERT(Smi::kMaxValue >= String::kMaxLength);
5480
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005481 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005482 if (!array->HasFastElements()) {
5483 return Top::Throw(Heap::illegal_argument_symbol());
5484 }
5485 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005486 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005487 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005488 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005489
5490 if (array_length == 0) {
5491 return Heap::empty_string();
5492 } else if (array_length == 1) {
5493 Object* first = fixed_array->get(0);
5494 if (first->IsString()) return first;
5495 }
5496
ager@chromium.org5ec48922009-05-05 07:25:34 +00005497 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005498 int position = 0;
5499 for (int i = 0; i < array_length; i++) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005500 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005501 Object* elt = fixed_array->get(i);
5502 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005503 // Smi encoding of position and length.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005504 int smi_value = Smi::cast(elt)->value();
5505 int pos;
5506 int len;
5507 if (smi_value > 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005508 // Position and length encoded in one smi.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005509 pos = StringBuilderSubstringPosition::decode(smi_value);
5510 len = StringBuilderSubstringLength::decode(smi_value);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005511 } else {
5512 // Position and length encoded in two smis.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005513 len = -smi_value;
5514 // Get the position and check that it is a positive smi.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005515 i++;
5516 if (i >= array_length) {
5517 return Top::Throw(Heap::illegal_argument_symbol());
5518 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005519 Object* next_smi = fixed_array->get(i);
5520 if (!next_smi->IsSmi()) {
5521 return Top::Throw(Heap::illegal_argument_symbol());
5522 }
5523 pos = Smi::cast(next_smi)->value();
5524 if (pos < 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005525 return Top::Throw(Heap::illegal_argument_symbol());
5526 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005527 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005528 ASSERT(pos >= 0);
5529 ASSERT(len >= 0);
5530 if (pos > special_length || len > special_length - pos) {
5531 return Top::Throw(Heap::illegal_argument_symbol());
5532 }
5533 increment = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005534 } else if (elt->IsString()) {
5535 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005536 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005537 increment = element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005538 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005539 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005540 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005541 } else {
5542 return Top::Throw(Heap::illegal_argument_symbol());
5543 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005544 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005545 Top::context()->mark_out_of_memory();
5546 return Failure::OutOfMemoryException();
5547 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005548 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005549 }
5550
5551 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005552 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005553
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005554 if (ascii) {
5555 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005556 if (object->IsFailure()) return object;
5557 SeqAsciiString* answer = SeqAsciiString::cast(object);
5558 StringBuilderConcatHelper(special,
5559 answer->GetChars(),
5560 fixed_array,
5561 array_length);
5562 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005563 } else {
5564 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005565 if (object->IsFailure()) return object;
5566 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
5567 StringBuilderConcatHelper(special,
5568 answer->GetChars(),
5569 fixed_array,
5570 array_length);
5571 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005572 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005573}
5574
5575
5576static Object* Runtime_NumberOr(Arguments args) {
5577 NoHandleAllocation ha;
5578 ASSERT(args.length() == 2);
5579
5580 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5581 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5582 return Heap::NumberFromInt32(x | y);
5583}
5584
5585
5586static Object* Runtime_NumberAnd(Arguments args) {
5587 NoHandleAllocation ha;
5588 ASSERT(args.length() == 2);
5589
5590 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5591 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5592 return Heap::NumberFromInt32(x & y);
5593}
5594
5595
5596static Object* Runtime_NumberXor(Arguments args) {
5597 NoHandleAllocation ha;
5598 ASSERT(args.length() == 2);
5599
5600 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5601 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5602 return Heap::NumberFromInt32(x ^ y);
5603}
5604
5605
5606static Object* Runtime_NumberNot(Arguments args) {
5607 NoHandleAllocation ha;
5608 ASSERT(args.length() == 1);
5609
5610 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5611 return Heap::NumberFromInt32(~x);
5612}
5613
5614
5615static Object* Runtime_NumberShl(Arguments args) {
5616 NoHandleAllocation ha;
5617 ASSERT(args.length() == 2);
5618
5619 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5620 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5621 return Heap::NumberFromInt32(x << (y & 0x1f));
5622}
5623
5624
5625static Object* Runtime_NumberShr(Arguments args) {
5626 NoHandleAllocation ha;
5627 ASSERT(args.length() == 2);
5628
5629 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
5630 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5631 return Heap::NumberFromUint32(x >> (y & 0x1f));
5632}
5633
5634
5635static Object* Runtime_NumberSar(Arguments args) {
5636 NoHandleAllocation ha;
5637 ASSERT(args.length() == 2);
5638
5639 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5640 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5641 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
5642}
5643
5644
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005645static Object* Runtime_NumberEquals(Arguments args) {
5646 NoHandleAllocation ha;
5647 ASSERT(args.length() == 2);
5648
5649 CONVERT_DOUBLE_CHECKED(x, args[0]);
5650 CONVERT_DOUBLE_CHECKED(y, args[1]);
5651 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
5652 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
5653 if (x == y) return Smi::FromInt(EQUAL);
5654 Object* result;
5655 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
5656 result = Smi::FromInt(EQUAL);
5657 } else {
5658 result = Smi::FromInt(NOT_EQUAL);
5659 }
5660 return result;
5661}
5662
5663
5664static Object* Runtime_StringEquals(Arguments args) {
5665 NoHandleAllocation ha;
5666 ASSERT(args.length() == 2);
5667
5668 CONVERT_CHECKED(String, x, args[0]);
5669 CONVERT_CHECKED(String, y, args[1]);
5670
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005671 bool not_equal = !x->Equals(y);
5672 // This is slightly convoluted because the value that signifies
5673 // equality is 0 and inequality is 1 so we have to negate the result
5674 // from String::Equals.
5675 ASSERT(not_equal == 0 || not_equal == 1);
5676 STATIC_CHECK(EQUAL == 0);
5677 STATIC_CHECK(NOT_EQUAL == 1);
5678 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005679}
5680
5681
5682static Object* Runtime_NumberCompare(Arguments args) {
5683 NoHandleAllocation ha;
5684 ASSERT(args.length() == 3);
5685
5686 CONVERT_DOUBLE_CHECKED(x, args[0]);
5687 CONVERT_DOUBLE_CHECKED(y, args[1]);
5688 if (isnan(x) || isnan(y)) return args[2];
5689 if (x == y) return Smi::FromInt(EQUAL);
5690 if (isless(x, y)) return Smi::FromInt(LESS);
5691 return Smi::FromInt(GREATER);
5692}
5693
5694
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005695// Compare two Smis as if they were converted to strings and then
5696// compared lexicographically.
5697static Object* Runtime_SmiLexicographicCompare(Arguments args) {
5698 NoHandleAllocation ha;
5699 ASSERT(args.length() == 2);
5700
5701 // Arrays for the individual characters of the two Smis. Smis are
5702 // 31 bit integers and 10 decimal digits are therefore enough.
5703 static int x_elms[10];
5704 static int y_elms[10];
5705
5706 // Extract the integer values from the Smis.
5707 CONVERT_CHECKED(Smi, x, args[0]);
5708 CONVERT_CHECKED(Smi, y, args[1]);
5709 int x_value = x->value();
5710 int y_value = y->value();
5711
5712 // If the integers are equal so are the string representations.
5713 if (x_value == y_value) return Smi::FromInt(EQUAL);
5714
5715 // If one of the integers are zero the normal integer order is the
5716 // same as the lexicographic order of the string representations.
5717 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
5718
ager@chromium.org32912102009-01-16 10:38:43 +00005719 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005720 // smallest because the char code of '-' is less than the char code
5721 // of any digit. Otherwise, we make both values positive.
5722 if (x_value < 0 || y_value < 0) {
5723 if (y_value >= 0) return Smi::FromInt(LESS);
5724 if (x_value >= 0) return Smi::FromInt(GREATER);
5725 x_value = -x_value;
5726 y_value = -y_value;
5727 }
5728
5729 // Convert the integers to arrays of their decimal digits.
5730 int x_index = 0;
5731 int y_index = 0;
5732 while (x_value > 0) {
5733 x_elms[x_index++] = x_value % 10;
5734 x_value /= 10;
5735 }
5736 while (y_value > 0) {
5737 y_elms[y_index++] = y_value % 10;
5738 y_value /= 10;
5739 }
5740
5741 // Loop through the arrays of decimal digits finding the first place
5742 // where they differ.
5743 while (--x_index >= 0 && --y_index >= 0) {
5744 int diff = x_elms[x_index] - y_elms[y_index];
5745 if (diff != 0) return Smi::FromInt(diff);
5746 }
5747
5748 // If one array is a suffix of the other array, the longest array is
5749 // the representation of the largest of the Smis in the
5750 // lexicographic ordering.
5751 return Smi::FromInt(x_index - y_index);
5752}
5753
5754
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005755static Object* StringInputBufferCompare(String* x, String* y) {
5756 static StringInputBuffer bufx;
5757 static StringInputBuffer bufy;
5758 bufx.Reset(x);
5759 bufy.Reset(y);
5760 while (bufx.has_more() && bufy.has_more()) {
5761 int d = bufx.GetNext() - bufy.GetNext();
5762 if (d < 0) return Smi::FromInt(LESS);
5763 else if (d > 0) return Smi::FromInt(GREATER);
5764 }
5765
5766 // x is (non-trivial) prefix of y:
5767 if (bufy.has_more()) return Smi::FromInt(LESS);
5768 // y is prefix of x:
5769 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
5770}
5771
5772
5773static Object* FlatStringCompare(String* x, String* y) {
5774 ASSERT(x->IsFlat());
5775 ASSERT(y->IsFlat());
5776 Object* equal_prefix_result = Smi::FromInt(EQUAL);
5777 int prefix_length = x->length();
5778 if (y->length() < prefix_length) {
5779 prefix_length = y->length();
5780 equal_prefix_result = Smi::FromInt(GREATER);
5781 } else if (y->length() > prefix_length) {
5782 equal_prefix_result = Smi::FromInt(LESS);
5783 }
5784 int r;
5785 if (x->IsAsciiRepresentation()) {
5786 Vector<const char> x_chars = x->ToAsciiVector();
5787 if (y->IsAsciiRepresentation()) {
5788 Vector<const char> y_chars = y->ToAsciiVector();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005789 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005790 } else {
5791 Vector<const uc16> y_chars = y->ToUC16Vector();
5792 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5793 }
5794 } else {
5795 Vector<const uc16> x_chars = x->ToUC16Vector();
5796 if (y->IsAsciiRepresentation()) {
5797 Vector<const char> y_chars = y->ToAsciiVector();
5798 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5799 } else {
5800 Vector<const uc16> y_chars = y->ToUC16Vector();
5801 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5802 }
5803 }
5804 Object* result;
5805 if (r == 0) {
5806 result = equal_prefix_result;
5807 } else {
5808 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
5809 }
5810 ASSERT(result == StringInputBufferCompare(x, y));
5811 return result;
5812}
5813
5814
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005815static Object* Runtime_StringCompare(Arguments args) {
5816 NoHandleAllocation ha;
5817 ASSERT(args.length() == 2);
5818
5819 CONVERT_CHECKED(String, x, args[0]);
5820 CONVERT_CHECKED(String, y, args[1]);
5821
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005822 Counters::string_compare_runtime.Increment();
5823
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005824 // A few fast case tests before we flatten.
5825 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005826 if (y->length() == 0) {
5827 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005828 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005829 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005830 return Smi::FromInt(LESS);
5831 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005832
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005833 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005834 if (d < 0) return Smi::FromInt(LESS);
5835 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005836
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005837 Object* obj = Heap::PrepareForCompare(x);
5838 if (obj->IsFailure()) return obj;
5839 obj = Heap::PrepareForCompare(y);
5840 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005841
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005842 return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
5843 : StringInputBufferCompare(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005844}
5845
5846
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005847static Object* Runtime_Math_acos(Arguments args) {
5848 NoHandleAllocation ha;
5849 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005850 Counters::math_acos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005851
5852 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005853 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005854}
5855
5856
5857static Object* Runtime_Math_asin(Arguments args) {
5858 NoHandleAllocation ha;
5859 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005860 Counters::math_asin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005861
5862 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005863 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005864}
5865
5866
5867static Object* Runtime_Math_atan(Arguments args) {
5868 NoHandleAllocation ha;
5869 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005870 Counters::math_atan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005871
5872 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005873 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005874}
5875
5876
5877static Object* Runtime_Math_atan2(Arguments args) {
5878 NoHandleAllocation ha;
5879 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005880 Counters::math_atan2.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005881
5882 CONVERT_DOUBLE_CHECKED(x, args[0]);
5883 CONVERT_DOUBLE_CHECKED(y, args[1]);
5884 double result;
5885 if (isinf(x) && isinf(y)) {
5886 // Make sure that the result in case of two infinite arguments
5887 // is a multiple of Pi / 4. The sign of the result is determined
5888 // by the first argument (x) and the sign of the second argument
5889 // determines the multiplier: one or three.
5890 static double kPiDividedBy4 = 0.78539816339744830962;
5891 int multiplier = (x < 0) ? -1 : 1;
5892 if (y < 0) multiplier *= 3;
5893 result = multiplier * kPiDividedBy4;
5894 } else {
5895 result = atan2(x, y);
5896 }
5897 return Heap::AllocateHeapNumber(result);
5898}
5899
5900
5901static Object* Runtime_Math_ceil(Arguments args) {
5902 NoHandleAllocation ha;
5903 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005904 Counters::math_ceil.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005905
5906 CONVERT_DOUBLE_CHECKED(x, args[0]);
5907 return Heap::NumberFromDouble(ceiling(x));
5908}
5909
5910
5911static Object* Runtime_Math_cos(Arguments args) {
5912 NoHandleAllocation ha;
5913 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005914 Counters::math_cos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005915
5916 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005917 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005918}
5919
5920
5921static Object* Runtime_Math_exp(Arguments args) {
5922 NoHandleAllocation ha;
5923 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005924 Counters::math_exp.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005925
5926 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005927 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005928}
5929
5930
5931static Object* Runtime_Math_floor(Arguments args) {
5932 NoHandleAllocation ha;
5933 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005934 Counters::math_floor.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005935
5936 CONVERT_DOUBLE_CHECKED(x, args[0]);
5937 return Heap::NumberFromDouble(floor(x));
5938}
5939
5940
5941static Object* Runtime_Math_log(Arguments args) {
5942 NoHandleAllocation ha;
5943 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005944 Counters::math_log.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005945
5946 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005947 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005948}
5949
5950
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005951// Helper function to compute x^y, where y is known to be an
5952// integer. Uses binary decomposition to limit the number of
5953// multiplications; see the discussion in "Hacker's Delight" by Henry
5954// S. Warren, Jr., figure 11-6, page 213.
5955static double powi(double x, int y) {
5956 ASSERT(y != kMinInt);
5957 unsigned n = (y < 0) ? -y : y;
5958 double m = x;
5959 double p = 1;
5960 while (true) {
5961 if ((n & 1) != 0) p *= m;
5962 n >>= 1;
5963 if (n == 0) {
5964 if (y < 0) {
5965 // Unfortunately, we have to be careful when p has reached
5966 // infinity in the computation, because sometimes the higher
5967 // internal precision in the pow() implementation would have
5968 // given us a finite p. This happens very rarely.
5969 double result = 1.0 / p;
5970 return (result == 0 && isinf(p))
5971 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
5972 : result;
5973 } else {
5974 return p;
5975 }
5976 }
5977 m *= m;
5978 }
5979}
5980
5981
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005982static Object* Runtime_Math_pow(Arguments args) {
5983 NoHandleAllocation ha;
5984 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005985 Counters::math_pow.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005986
5987 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005988
5989 // If the second argument is a smi, it is much faster to call the
5990 // custom powi() function than the generic pow().
5991 if (args[1]->IsSmi()) {
5992 int y = Smi::cast(args[1])->value();
5993 return Heap::AllocateHeapNumber(powi(x, y));
5994 }
5995
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005996 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00005997
5998 if (!isinf(x)) {
5999 if (y == 0.5) {
6000 // It's not uncommon to use Math.pow(x, 0.5) to compute the
6001 // square root of a number. To speed up such computations, we
6002 // explictly check for this case and use the sqrt() function
6003 // which is faster than pow().
6004 return Heap::AllocateHeapNumber(sqrt(x));
6005 } else if (y == -0.5) {
6006 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
6007 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
6008 }
6009 }
6010
6011 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006012 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006013 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6014 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006015 } else {
6016 return Heap::AllocateHeapNumber(pow(x, y));
6017 }
6018}
6019
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006020// Fast version of Math.pow if we know that y is not an integer and
6021// y is not -0.5 or 0.5. Used as slowcase from codegen.
6022static Object* Runtime_Math_pow_cfunction(Arguments args) {
6023 NoHandleAllocation ha;
6024 ASSERT(args.length() == 2);
6025 CONVERT_DOUBLE_CHECKED(x, args[0]);
6026 CONVERT_DOUBLE_CHECKED(y, args[1]);
6027 if (y == 0) {
6028 return Smi::FromInt(1);
6029 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6030 return Heap::nan_value();
6031 } else {
6032 return Heap::AllocateHeapNumber(pow(x, y));
6033 }
6034}
6035
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006036
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006037static Object* Runtime_RoundNumber(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006038 NoHandleAllocation ha;
6039 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006040 Counters::math_round.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006041
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006042 if (!args[0]->IsHeapNumber()) {
6043 // Must be smi. Return the argument unchanged for all the other types
6044 // to make fuzz-natives test happy.
6045 return args[0];
6046 }
6047
6048 HeapNumber* number = reinterpret_cast<HeapNumber*>(args[0]);
6049
6050 double value = number->value();
6051 int exponent = number->get_exponent();
6052 int sign = number->get_sign();
6053
6054 // We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
6055 // should be rounded to 2^30, which is not smi.
6056 if (!sign && exponent <= kSmiValueSize - 3) {
6057 return Smi::FromInt(static_cast<int>(value + 0.5));
6058 }
6059
6060 // If the magnitude is big enough, there's no place for fraction part. If we
6061 // try to add 0.5 to this number, 1.0 will be added instead.
6062 if (exponent >= 52) {
6063 return number;
6064 }
6065
6066 if (sign && value >= -0.5) return Heap::minus_zero_value();
6067
6068 return Heap::NumberFromDouble(floor(value + 0.5));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006069}
6070
6071
6072static Object* Runtime_Math_sin(Arguments args) {
6073 NoHandleAllocation ha;
6074 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006075 Counters::math_sin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006076
6077 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006078 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006079}
6080
6081
6082static Object* Runtime_Math_sqrt(Arguments args) {
6083 NoHandleAllocation ha;
6084 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006085 Counters::math_sqrt.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006086
6087 CONVERT_DOUBLE_CHECKED(x, args[0]);
6088 return Heap::AllocateHeapNumber(sqrt(x));
6089}
6090
6091
6092static Object* Runtime_Math_tan(Arguments args) {
6093 NoHandleAllocation ha;
6094 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006095 Counters::math_tan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006096
6097 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006098 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006099}
6100
6101
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006102static int MakeDay(int year, int month, int day) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006103 static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
6104 181, 212, 243, 273, 304, 334};
6105 static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
6106 182, 213, 244, 274, 305, 335};
6107
6108 year += month / 12;
6109 month %= 12;
6110 if (month < 0) {
6111 year--;
6112 month += 12;
6113 }
6114
6115 ASSERT(month >= 0);
6116 ASSERT(month < 12);
6117
6118 // year_delta is an arbitrary number such that:
6119 // a) year_delta = -1 (mod 400)
6120 // b) year + year_delta > 0 for years in the range defined by
6121 // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
6122 // Jan 1 1970. This is required so that we don't run into integer
6123 // division of negative numbers.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006124 // c) there shouldn't be an overflow for 32-bit integers in the following
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006125 // operations.
6126 static const int year_delta = 399999;
6127 static const int base_day = 365 * (1970 + year_delta) +
6128 (1970 + year_delta) / 4 -
6129 (1970 + year_delta) / 100 +
6130 (1970 + year_delta) / 400;
6131
6132 int year1 = year + year_delta;
6133 int day_from_year = 365 * year1 +
6134 year1 / 4 -
6135 year1 / 100 +
6136 year1 / 400 -
6137 base_day;
6138
6139 if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006140 return day_from_year + day_from_month[month] + day - 1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006141 }
6142
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006143 return day_from_year + day_from_month_leap[month] + day - 1;
6144}
6145
6146
6147static Object* Runtime_DateMakeDay(Arguments args) {
6148 NoHandleAllocation ha;
6149 ASSERT(args.length() == 3);
6150
6151 CONVERT_SMI_CHECKED(year, args[0]);
6152 CONVERT_SMI_CHECKED(month, args[1]);
6153 CONVERT_SMI_CHECKED(date, args[2]);
6154
6155 return Smi::FromInt(MakeDay(year, month, date));
6156}
6157
6158
6159static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
6160static const int kDaysIn4Years = 4 * 365 + 1;
6161static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
6162static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
6163static const int kDays1970to2000 = 30 * 365 + 7;
6164static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
6165 kDays1970to2000;
6166static const int kYearsOffset = 400000;
6167
6168static const char kDayInYear[] = {
6169 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6170 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6171 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6172 22, 23, 24, 25, 26, 27, 28,
6173 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6174 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6175 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6176 22, 23, 24, 25, 26, 27, 28, 29, 30,
6177 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6178 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6179 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6180 22, 23, 24, 25, 26, 27, 28, 29, 30,
6181 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6182 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6183 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6184 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6185 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6186 22, 23, 24, 25, 26, 27, 28, 29, 30,
6187 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6188 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6189 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6190 22, 23, 24, 25, 26, 27, 28, 29, 30,
6191 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6192 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6193
6194 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6195 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6196 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6197 22, 23, 24, 25, 26, 27, 28,
6198 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6199 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6200 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6201 22, 23, 24, 25, 26, 27, 28, 29, 30,
6202 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6203 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6204 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6205 22, 23, 24, 25, 26, 27, 28, 29, 30,
6206 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6207 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6208 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6209 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6210 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6211 22, 23, 24, 25, 26, 27, 28, 29, 30,
6212 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6213 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6214 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6215 22, 23, 24, 25, 26, 27, 28, 29, 30,
6216 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6217 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6218
6219 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6220 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6221 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6222 22, 23, 24, 25, 26, 27, 28, 29,
6223 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6224 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6225 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6226 22, 23, 24, 25, 26, 27, 28, 29, 30,
6227 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6228 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6229 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6230 22, 23, 24, 25, 26, 27, 28, 29, 30,
6231 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6232 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6233 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6234 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6235 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6236 22, 23, 24, 25, 26, 27, 28, 29, 30,
6237 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6238 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6239 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6240 22, 23, 24, 25, 26, 27, 28, 29, 30,
6241 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6242 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6243
6244 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6245 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6246 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6247 22, 23, 24, 25, 26, 27, 28,
6248 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6249 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6250 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6251 22, 23, 24, 25, 26, 27, 28, 29, 30,
6252 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6253 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6254 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6255 22, 23, 24, 25, 26, 27, 28, 29, 30,
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, 31,
6260 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6261 22, 23, 24, 25, 26, 27, 28, 29, 30,
6262 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6263 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6264 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
6269static const char kMonthInYear[] = {
6270 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,
6271 0, 0, 0, 0, 0, 0,
6272 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,
6273 1, 1, 1,
6274 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,
6275 2, 2, 2, 2, 2, 2,
6276 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,
6277 3, 3, 3, 3, 3,
6278 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,
6279 4, 4, 4, 4, 4, 4,
6280 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,
6281 5, 5, 5, 5, 5,
6282 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,
6283 6, 6, 6, 6, 6, 6,
6284 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,
6285 7, 7, 7, 7, 7, 7,
6286 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,
6287 8, 8, 8, 8, 8,
6288 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,
6289 9, 9, 9, 9, 9, 9,
6290 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6291 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6292 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6293 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6294
6295 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,
6296 0, 0, 0, 0, 0, 0,
6297 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,
6298 1, 1, 1,
6299 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,
6300 2, 2, 2, 2, 2, 2,
6301 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,
6302 3, 3, 3, 3, 3,
6303 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,
6304 4, 4, 4, 4, 4, 4,
6305 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,
6306 5, 5, 5, 5, 5,
6307 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,
6308 6, 6, 6, 6, 6, 6,
6309 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,
6310 7, 7, 7, 7, 7, 7,
6311 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,
6312 8, 8, 8, 8, 8,
6313 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,
6314 9, 9, 9, 9, 9, 9,
6315 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6316 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6317 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6318 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6319
6320 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,
6321 0, 0, 0, 0, 0, 0,
6322 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,
6323 1, 1, 1, 1,
6324 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,
6325 2, 2, 2, 2, 2, 2,
6326 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,
6327 3, 3, 3, 3, 3,
6328 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,
6329 4, 4, 4, 4, 4, 4,
6330 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,
6331 5, 5, 5, 5, 5,
6332 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,
6333 6, 6, 6, 6, 6, 6,
6334 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,
6335 7, 7, 7, 7, 7, 7,
6336 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,
6337 8, 8, 8, 8, 8,
6338 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,
6339 9, 9, 9, 9, 9, 9,
6340 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6341 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6342 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6343 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6344
6345 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,
6346 0, 0, 0, 0, 0, 0,
6347 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,
6348 1, 1, 1,
6349 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,
6350 2, 2, 2, 2, 2, 2,
6351 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,
6352 3, 3, 3, 3, 3,
6353 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,
6354 4, 4, 4, 4, 4, 4,
6355 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,
6356 5, 5, 5, 5, 5,
6357 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,
6358 6, 6, 6, 6, 6, 6,
6359 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,
6360 7, 7, 7, 7, 7, 7,
6361 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,
6362 8, 8, 8, 8, 8,
6363 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,
6364 9, 9, 9, 9, 9, 9,
6365 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6366 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6367 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6368 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
6369
6370
6371// This function works for dates from 1970 to 2099.
6372static inline void DateYMDFromTimeAfter1970(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006373 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006374#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006375 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006376#endif
6377
6378 year = 1970 + (4 * date + 2) / kDaysIn4Years;
6379 date %= kDaysIn4Years;
6380
6381 month = kMonthInYear[date];
6382 day = kDayInYear[date];
6383
6384 ASSERT(MakeDay(year, month, day) == save_date);
6385}
6386
6387
6388static inline void DateYMDFromTimeSlow(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006389 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006390#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006391 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006392#endif
6393
6394 date += kDaysOffset;
6395 year = 400 * (date / kDaysIn400Years) - kYearsOffset;
6396 date %= kDaysIn400Years;
6397
6398 ASSERT(MakeDay(year, 0, 1) + date == save_date);
6399
6400 date--;
6401 int yd1 = date / kDaysIn100Years;
6402 date %= kDaysIn100Years;
6403 year += 100 * yd1;
6404
6405 date++;
6406 int yd2 = date / kDaysIn4Years;
6407 date %= kDaysIn4Years;
6408 year += 4 * yd2;
6409
6410 date--;
6411 int yd3 = date / 365;
6412 date %= 365;
6413 year += yd3;
6414
6415 bool is_leap = (!yd1 || yd2) && !yd3;
6416
6417 ASSERT(date >= -1);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006418 ASSERT(is_leap || (date >= 0));
6419 ASSERT((date < 365) || (is_leap && (date < 366)));
6420 ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0))));
6421 ASSERT(is_leap || ((MakeDay(year, 0, 1) + date) == save_date));
6422 ASSERT(!is_leap || ((MakeDay(year, 0, 1) + date + 1) == save_date));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006423
6424 if (is_leap) {
6425 day = kDayInYear[2*365 + 1 + date];
6426 month = kMonthInYear[2*365 + 1 + date];
6427 } else {
6428 day = kDayInYear[date];
6429 month = kMonthInYear[date];
6430 }
6431
6432 ASSERT(MakeDay(year, month, day) == save_date);
6433}
6434
6435
6436static inline void DateYMDFromTime(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006437 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006438 if (date >= 0 && date < 32 * kDaysIn4Years) {
6439 DateYMDFromTimeAfter1970(date, year, month, day);
6440 } else {
6441 DateYMDFromTimeSlow(date, year, month, day);
6442 }
6443}
6444
6445
6446static Object* Runtime_DateYMDFromTime(Arguments args) {
6447 NoHandleAllocation ha;
6448 ASSERT(args.length() == 2);
6449
6450 CONVERT_DOUBLE_CHECKED(t, args[0]);
6451 CONVERT_CHECKED(JSArray, res_array, args[1]);
6452
6453 int year, month, day;
6454 DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
6455
6456 res_array->SetElement(0, Smi::FromInt(year));
6457 res_array->SetElement(1, Smi::FromInt(month));
6458 res_array->SetElement(2, Smi::FromInt(day));
6459
6460 return Heap::undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006461}
6462
6463
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006464static Object* Runtime_NewArgumentsFast(Arguments args) {
6465 NoHandleAllocation ha;
6466 ASSERT(args.length() == 3);
6467
6468 JSFunction* callee = JSFunction::cast(args[0]);
6469 Object** parameters = reinterpret_cast<Object**>(args[1]);
6470 const int length = Smi::cast(args[2])->value();
6471
6472 Object* result = Heap::AllocateArgumentsObject(callee, length);
6473 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006474 // Allocate the elements if needed.
6475 if (length > 0) {
6476 // Allocate the fixed array.
6477 Object* obj = Heap::AllocateRawFixedArray(length);
6478 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006479
6480 AssertNoAllocation no_gc;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006481 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
6482 FixedArray* array = FixedArray::cast(obj);
6483 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006484
6485 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006486 for (int i = 0; i < length; i++) {
6487 array->set(i, *--parameters, mode);
6488 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006489 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006490 }
6491 return result;
6492}
6493
6494
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006495static Object* Runtime_NewClosure(Arguments args) {
6496 HandleScope scope;
6497 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00006498 CONVERT_ARG_CHECKED(Context, context, 0);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006499 CONVERT_ARG_CHECKED(SharedFunctionInfo, shared, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006500
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00006501 PretenureFlag pretenure = (context->global_context() == *context)
6502 ? TENURED // Allocate global closures in old space.
6503 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006504 Handle<JSFunction> result =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006505 Factory::NewFunctionFromSharedFunctionInfo(shared, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006506 return *result;
6507}
6508
6509
ager@chromium.org5c838252010-02-19 08:53:10 +00006510static Code* ComputeConstructStub(Handle<JSFunction> function) {
6511 Handle<Object> prototype = Factory::null_value();
6512 if (function->has_instance_prototype()) {
6513 prototype = Handle<Object>(function->instance_prototype());
6514 }
6515 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006516 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00006517 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006518 if (code->IsFailure()) {
6519 return Builtins::builtin(Builtins::JSConstructStubGeneric);
6520 }
6521 return Code::cast(code);
6522 }
6523
ager@chromium.org5c838252010-02-19 08:53:10 +00006524 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006525}
6526
6527
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006528static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006529 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006530 ASSERT(args.length() == 1);
6531
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006532 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006533
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006534 // If the constructor isn't a proper function we throw a type error.
6535 if (!constructor->IsJSFunction()) {
6536 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6537 Handle<Object> type_error =
6538 Factory::NewTypeError("not_constructor", arguments);
6539 return Top::Throw(*type_error);
6540 }
6541
6542 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006543#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006544 // Handle stepping into constructors if step into is active.
6545 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006546 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006547 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006548#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006549
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006550 if (function->has_initial_map()) {
6551 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006552 // The 'Function' function ignores the receiver object when
6553 // called using 'new' and creates a new JSFunction object that
6554 // is returned. The receiver object is only used for error
6555 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006556 // JSFunction. Factory::NewJSObject() should not be used to
6557 // allocate JSFunctions since it does not properly initialize
6558 // the shared part of the function. Since the receiver is
6559 // ignored anyway, we use the global object as the receiver
6560 // instead of a new JSFunction object. This way, errors are
6561 // reported the same way whether or not 'Function' is called
6562 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006563 return Top::context()->global();
6564 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006565 }
6566
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006567 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006568 Handle<SharedFunctionInfo> shared(function->shared());
6569 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006570
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006571 bool first_allocation = !function->has_initial_map();
6572 Handle<JSObject> result = Factory::NewJSObject(function);
6573 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006574 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00006575 ComputeConstructStub(Handle<JSFunction>(function)));
6576 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006577 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006578
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006579 Counters::constructed_objects.Increment();
6580 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006581
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006582 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006583}
6584
6585
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006586static Object* Runtime_LazyCompile(Arguments args) {
6587 HandleScope scope;
6588 ASSERT(args.length() == 1);
6589
6590 Handle<JSFunction> function = args.at<JSFunction>(0);
6591#ifdef DEBUG
6592 if (FLAG_trace_lazy) {
6593 PrintF("[lazy: ");
6594 function->shared()->name()->Print();
6595 PrintF("]\n");
6596 }
6597#endif
6598
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006599 // Compile the target function. Here we compile using CompileLazyInLoop in
6600 // order to get the optimized version. This helps code like delta-blue
6601 // that calls performance-critical routines through constructors. A
6602 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
6603 // direct call. Since the in-loop tracking takes place through CallICs
6604 // this means that things called through constructors are never known to
6605 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006606 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006607 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006608 return Failure::Exception();
6609 }
6610
6611 return function->code();
6612}
6613
6614
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006615static Object* Runtime_GetFunctionDelegate(Arguments args) {
6616 HandleScope scope;
6617 ASSERT(args.length() == 1);
6618 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6619 return *Execution::GetFunctionDelegate(args.at<Object>(0));
6620}
6621
6622
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00006623static Object* Runtime_GetConstructorDelegate(Arguments args) {
6624 HandleScope scope;
6625 ASSERT(args.length() == 1);
6626 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6627 return *Execution::GetConstructorDelegate(args.at<Object>(0));
6628}
6629
6630
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006631static Object* Runtime_NewContext(Arguments args) {
6632 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00006633 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006634
kasper.lund7276f142008-07-30 08:49:36 +00006635 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006636 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
6637 Object* result = Heap::AllocateFunctionContext(length, function);
6638 if (result->IsFailure()) return result;
6639
6640 Top::set_context(Context::cast(result));
6641
kasper.lund7276f142008-07-30 08:49:36 +00006642 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006643}
6644
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006645static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006646 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006647 Object* js_object = object;
6648 if (!js_object->IsJSObject()) {
6649 js_object = js_object->ToObject();
6650 if (js_object->IsFailure()) {
6651 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006652 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006653 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006654 Handle<Object> result =
6655 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
6656 return Top::Throw(*result);
6657 }
6658 }
6659
6660 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006661 Heap::AllocateWithContext(Top::context(),
6662 JSObject::cast(js_object),
6663 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006664 if (result->IsFailure()) return result;
6665
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006666 Context* context = Context::cast(result);
6667 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006668
kasper.lund7276f142008-07-30 08:49:36 +00006669 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006670}
6671
6672
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006673static Object* Runtime_PushContext(Arguments args) {
6674 NoHandleAllocation ha;
6675 ASSERT(args.length() == 1);
6676 return PushContextHelper(args[0], false);
6677}
6678
6679
6680static Object* Runtime_PushCatchContext(Arguments args) {
6681 NoHandleAllocation ha;
6682 ASSERT(args.length() == 1);
6683 return PushContextHelper(args[0], true);
6684}
6685
6686
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006687static Object* Runtime_LookupContext(Arguments args) {
6688 HandleScope scope;
6689 ASSERT(args.length() == 2);
6690
6691 CONVERT_ARG_CHECKED(Context, context, 0);
6692 CONVERT_ARG_CHECKED(String, name, 1);
6693
6694 int index;
6695 PropertyAttributes attributes;
6696 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006697 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006698 context->Lookup(name, flags, &index, &attributes);
6699
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006700 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006701 ASSERT(holder->IsJSObject());
6702 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006703 }
6704
6705 // No intermediate context found. Use global object by default.
6706 return Top::context()->global();
6707}
6708
6709
ager@chromium.orga1645e22009-09-09 19:27:10 +00006710// A mechanism to return a pair of Object pointers in registers (if possible).
6711// How this is achieved is calling convention-dependent.
6712// All currently supported x86 compiles uses calling conventions that are cdecl
6713// variants where a 64-bit value is returned in two 32-bit registers
6714// (edx:eax on ia32, r1:r0 on ARM).
6715// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
6716// In Win64 calling convention, a struct of two pointers is returned in memory,
6717// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006718#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006719struct ObjectPair {
6720 Object* x;
6721 Object* y;
6722};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006723
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006724static inline ObjectPair MakePair(Object* x, Object* y) {
6725 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006726 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
6727 // In Win64 they are assigned to a hidden first argument.
6728 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006729}
6730#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006731typedef uint64_t ObjectPair;
6732static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006733 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006734 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006735}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006736#endif
6737
6738
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006739static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006740 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
6741 USE(attributes);
6742 return x->IsTheHole() ? Heap::undefined_value() : x;
6743}
6744
6745
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006746static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
6747 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006748 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006749 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006750 JSFunction* context_extension_function =
6751 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006752 // If the holder isn't a context extension object, we just return it
6753 // as the receiver. This allows arguments objects to be used as
6754 // receivers, but only if they are put in the context scope chain
6755 // explicitly via a with-statement.
6756 Object* constructor = holder->map()->constructor();
6757 if (constructor != context_extension_function) return holder;
6758 // Fall back to using the global object as the receiver if the
6759 // property turns out to be a local variable allocated in a context
6760 // extension object - introduced via eval.
6761 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006762}
6763
6764
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006765static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006766 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00006767 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006768
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006769 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00006770 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006771 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006772 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006773 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006774
6775 int index;
6776 PropertyAttributes attributes;
6777 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006778 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006779 context->Lookup(name, flags, &index, &attributes);
6780
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006781 // If the index is non-negative, the slot has been found in a local
6782 // variable or a parameter. Read it from the context object or the
6783 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006784 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006785 // If the "property" we were looking for is a local variable or an
6786 // argument in a context, the receiver is the global object; see
6787 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
6788 JSObject* receiver = Top::context()->global()->global_receiver();
6789 Object* value = (holder->IsContext())
6790 ? Context::cast(*holder)->get(index)
6791 : JSObject::cast(*holder)->GetElement(index);
6792 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006793 }
6794
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006795 // If the holder is found, we read the property from it.
6796 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006797 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006798 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006799 JSObject* receiver;
6800 if (object->IsGlobalObject()) {
6801 receiver = GlobalObject::cast(object)->global_receiver();
6802 } else if (context->is_exception_holder(*holder)) {
6803 receiver = Top::context()->global()->global_receiver();
6804 } else {
6805 receiver = ComputeReceiverForNonGlobal(object);
6806 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006807 // No need to unhole the value here. This is taken care of by the
6808 // GetProperty function.
6809 Object* value = object->GetProperty(*name);
6810 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006811 }
6812
6813 if (throw_error) {
6814 // The property doesn't exist - throw exception.
6815 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006816 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006817 return MakePair(Top::Throw(*reference_error), NULL);
6818 } else {
6819 // The property doesn't exist - return undefined
6820 return MakePair(Heap::undefined_value(), Heap::undefined_value());
6821 }
6822}
6823
6824
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006825static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006826 return LoadContextSlotHelper(args, true);
6827}
6828
6829
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006830static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006831 return LoadContextSlotHelper(args, false);
6832}
6833
6834
6835static Object* Runtime_StoreContextSlot(Arguments args) {
6836 HandleScope scope;
6837 ASSERT(args.length() == 3);
6838
6839 Handle<Object> value(args[0]);
6840 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006841 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006842
6843 int index;
6844 PropertyAttributes attributes;
6845 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006846 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006847 context->Lookup(name, flags, &index, &attributes);
6848
6849 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006850 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006851 // Ignore if read_only variable.
6852 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006853 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006854 }
6855 } else {
6856 ASSERT((attributes & READ_ONLY) == 0);
6857 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006858 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006859 USE(result);
6860 ASSERT(!result->IsFailure());
6861 }
6862 return *value;
6863 }
6864
6865 // Slow case: The property is not in a FixedArray context.
6866 // It is either in an JSObject extension context or it was not found.
6867 Handle<JSObject> context_ext;
6868
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006869 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006870 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006871 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006872 } else {
6873 // The property was not found. It needs to be stored in the global context.
6874 ASSERT(attributes == ABSENT);
6875 attributes = NONE;
6876 context_ext = Handle<JSObject>(Top::context()->global());
6877 }
6878
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006879 // Set the property, but ignore if read_only variable on the context
6880 // extension object itself.
6881 if ((attributes & READ_ONLY) == 0 ||
6882 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006883 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
6884 if (set.is_null()) {
6885 // Failure::Exception is converted to a null handle in the
6886 // handle-based methods such as SetProperty. We therefore need
6887 // to convert null handles back to exceptions.
6888 ASSERT(Top::has_pending_exception());
6889 return Failure::Exception();
6890 }
6891 }
6892 return *value;
6893}
6894
6895
6896static Object* Runtime_Throw(Arguments args) {
6897 HandleScope scope;
6898 ASSERT(args.length() == 1);
6899
6900 return Top::Throw(args[0]);
6901}
6902
6903
6904static Object* Runtime_ReThrow(Arguments args) {
6905 HandleScope scope;
6906 ASSERT(args.length() == 1);
6907
6908 return Top::ReThrow(args[0]);
6909}
6910
6911
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006912static Object* Runtime_PromoteScheduledException(Arguments args) {
6913 ASSERT_EQ(0, args.length());
6914 return Top::PromoteScheduledException();
6915}
6916
6917
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006918static Object* Runtime_ThrowReferenceError(Arguments args) {
6919 HandleScope scope;
6920 ASSERT(args.length() == 1);
6921
6922 Handle<Object> name(args[0]);
6923 Handle<Object> reference_error =
6924 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
6925 return Top::Throw(*reference_error);
6926}
6927
6928
6929static Object* Runtime_StackOverflow(Arguments args) {
6930 NoHandleAllocation na;
6931 return Top::StackOverflow();
6932}
6933
6934
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006935static Object* Runtime_StackGuard(Arguments args) {
6936 ASSERT(args.length() == 1);
6937
6938 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006939 if (StackGuard::IsStackOverflow()) {
6940 return Runtime_StackOverflow(args);
6941 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006942
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006943 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006944}
6945
6946
6947// NOTE: These PrintXXX functions are defined for all builds (not just
6948// DEBUG builds) because we may want to be able to trace function
6949// calls in all modes.
6950static void PrintString(String* str) {
6951 // not uncommon to have empty strings
6952 if (str->length() > 0) {
6953 SmartPointer<char> s =
6954 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
6955 PrintF("%s", *s);
6956 }
6957}
6958
6959
6960static void PrintObject(Object* obj) {
6961 if (obj->IsSmi()) {
6962 PrintF("%d", Smi::cast(obj)->value());
6963 } else if (obj->IsString() || obj->IsSymbol()) {
6964 PrintString(String::cast(obj));
6965 } else if (obj->IsNumber()) {
6966 PrintF("%g", obj->Number());
6967 } else if (obj->IsFailure()) {
6968 PrintF("<failure>");
6969 } else if (obj->IsUndefined()) {
6970 PrintF("<undefined>");
6971 } else if (obj->IsNull()) {
6972 PrintF("<null>");
6973 } else if (obj->IsTrue()) {
6974 PrintF("<true>");
6975 } else if (obj->IsFalse()) {
6976 PrintF("<false>");
6977 } else {
6978 PrintF("%p", obj);
6979 }
6980}
6981
6982
6983static int StackSize() {
6984 int n = 0;
6985 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
6986 return n;
6987}
6988
6989
6990static void PrintTransition(Object* result) {
6991 // indentation
6992 { const int nmax = 80;
6993 int n = StackSize();
6994 if (n <= nmax)
6995 PrintF("%4d:%*s", n, n, "");
6996 else
6997 PrintF("%4d:%*s", n, nmax, "...");
6998 }
6999
7000 if (result == NULL) {
7001 // constructor calls
7002 JavaScriptFrameIterator it;
7003 JavaScriptFrame* frame = it.frame();
7004 if (frame->IsConstructor()) PrintF("new ");
7005 // function name
7006 Object* fun = frame->function();
7007 if (fun->IsJSFunction()) {
7008 PrintObject(JSFunction::cast(fun)->shared()->name());
7009 } else {
7010 PrintObject(fun);
7011 }
7012 // function arguments
7013 // (we are intentionally only printing the actually
7014 // supplied parameters, not all parameters required)
7015 PrintF("(this=");
7016 PrintObject(frame->receiver());
7017 const int length = frame->GetProvidedParametersCount();
7018 for (int i = 0; i < length; i++) {
7019 PrintF(", ");
7020 PrintObject(frame->GetParameter(i));
7021 }
7022 PrintF(") {\n");
7023
7024 } else {
7025 // function result
7026 PrintF("} -> ");
7027 PrintObject(result);
7028 PrintF("\n");
7029 }
7030}
7031
7032
7033static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007034 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007035 NoHandleAllocation ha;
7036 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007037 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007038}
7039
7040
7041static Object* Runtime_TraceExit(Arguments args) {
7042 NoHandleAllocation ha;
7043 PrintTransition(args[0]);
7044 return args[0]; // return TOS
7045}
7046
7047
7048static Object* Runtime_DebugPrint(Arguments args) {
7049 NoHandleAllocation ha;
7050 ASSERT(args.length() == 1);
7051
7052#ifdef DEBUG
7053 if (args[0]->IsString()) {
7054 // If we have a string, assume it's a code "marker"
7055 // and print some interesting cpu debugging info.
7056 JavaScriptFrameIterator it;
7057 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007058 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
7059 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007060 } else {
7061 PrintF("DebugPrint: ");
7062 }
7063 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007064 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007065 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007066 HeapObject::cast(args[0])->map()->Print();
7067 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007068#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007069 // ShortPrint is available in release mode. Print is not.
7070 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007071#endif
7072 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00007073 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007074
7075 return args[0]; // return TOS
7076}
7077
7078
7079static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007080 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007081 NoHandleAllocation ha;
7082 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007083 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007084}
7085
7086
mads.s.ager31e71382008-08-13 09:32:07 +00007087static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007088 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007089 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007090
7091 // According to ECMA-262, section 15.9.1, page 117, the precision of
7092 // the number in a Date object representing a particular instant in
7093 // time is milliseconds. Therefore, we floor the result of getting
7094 // the OS time.
7095 double millis = floor(OS::TimeCurrentMillis());
7096 return Heap::NumberFromDouble(millis);
7097}
7098
7099
7100static Object* Runtime_DateParseString(Arguments args) {
7101 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007102 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007103
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007104 CONVERT_ARG_CHECKED(String, str, 0);
7105 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007106
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007107 CONVERT_ARG_CHECKED(JSArray, output, 1);
7108 RUNTIME_ASSERT(output->HasFastElements());
7109
7110 AssertNoAllocation no_allocation;
7111
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007112 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007113 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
7114 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007115 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007116 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007117 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007118 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007119 result = DateParser::Parse(str->ToUC16Vector(), output_array);
7120 }
7121
7122 if (result) {
7123 return *output;
7124 } else {
7125 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007126 }
7127}
7128
7129
7130static Object* Runtime_DateLocalTimezone(Arguments args) {
7131 NoHandleAllocation ha;
7132 ASSERT(args.length() == 1);
7133
7134 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007135 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007136 return Heap::AllocateStringFromUtf8(CStrVector(zone));
7137}
7138
7139
7140static Object* Runtime_DateLocalTimeOffset(Arguments args) {
7141 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007142 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007143
7144 return Heap::NumberFromDouble(OS::LocalTimeOffset());
7145}
7146
7147
7148static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
7149 NoHandleAllocation ha;
7150 ASSERT(args.length() == 1);
7151
7152 CONVERT_DOUBLE_CHECKED(x, args[0]);
7153 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
7154}
7155
7156
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007157static Object* Runtime_GlobalReceiver(Arguments args) {
7158 ASSERT(args.length() == 1);
7159 Object* global = args[0];
7160 if (!global->IsJSGlobalObject()) return Heap::null_value();
7161 return JSGlobalObject::cast(global)->global_receiver();
7162}
7163
7164
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007165static Object* Runtime_CompileString(Arguments args) {
7166 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007167 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007168 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007169 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007170
ager@chromium.org381abbb2009-02-25 13:23:22 +00007171 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007172 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007173 Compiler::ValidationState validate = (is_json->IsTrue())
7174 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007175 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
7176 context,
7177 true,
7178 validate);
7179 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007180 Handle<JSFunction> fun =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007181 Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007182 return *fun;
7183}
7184
7185
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007186static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
7187 ASSERT(args.length() == 3);
7188 if (!args[0]->IsJSFunction()) {
7189 return MakePair(Top::ThrowIllegalOperation(), NULL);
7190 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007191
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007192 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007193 Handle<JSFunction> callee = args.at<JSFunction>(0);
7194 Handle<Object> receiver; // Will be overwritten.
7195
7196 // Compute the calling context.
7197 Handle<Context> context = Handle<Context>(Top::context());
7198#ifdef DEBUG
7199 // Make sure Top::context() agrees with the old code that traversed
7200 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007201 StackFrameLocator locator;
7202 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007203 ASSERT(Context::cast(frame->context()) == *context);
7204#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007205
7206 // Find where the 'eval' symbol is bound. It is unaliased only if
7207 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007208 int index = -1;
7209 PropertyAttributes attributes = ABSENT;
7210 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007211 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
7212 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007213 // Stop search when eval is found or when the global context is
7214 // reached.
7215 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007216 if (context->is_function_context()) {
7217 context = Handle<Context>(Context::cast(context->closure()->context()));
7218 } else {
7219 context = Handle<Context>(context->previous());
7220 }
7221 }
7222
iposva@chromium.org245aa852009-02-10 00:49:54 +00007223 // If eval could not be resolved, it has been deleted and we need to
7224 // throw a reference error.
7225 if (attributes == ABSENT) {
7226 Handle<Object> name = Factory::eval_symbol();
7227 Handle<Object> reference_error =
7228 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007229 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007230 }
7231
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007232 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007233 // 'eval' is not bound in the global context. Just call the function
7234 // with the given arguments. This is not necessarily the global eval.
7235 if (receiver->IsContext()) {
7236 context = Handle<Context>::cast(receiver);
7237 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007238 } else if (receiver->IsJSContextExtensionObject()) {
7239 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007240 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007241 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007242 }
7243
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007244 // 'eval' is bound in the global context, but it may have been overwritten.
7245 // Compare it to the builtin 'GlobalEval' function to make sure.
7246 if (*callee != Top::global_context()->global_eval_fun() ||
7247 !args[1]->IsString()) {
7248 return MakePair(*callee, Top::context()->global()->global_receiver());
7249 }
7250
7251 // Deal with a normal eval call with a string argument. Compile it
7252 // and return the compiled function bound in the local context.
7253 Handle<String> source = args.at<String>(1);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007254 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007255 source,
7256 Handle<Context>(Top::context()),
7257 Top::context()->IsGlobalContext(),
7258 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007259 if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
7260 callee = Factory::NewFunctionFromSharedFunctionInfo(
7261 shared,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007262 Handle<Context>(Top::context()),
7263 NOT_TENURED);
7264 return MakePair(*callee, args[2]);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007265}
7266
7267
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007268static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
7269 // This utility adjusts the property attributes for newly created Function
7270 // object ("new Function(...)") by changing the map.
7271 // All it does is changing the prototype property to enumerable
7272 // as specified in ECMA262, 15.3.5.2.
7273 HandleScope scope;
7274 ASSERT(args.length() == 1);
7275 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7276 ASSERT(func->map()->instance_type() ==
7277 Top::function_instance_map()->instance_type());
7278 ASSERT(func->map()->instance_size() ==
7279 Top::function_instance_map()->instance_size());
7280 func->set_map(*Top::function_instance_map());
7281 return *func;
7282}
7283
7284
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007285// Push an array unto an array of arrays if it is not already in the
7286// array. Returns true if the element was pushed on the stack and
7287// false otherwise.
7288static Object* Runtime_PushIfAbsent(Arguments args) {
7289 ASSERT(args.length() == 2);
7290 CONVERT_CHECKED(JSArray, array, args[0]);
7291 CONVERT_CHECKED(JSArray, element, args[1]);
7292 RUNTIME_ASSERT(array->HasFastElements());
7293 int length = Smi::cast(array->length())->value();
7294 FixedArray* elements = FixedArray::cast(array->elements());
7295 for (int i = 0; i < length; i++) {
7296 if (elements->get(i) == element) return Heap::false_value();
7297 }
7298 Object* obj = array->SetFastElement(length, element);
7299 if (obj->IsFailure()) return obj;
7300 return Heap::true_value();
7301}
7302
7303
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007304/**
7305 * A simple visitor visits every element of Array's.
7306 * The backend storage can be a fixed array for fast elements case,
7307 * or a dictionary for sparse array. Since Dictionary is a subtype
7308 * of FixedArray, the class can be used by both fast and slow cases.
7309 * The second parameter of the constructor, fast_elements, specifies
7310 * whether the storage is a FixedArray or Dictionary.
7311 *
7312 * An index limit is used to deal with the situation that a result array
7313 * length overflows 32-bit non-negative integer.
7314 */
7315class ArrayConcatVisitor {
7316 public:
7317 ArrayConcatVisitor(Handle<FixedArray> storage,
7318 uint32_t index_limit,
7319 bool fast_elements) :
7320 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007321 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007322
7323 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007324 if (i >= index_limit_ - index_offset_) return;
7325 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007326
7327 if (fast_elements_) {
7328 ASSERT(index < static_cast<uint32_t>(storage_->length()));
7329 storage_->set(index, *elm);
7330
7331 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007332 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
7333 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007334 Factory::DictionaryAtNumberPut(dict, index, elm);
7335 if (!result.is_identical_to(dict))
7336 storage_ = result;
7337 }
7338 }
7339
7340 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007341 if (index_limit_ - index_offset_ < delta) {
7342 index_offset_ = index_limit_;
7343 } else {
7344 index_offset_ += delta;
7345 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007346 }
7347
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007348 Handle<FixedArray> storage() { return storage_; }
7349
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007350 private:
7351 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007352 // Limit on the accepted indices. Elements with indices larger than the
7353 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007354 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007355 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007356 uint32_t index_offset_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007357 bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007358};
7359
7360
ager@chromium.org3811b432009-10-28 14:53:37 +00007361template<class ExternalArrayClass, class ElementType>
7362static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
7363 bool elements_are_ints,
7364 bool elements_are_guaranteed_smis,
7365 uint32_t range,
7366 ArrayConcatVisitor* visitor) {
7367 Handle<ExternalArrayClass> array(
7368 ExternalArrayClass::cast(receiver->elements()));
7369 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
7370
7371 if (visitor != NULL) {
7372 if (elements_are_ints) {
7373 if (elements_are_guaranteed_smis) {
7374 for (uint32_t j = 0; j < len; j++) {
7375 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
7376 visitor->visit(j, e);
7377 }
7378 } else {
7379 for (uint32_t j = 0; j < len; j++) {
7380 int64_t val = static_cast<int64_t>(array->get(j));
7381 if (Smi::IsValid(static_cast<intptr_t>(val))) {
7382 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
7383 visitor->visit(j, e);
7384 } else {
7385 Handle<Object> e(
7386 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
7387 visitor->visit(j, e);
7388 }
7389 }
7390 }
7391 } else {
7392 for (uint32_t j = 0; j < len; j++) {
7393 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
7394 visitor->visit(j, e);
7395 }
7396 }
7397 }
7398
7399 return len;
7400}
7401
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007402/**
7403 * A helper function that visits elements of a JSObject. Only elements
7404 * whose index between 0 and range (exclusive) are visited.
7405 *
7406 * If the third parameter, visitor, is not NULL, the visitor is called
7407 * with parameters, 'visitor_index_offset + element index' and the element.
7408 *
7409 * It returns the number of visisted elements.
7410 */
7411static uint32_t IterateElements(Handle<JSObject> receiver,
7412 uint32_t range,
7413 ArrayConcatVisitor* visitor) {
7414 uint32_t num_of_elements = 0;
7415
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007416 switch (receiver->GetElementsKind()) {
7417 case JSObject::FAST_ELEMENTS: {
7418 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
7419 uint32_t len = elements->length();
7420 if (range < len) {
7421 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007422 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007423
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007424 for (uint32_t j = 0; j < len; j++) {
7425 Handle<Object> e(elements->get(j));
7426 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007427 num_of_elements++;
7428 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007429 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007430 }
7431 }
7432 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007433 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007434 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007435 case JSObject::PIXEL_ELEMENTS: {
7436 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
7437 uint32_t len = pixels->length();
7438 if (range < len) {
7439 len = range;
7440 }
7441
7442 for (uint32_t j = 0; j < len; j++) {
7443 num_of_elements++;
7444 if (visitor != NULL) {
7445 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
7446 visitor->visit(j, e);
7447 }
7448 }
7449 break;
7450 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007451 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
7452 num_of_elements =
7453 IterateExternalArrayElements<ExternalByteArray, int8_t>(
7454 receiver, true, true, range, visitor);
7455 break;
7456 }
7457 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7458 num_of_elements =
7459 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
7460 receiver, true, true, range, visitor);
7461 break;
7462 }
7463 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
7464 num_of_elements =
7465 IterateExternalArrayElements<ExternalShortArray, int16_t>(
7466 receiver, true, true, range, visitor);
7467 break;
7468 }
7469 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7470 num_of_elements =
7471 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
7472 receiver, true, true, range, visitor);
7473 break;
7474 }
7475 case JSObject::EXTERNAL_INT_ELEMENTS: {
7476 num_of_elements =
7477 IterateExternalArrayElements<ExternalIntArray, int32_t>(
7478 receiver, true, false, range, visitor);
7479 break;
7480 }
7481 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7482 num_of_elements =
7483 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
7484 receiver, true, false, range, visitor);
7485 break;
7486 }
7487 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
7488 num_of_elements =
7489 IterateExternalArrayElements<ExternalFloatArray, float>(
7490 receiver, false, false, range, visitor);
7491 break;
7492 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007493 case JSObject::DICTIONARY_ELEMENTS: {
7494 Handle<NumberDictionary> dict(receiver->element_dictionary());
7495 uint32_t capacity = dict->Capacity();
7496 for (uint32_t j = 0; j < capacity; j++) {
7497 Handle<Object> k(dict->KeyAt(j));
7498 if (dict->IsKey(*k)) {
7499 ASSERT(k->IsNumber());
7500 uint32_t index = static_cast<uint32_t>(k->Number());
7501 if (index < range) {
7502 num_of_elements++;
7503 if (visitor) {
7504 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
7505 }
7506 }
7507 }
7508 }
7509 break;
7510 }
7511 default:
7512 UNREACHABLE();
7513 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007514 }
7515
7516 return num_of_elements;
7517}
7518
7519
7520/**
7521 * A helper function that visits elements of an Array object, and elements
7522 * on its prototypes.
7523 *
7524 * Elements on prototypes are visited first, and only elements whose indices
7525 * less than Array length are visited.
7526 *
7527 * If a ArrayConcatVisitor object is given, the visitor is called with
7528 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007529 *
7530 * The returned number of elements is an upper bound on the actual number
7531 * of elements added. If the same element occurs in more than one object
7532 * in the array's prototype chain, it will be counted more than once, but
7533 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007534 */
7535static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
7536 ArrayConcatVisitor* visitor) {
7537 uint32_t range = static_cast<uint32_t>(array->length()->Number());
7538 Handle<Object> obj = array;
7539
7540 static const int kEstimatedPrototypes = 3;
7541 List< Handle<JSObject> > objects(kEstimatedPrototypes);
7542
7543 // Visit prototype first. If an element on the prototype is shadowed by
7544 // the inheritor using the same index, the ArrayConcatVisitor visits
7545 // the prototype element before the shadowing element.
7546 // The visitor can simply overwrite the old value by new value using
7547 // the same index. This follows Array::concat semantics.
7548 while (!obj->IsNull()) {
7549 objects.Add(Handle<JSObject>::cast(obj));
7550 obj = Handle<Object>(obj->GetPrototype());
7551 }
7552
7553 uint32_t nof_elements = 0;
7554 for (int i = objects.length() - 1; i >= 0; i--) {
7555 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007556 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007557 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007558
7559 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
7560 nof_elements = JSObject::kMaxElementCount;
7561 } else {
7562 nof_elements += encountered_elements;
7563 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007564 }
7565
7566 return nof_elements;
7567}
7568
7569
7570/**
7571 * A helper function of Runtime_ArrayConcat.
7572 *
7573 * The first argument is an Array of arrays and objects. It is the
7574 * same as the arguments array of Array::concat JS function.
7575 *
7576 * If an argument is an Array object, the function visits array
7577 * elements. If an argument is not an Array object, the function
7578 * visits the object as if it is an one-element array.
7579 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007580 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007581 * non-negative number is used as new length. For example, if one
7582 * array length is 2^32 - 1, second array length is 1, the
7583 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007584 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
7585 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007586 */
7587static uint32_t IterateArguments(Handle<JSArray> arguments,
7588 ArrayConcatVisitor* visitor) {
7589 uint32_t visited_elements = 0;
7590 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7591
7592 for (uint32_t i = 0; i < num_of_args; i++) {
7593 Handle<Object> obj(arguments->GetElement(i));
7594 if (obj->IsJSArray()) {
7595 Handle<JSArray> array = Handle<JSArray>::cast(obj);
7596 uint32_t len = static_cast<uint32_t>(array->length()->Number());
7597 uint32_t nof_elements =
7598 IterateArrayAndPrototypeElements(array, visitor);
7599 // Total elements of array and its prototype chain can be more than
7600 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007601 // the array length number of elements. We use the length as an estimate
7602 // for the actual number of elements added.
7603 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
7604 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
7605 visited_elements = JSArray::kMaxElementCount;
7606 } else {
7607 visited_elements += added_elements;
7608 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007609 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007610 } else {
7611 if (visitor) {
7612 visitor->visit(0, obj);
7613 visitor->increase_index_offset(1);
7614 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007615 if (visited_elements < JSArray::kMaxElementCount) {
7616 visited_elements++;
7617 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007618 }
7619 }
7620 return visited_elements;
7621}
7622
7623
7624/**
7625 * Array::concat implementation.
7626 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007627 * TODO(lrn): Fix non-compliance for very large concatenations and update to
7628 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007629 */
7630static Object* Runtime_ArrayConcat(Arguments args) {
7631 ASSERT(args.length() == 1);
7632 HandleScope handle_scope;
7633
7634 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
7635 Handle<JSArray> arguments(arg_arrays);
7636
7637 // Pass 1: estimate the number of elements of the result
7638 // (it could be more than real numbers if prototype has elements).
7639 uint32_t result_length = 0;
7640 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7641
7642 { AssertNoAllocation nogc;
7643 for (uint32_t i = 0; i < num_of_args; i++) {
7644 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007645 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007646 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007647 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007648 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
7649 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007650 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007651 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007652 if (JSObject::kMaxElementCount - result_length < length_estimate) {
7653 result_length = JSObject::kMaxElementCount;
7654 break;
7655 }
7656 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007657 }
7658 }
7659
7660 // Allocate an empty array, will set length and content later.
7661 Handle<JSArray> result = Factory::NewJSArray(0);
7662
7663 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
7664 // If estimated number of elements is more than half of length, a
7665 // fixed array (fast case) is more time and space-efficient than a
7666 // dictionary.
7667 bool fast_case = (estimate_nof_elements * 2) >= result_length;
7668
7669 Handle<FixedArray> storage;
7670 if (fast_case) {
7671 // The backing storage array must have non-existing elements to
7672 // preserve holes across concat operations.
7673 storage = Factory::NewFixedArrayWithHoles(result_length);
7674
7675 } else {
7676 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
7677 uint32_t at_least_space_for = estimate_nof_elements +
7678 (estimate_nof_elements >> 2);
7679 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007680 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007681 }
7682
7683 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
7684
7685 ArrayConcatVisitor visitor(storage, result_length, fast_case);
7686
7687 IterateArguments(arguments, &visitor);
7688
7689 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007690 // Please note the storage might have changed in the visitor.
7691 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007692
7693 return *result;
7694}
7695
7696
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007697// This will not allocate (flatten the string), but it may run
7698// very slowly for very deeply nested ConsStrings. For debugging use only.
7699static Object* Runtime_GlobalPrint(Arguments args) {
7700 NoHandleAllocation ha;
7701 ASSERT(args.length() == 1);
7702
7703 CONVERT_CHECKED(String, string, args[0]);
7704 StringInputBuffer buffer(string);
7705 while (buffer.has_more()) {
7706 uint16_t character = buffer.GetNext();
7707 PrintF("%c", character);
7708 }
7709 return string;
7710}
7711
ager@chromium.org5ec48922009-05-05 07:25:34 +00007712// Moves all own elements of an object, that are below a limit, to positions
7713// starting at zero. All undefined values are placed after non-undefined values,
7714// and are followed by non-existing element. Does not change the length
7715// property.
7716// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007717static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007718 ASSERT(args.length() == 2);
7719 CONVERT_CHECKED(JSObject, object, args[0]);
7720 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
7721 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007722}
7723
7724
7725// Move contents of argument 0 (an array) to argument 1 (an array)
7726static Object* Runtime_MoveArrayContents(Arguments args) {
7727 ASSERT(args.length() == 2);
7728 CONVERT_CHECKED(JSArray, from, args[0]);
7729 CONVERT_CHECKED(JSArray, to, args[1]);
7730 to->SetContent(FixedArray::cast(from->elements()));
7731 to->set_length(from->length());
7732 from->SetContent(Heap::empty_fixed_array());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007733 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007734 return to;
7735}
7736
7737
7738// How many elements does this array have?
7739static Object* Runtime_EstimateNumberOfElements(Arguments args) {
7740 ASSERT(args.length() == 1);
7741 CONVERT_CHECKED(JSArray, array, args[0]);
7742 HeapObject* elements = array->elements();
7743 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007744 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007745 } else {
7746 return array->length();
7747 }
7748}
7749
7750
7751// Returns an array that tells you where in the [0, length) interval an array
7752// might have elements. Can either return keys or intervals. Keys can have
7753// gaps in (undefined). Intervals can also span over some undefined keys.
7754static Object* Runtime_GetArrayKeys(Arguments args) {
7755 ASSERT(args.length() == 2);
7756 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007757 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007758 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007759 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007760 // Create an array and get all the keys into it, then remove all the
7761 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007762 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007763 int keys_length = keys->length();
7764 for (int i = 0; i < keys_length; i++) {
7765 Object* key = keys->get(i);
7766 uint32_t index;
7767 if (!Array::IndexFromObject(key, &index) || index >= length) {
7768 // Zap invalid keys.
7769 keys->set_undefined(i);
7770 }
7771 }
7772 return *Factory::NewJSArrayWithElements(keys);
7773 } else {
7774 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
7775 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007776 single_interval->set(0, Smi::FromInt(-1));
ager@chromium.org5ec48922009-05-05 07:25:34 +00007777 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
7778 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007779 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00007780 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007781 single_interval->set(1, *length_object);
7782 return *Factory::NewJSArrayWithElements(single_interval);
7783 }
7784}
7785
7786
7787// DefineAccessor takes an optional final argument which is the
7788// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
7789// to the way accessors are implemented, it is set for both the getter
7790// and setter on the first call to DefineAccessor and ignored on
7791// subsequent calls.
7792static Object* Runtime_DefineAccessor(Arguments args) {
7793 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
7794 // Compute attributes.
7795 PropertyAttributes attributes = NONE;
7796 if (args.length() == 5) {
7797 CONVERT_CHECKED(Smi, attrs, args[4]);
7798 int value = attrs->value();
7799 // Only attribute bits should be set.
7800 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
7801 attributes = static_cast<PropertyAttributes>(value);
7802 }
7803
7804 CONVERT_CHECKED(JSObject, obj, args[0]);
7805 CONVERT_CHECKED(String, name, args[1]);
7806 CONVERT_CHECKED(Smi, flag, args[2]);
7807 CONVERT_CHECKED(JSFunction, fun, args[3]);
7808 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
7809}
7810
7811
7812static Object* Runtime_LookupAccessor(Arguments args) {
7813 ASSERT(args.length() == 3);
7814 CONVERT_CHECKED(JSObject, obj, args[0]);
7815 CONVERT_CHECKED(String, name, args[1]);
7816 CONVERT_CHECKED(Smi, flag, args[2]);
7817 return obj->LookupAccessor(name, flag->value() == 0);
7818}
7819
7820
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007821#ifdef ENABLE_DEBUGGER_SUPPORT
7822static Object* Runtime_DebugBreak(Arguments args) {
7823 ASSERT(args.length() == 0);
7824 return Execution::DebugBreakHelper();
7825}
7826
7827
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007828// Helper functions for wrapping and unwrapping stack frame ids.
7829static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007830 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007831 return Smi::FromInt(id >> 2);
7832}
7833
7834
7835static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
7836 return static_cast<StackFrame::Id>(wrapped->value() << 2);
7837}
7838
7839
7840// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00007841// args[0]: debug event listener function to set or null or undefined for
7842// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007843// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00007844static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007845 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007846 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
7847 args[0]->IsUndefined() ||
7848 args[0]->IsNull());
7849 Handle<Object> callback = args.at<Object>(0);
7850 Handle<Object> data = args.at<Object>(1);
7851 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007852
7853 return Heap::undefined_value();
7854}
7855
7856
7857static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007858 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007859 StackGuard::DebugBreak();
7860 return Heap::undefined_value();
7861}
7862
7863
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007864static Object* DebugLookupResultValue(Object* receiver, String* name,
7865 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00007866 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007867 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007868 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007869 case NORMAL:
7870 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007871 if (value->IsTheHole()) {
7872 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007873 }
7874 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007875 case FIELD:
7876 value =
7877 JSObject::cast(
7878 result->holder())->FastPropertyAt(result->GetFieldIndex());
7879 if (value->IsTheHole()) {
7880 return Heap::undefined_value();
7881 }
7882 return value;
7883 case CONSTANT_FUNCTION:
7884 return result->GetConstantFunction();
7885 case CALLBACKS: {
7886 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007887 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00007888 value = receiver->GetPropertyWithCallback(
7889 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00007890 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007891 value = Top::pending_exception();
7892 Top::clear_pending_exception();
7893 if (caught_exception != NULL) {
7894 *caught_exception = true;
7895 }
7896 }
7897 return value;
7898 } else {
7899 return Heap::undefined_value();
7900 }
7901 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007902 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007903 case MAP_TRANSITION:
7904 case CONSTANT_TRANSITION:
7905 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007906 return Heap::undefined_value();
7907 default:
7908 UNREACHABLE();
7909 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007910 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007911 return Heap::undefined_value();
7912}
7913
7914
ager@chromium.org32912102009-01-16 10:38:43 +00007915// Get debugger related details for an object property.
7916// args[0]: object holding property
7917// args[1]: name of the property
7918//
7919// The array returned contains the following information:
7920// 0: Property value
7921// 1: Property details
7922// 2: Property value is exception
7923// 3: Getter function if defined
7924// 4: Setter function if defined
7925// Items 2-4 are only filled if the property has either a getter or a setter
7926// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007927static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007928 HandleScope scope;
7929
7930 ASSERT(args.length() == 2);
7931
7932 CONVERT_ARG_CHECKED(JSObject, obj, 0);
7933 CONVERT_ARG_CHECKED(String, name, 1);
7934
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00007935 // Make sure to set the current context to the context before the debugger was
7936 // entered (if the debugger is entered). The reason for switching context here
7937 // is that for some property lookups (accessors and interceptors) callbacks
7938 // into the embedding application can occour, and the embedding application
7939 // could have the assumption that its own global context is the current
7940 // context and not some internal debugger context.
7941 SaveContext save;
7942 if (Debug::InDebugger()) {
7943 Top::set_context(*Debug::debugger_entry()->GetContext());
7944 }
7945
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007946 // Skip the global proxy as it has no properties and always delegates to the
7947 // real global object.
7948 if (obj->IsJSGlobalProxy()) {
7949 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
7950 }
7951
7952
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007953 // Check if the name is trivially convertible to an index and get the element
7954 // if so.
7955 uint32_t index;
7956 if (name->AsArrayIndex(&index)) {
7957 Handle<FixedArray> details = Factory::NewFixedArray(2);
7958 details->set(0, Runtime::GetElementOrCharAt(obj, index));
7959 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
7960 return *Factory::NewJSArrayWithElements(details);
7961 }
7962
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007963 // Find the number of objects making up this.
7964 int length = LocalPrototypeChainLength(*obj);
7965
7966 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007967 Handle<JSObject> jsproto = obj;
7968 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007969 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007970 jsproto->LocalLookup(*name, &result);
7971 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007972 // LookupResult is not GC safe as it holds raw object pointers.
7973 // GC can happen later in this code so put the required fields into
7974 // local variables using handles when required for later use.
7975 PropertyType result_type = result.type();
7976 Handle<Object> result_callback_obj;
7977 if (result_type == CALLBACKS) {
7978 result_callback_obj = Handle<Object>(result.GetCallbackObject());
7979 }
7980 Smi* property_details = result.GetPropertyDetails().AsSmi();
7981 // DebugLookupResultValue can cause GC so details from LookupResult needs
7982 // to be copied to handles before this.
7983 bool caught_exception = false;
7984 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
7985 &caught_exception);
7986 if (raw_value->IsFailure()) return raw_value;
7987 Handle<Object> value(raw_value);
7988
7989 // If the callback object is a fixed array then it contains JavaScript
7990 // getter and/or setter.
7991 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
7992 result_callback_obj->IsFixedArray();
7993 Handle<FixedArray> details =
7994 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
7995 details->set(0, *value);
7996 details->set(1, property_details);
7997 if (hasJavaScriptAccessors) {
7998 details->set(2,
7999 caught_exception ? Heap::true_value()
8000 : Heap::false_value());
8001 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
8002 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
8003 }
8004
8005 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008006 }
8007 if (i < length - 1) {
8008 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
8009 }
8010 }
8011
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008012 return Heap::undefined_value();
8013}
8014
8015
8016static Object* Runtime_DebugGetProperty(Arguments args) {
8017 HandleScope scope;
8018
8019 ASSERT(args.length() == 2);
8020
8021 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8022 CONVERT_ARG_CHECKED(String, name, 1);
8023
8024 LookupResult result;
8025 obj->Lookup(*name, &result);
8026 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008027 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008028 }
8029 return Heap::undefined_value();
8030}
8031
8032
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008033// Return the property type calculated from the property details.
8034// args[0]: smi with property details.
8035static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
8036 ASSERT(args.length() == 1);
8037 CONVERT_CHECKED(Smi, details, args[0]);
8038 PropertyType type = PropertyDetails(details).type();
8039 return Smi::FromInt(static_cast<int>(type));
8040}
8041
8042
8043// Return the property attribute calculated from the property details.
8044// args[0]: smi with property details.
8045static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
8046 ASSERT(args.length() == 1);
8047 CONVERT_CHECKED(Smi, details, args[0]);
8048 PropertyAttributes attributes = PropertyDetails(details).attributes();
8049 return Smi::FromInt(static_cast<int>(attributes));
8050}
8051
8052
8053// Return the property insertion index calculated from the property details.
8054// args[0]: smi with property details.
8055static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
8056 ASSERT(args.length() == 1);
8057 CONVERT_CHECKED(Smi, details, args[0]);
8058 int index = PropertyDetails(details).index();
8059 return Smi::FromInt(index);
8060}
8061
8062
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008063// Return property value from named interceptor.
8064// args[0]: object
8065// args[1]: property name
8066static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
8067 HandleScope scope;
8068 ASSERT(args.length() == 2);
8069 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8070 RUNTIME_ASSERT(obj->HasNamedInterceptor());
8071 CONVERT_ARG_CHECKED(String, name, 1);
8072
8073 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008074 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008075}
8076
8077
8078// Return element value from indexed interceptor.
8079// args[0]: object
8080// args[1]: index
8081static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
8082 HandleScope scope;
8083 ASSERT(args.length() == 2);
8084 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8085 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
8086 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
8087
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008088 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008089}
8090
8091
8092static Object* Runtime_CheckExecutionState(Arguments args) {
8093 ASSERT(args.length() >= 1);
8094 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00008095 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008096 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008097 return Top::Throw(Heap::illegal_execution_state_symbol());
8098 }
8099
8100 return Heap::true_value();
8101}
8102
8103
8104static Object* Runtime_GetFrameCount(Arguments args) {
8105 HandleScope scope;
8106 ASSERT(args.length() == 1);
8107
8108 // Check arguments.
8109 Object* result = Runtime_CheckExecutionState(args);
8110 if (result->IsFailure()) return result;
8111
8112 // Count all frames which are relevant to debugging stack trace.
8113 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008114 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008115 if (id == StackFrame::NO_ID) {
8116 // If there is no JavaScript stack frame count is 0.
8117 return Smi::FromInt(0);
8118 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008119 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
8120 return Smi::FromInt(n);
8121}
8122
8123
8124static const int kFrameDetailsFrameIdIndex = 0;
8125static const int kFrameDetailsReceiverIndex = 1;
8126static const int kFrameDetailsFunctionIndex = 2;
8127static const int kFrameDetailsArgumentCountIndex = 3;
8128static const int kFrameDetailsLocalCountIndex = 4;
8129static const int kFrameDetailsSourcePositionIndex = 5;
8130static const int kFrameDetailsConstructCallIndex = 6;
8131static const int kFrameDetailsDebuggerFrameIndex = 7;
8132static const int kFrameDetailsFirstDynamicIndex = 8;
8133
8134// Return an array with frame details
8135// args[0]: number: break id
8136// args[1]: number: frame index
8137//
8138// The array returned contains the following information:
8139// 0: Frame id
8140// 1: Receiver
8141// 2: Function
8142// 3: Argument count
8143// 4: Local count
8144// 5: Source position
8145// 6: Constructor call
8146// 7: Debugger frame
8147// Arguments name, value
8148// Locals name, value
8149static Object* Runtime_GetFrameDetails(Arguments args) {
8150 HandleScope scope;
8151 ASSERT(args.length() == 2);
8152
8153 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008154 Object* check = Runtime_CheckExecutionState(args);
8155 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008156 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8157
8158 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008159 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008160 if (id == StackFrame::NO_ID) {
8161 // If there are no JavaScript stack frames return undefined.
8162 return Heap::undefined_value();
8163 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008164 int count = 0;
8165 JavaScriptFrameIterator it(id);
8166 for (; !it.done(); it.Advance()) {
8167 if (count == index) break;
8168 count++;
8169 }
8170 if (it.done()) return Heap::undefined_value();
8171
8172 // Traverse the saved contexts chain to find the active context for the
8173 // selected frame.
8174 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008175 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008176 save = save->prev();
8177 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008178 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008179
8180 // Get the frame id.
8181 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
8182
8183 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008184 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008185
8186 // Check for constructor frame.
8187 bool constructor = it.frame()->IsConstructor();
8188
8189 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008190 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008191 ScopeInfo<> info(*code);
8192
8193 // Get the context.
8194 Handle<Context> context(Context::cast(it.frame()->context()));
8195
8196 // Get the locals names and values into a temporary array.
8197 //
8198 // TODO(1240907): Hide compiler-introduced stack variables
8199 // (e.g. .result)? For users of the debugger, they will probably be
8200 // confusing.
8201 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
8202 for (int i = 0; i < info.NumberOfLocals(); i++) {
8203 // Name of the local.
8204 locals->set(i * 2, *info.LocalName(i));
8205
8206 // Fetch the value of the local - either from the stack or from a
8207 // heap-allocated context.
8208 if (i < info.number_of_stack_slots()) {
8209 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
8210 } else {
8211 Handle<String> name = info.LocalName(i);
8212 // Traverse the context chain to the function context as all local
8213 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008214 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008215 context = Handle<Context>(context->previous());
8216 }
8217 ASSERT(context->is_function_context());
8218 locals->set(i * 2 + 1,
8219 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
8220 NULL)));
8221 }
8222 }
8223
8224 // Now advance to the arguments adapter frame (if any). If contains all
8225 // the provided parameters and
8226
8227 // Now advance to the arguments adapter frame (if any). It contains all
8228 // the provided parameters whereas the function frame always have the number
8229 // of arguments matching the functions parameters. The rest of the
8230 // information (except for what is collected above) is the same.
8231 it.AdvanceToArgumentsFrame();
8232
8233 // Find the number of arguments to fill. At least fill the number of
8234 // parameters for the function and fill more if more parameters are provided.
8235 int argument_count = info.number_of_parameters();
8236 if (argument_count < it.frame()->GetProvidedParametersCount()) {
8237 argument_count = it.frame()->GetProvidedParametersCount();
8238 }
8239
8240 // Calculate the size of the result.
8241 int details_size = kFrameDetailsFirstDynamicIndex +
8242 2 * (argument_count + info.NumberOfLocals());
8243 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8244
8245 // Add the frame id.
8246 details->set(kFrameDetailsFrameIdIndex, *frame_id);
8247
8248 // Add the function (same as in function frame).
8249 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
8250
8251 // Add the arguments count.
8252 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
8253
8254 // Add the locals count
8255 details->set(kFrameDetailsLocalCountIndex,
8256 Smi::FromInt(info.NumberOfLocals()));
8257
8258 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00008259 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008260 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
8261 } else {
8262 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
8263 }
8264
8265 // Add the constructor information.
8266 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
8267
8268 // Add information on whether this frame is invoked in the debugger context.
8269 details->set(kFrameDetailsDebuggerFrameIndex,
8270 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
8271
8272 // Fill the dynamic part.
8273 int details_index = kFrameDetailsFirstDynamicIndex;
8274
8275 // Add arguments name and value.
8276 for (int i = 0; i < argument_count; i++) {
8277 // Name of the argument.
8278 if (i < info.number_of_parameters()) {
8279 details->set(details_index++, *info.parameter_name(i));
8280 } else {
8281 details->set(details_index++, Heap::undefined_value());
8282 }
8283
8284 // Parameter value.
8285 if (i < it.frame()->GetProvidedParametersCount()) {
8286 details->set(details_index++, it.frame()->GetParameter(i));
8287 } else {
8288 details->set(details_index++, Heap::undefined_value());
8289 }
8290 }
8291
8292 // Add locals name and value from the temporary copy from the function frame.
8293 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
8294 details->set(details_index++, locals->get(i));
8295 }
8296
8297 // Add the receiver (same as in function frame).
8298 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
8299 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
8300 Handle<Object> receiver(it.frame()->receiver());
8301 if (!receiver->IsJSObject()) {
8302 // If the receiver is NOT a JSObject we have hit an optimization
8303 // where a value object is not converted into a wrapped JS objects.
8304 // To hide this optimization from the debugger, we wrap the receiver
8305 // by creating correct wrapper object based on the calling frame's
8306 // global context.
8307 it.Advance();
8308 Handle<Context> calling_frames_global_context(
8309 Context::cast(Context::cast(it.frame()->context())->global_context()));
8310 receiver = Factory::ToObject(receiver, calling_frames_global_context);
8311 }
8312 details->set(kFrameDetailsReceiverIndex, *receiver);
8313
8314 ASSERT_EQ(details_size, details_index);
8315 return *Factory::NewJSArrayWithElements(details);
8316}
8317
8318
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008319// Copy all the context locals into an object used to materialize a scope.
8320static void CopyContextLocalsToScopeObject(Handle<Code> code,
8321 ScopeInfo<>& scope_info,
8322 Handle<Context> context,
8323 Handle<JSObject> scope_object) {
8324 // Fill all context locals to the context extension.
8325 for (int i = Context::MIN_CONTEXT_SLOTS;
8326 i < scope_info.number_of_context_slots();
8327 i++) {
8328 int context_index =
8329 ScopeInfo<>::ContextSlotIndex(*code,
8330 *scope_info.context_slot_name(i),
8331 NULL);
8332
8333 // Don't include the arguments shadow (.arguments) context variable.
8334 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
8335 SetProperty(scope_object,
8336 scope_info.context_slot_name(i),
8337 Handle<Object>(context->get(context_index)), NONE);
8338 }
8339 }
8340}
8341
8342
8343// Create a plain JSObject which materializes the local scope for the specified
8344// frame.
8345static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
8346 Handle<JSFunction> function(JSFunction::cast(frame->function()));
8347 Handle<Code> code(function->code());
8348 ScopeInfo<> scope_info(*code);
8349
8350 // Allocate and initialize a JSObject with all the arguments, stack locals
8351 // heap locals and extension properties of the debugged function.
8352 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
8353
8354 // First fill all parameters.
8355 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8356 SetProperty(local_scope,
8357 scope_info.parameter_name(i),
8358 Handle<Object>(frame->GetParameter(i)), NONE);
8359 }
8360
8361 // Second fill all stack locals.
8362 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
8363 SetProperty(local_scope,
8364 scope_info.stack_slot_name(i),
8365 Handle<Object>(frame->GetExpression(i)), NONE);
8366 }
8367
8368 // Third fill all context locals.
8369 Handle<Context> frame_context(Context::cast(frame->context()));
8370 Handle<Context> function_context(frame_context->fcontext());
8371 CopyContextLocalsToScopeObject(code, scope_info,
8372 function_context, local_scope);
8373
8374 // Finally copy any properties from the function context extension. This will
8375 // be variables introduced by eval.
8376 if (function_context->closure() == *function) {
8377 if (function_context->has_extension() &&
8378 !function_context->IsGlobalContext()) {
8379 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008380 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008381 for (int i = 0; i < keys->length(); i++) {
8382 // Names of variables introduced by eval are strings.
8383 ASSERT(keys->get(i)->IsString());
8384 Handle<String> key(String::cast(keys->get(i)));
8385 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
8386 }
8387 }
8388 }
8389 return local_scope;
8390}
8391
8392
8393// Create a plain JSObject which materializes the closure content for the
8394// context.
8395static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
8396 ASSERT(context->is_function_context());
8397
8398 Handle<Code> code(context->closure()->code());
8399 ScopeInfo<> scope_info(*code);
8400
8401 // Allocate and initialize a JSObject with all the content of theis function
8402 // closure.
8403 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
8404
8405 // Check whether the arguments shadow object exists.
8406 int arguments_shadow_index =
8407 ScopeInfo<>::ContextSlotIndex(*code,
8408 Heap::arguments_shadow_symbol(),
8409 NULL);
8410 if (arguments_shadow_index >= 0) {
8411 // In this case all the arguments are available in the arguments shadow
8412 // object.
8413 Handle<JSObject> arguments_shadow(
8414 JSObject::cast(context->get(arguments_shadow_index)));
8415 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8416 SetProperty(closure_scope,
8417 scope_info.parameter_name(i),
8418 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
8419 }
8420 }
8421
8422 // Fill all context locals to the context extension.
8423 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
8424
8425 // Finally copy any properties from the function context extension. This will
8426 // be variables introduced by eval.
8427 if (context->has_extension()) {
8428 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008429 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008430 for (int i = 0; i < keys->length(); i++) {
8431 // Names of variables introduced by eval are strings.
8432 ASSERT(keys->get(i)->IsString());
8433 Handle<String> key(String::cast(keys->get(i)));
8434 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
8435 }
8436 }
8437
8438 return closure_scope;
8439}
8440
8441
8442// Iterate over the actual scopes visible from a stack frame. All scopes are
8443// backed by an actual context except the local scope, which is inserted
8444// "artifically" in the context chain.
8445class ScopeIterator {
8446 public:
8447 enum ScopeType {
8448 ScopeTypeGlobal = 0,
8449 ScopeTypeLocal,
8450 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00008451 ScopeTypeClosure,
8452 // Every catch block contains an implicit with block (its parameter is
8453 // a JSContextExtensionObject) that extends current scope with a variable
8454 // holding exception object. Such with blocks are treated as scopes of their
8455 // own type.
8456 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008457 };
8458
8459 explicit ScopeIterator(JavaScriptFrame* frame)
8460 : frame_(frame),
8461 function_(JSFunction::cast(frame->function())),
8462 context_(Context::cast(frame->context())),
8463 local_done_(false),
8464 at_local_(false) {
8465
8466 // Check whether the first scope is actually a local scope.
8467 if (context_->IsGlobalContext()) {
8468 // If there is a stack slot for .result then this local scope has been
8469 // created for evaluating top level code and it is not a real local scope.
8470 // Checking for the existence of .result seems fragile, but the scope info
8471 // saved with the code object does not otherwise have that information.
8472 Handle<Code> code(function_->code());
8473 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
8474 at_local_ = index < 0;
8475 } else if (context_->is_function_context()) {
8476 at_local_ = true;
8477 }
8478 }
8479
8480 // More scopes?
8481 bool Done() { return context_.is_null(); }
8482
8483 // Move to the next scope.
8484 void Next() {
8485 // If at a local scope mark the local scope as passed.
8486 if (at_local_) {
8487 at_local_ = false;
8488 local_done_ = true;
8489
8490 // If the current context is not associated with the local scope the
8491 // current context is the next real scope, so don't move to the next
8492 // context in this case.
8493 if (context_->closure() != *function_) {
8494 return;
8495 }
8496 }
8497
8498 // The global scope is always the last in the chain.
8499 if (context_->IsGlobalContext()) {
8500 context_ = Handle<Context>();
8501 return;
8502 }
8503
8504 // Move to the next context.
8505 if (context_->is_function_context()) {
8506 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
8507 } else {
8508 context_ = Handle<Context>(context_->previous());
8509 }
8510
8511 // If passing the local scope indicate that the current scope is now the
8512 // local scope.
8513 if (!local_done_ &&
8514 (context_->IsGlobalContext() || (context_->is_function_context()))) {
8515 at_local_ = true;
8516 }
8517 }
8518
8519 // Return the type of the current scope.
8520 int Type() {
8521 if (at_local_) {
8522 return ScopeTypeLocal;
8523 }
8524 if (context_->IsGlobalContext()) {
8525 ASSERT(context_->global()->IsGlobalObject());
8526 return ScopeTypeGlobal;
8527 }
8528 if (context_->is_function_context()) {
8529 return ScopeTypeClosure;
8530 }
8531 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00008532 // Current scope is either an explicit with statement or a with statement
8533 // implicitely generated for a catch block.
8534 // If the extension object here is a JSContextExtensionObject then
8535 // current with statement is one frome a catch block otherwise it's a
8536 // regular with statement.
8537 if (context_->extension()->IsJSContextExtensionObject()) {
8538 return ScopeTypeCatch;
8539 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008540 return ScopeTypeWith;
8541 }
8542
8543 // Return the JavaScript object with the content of the current scope.
8544 Handle<JSObject> ScopeObject() {
8545 switch (Type()) {
8546 case ScopeIterator::ScopeTypeGlobal:
8547 return Handle<JSObject>(CurrentContext()->global());
8548 break;
8549 case ScopeIterator::ScopeTypeLocal:
8550 // Materialize the content of the local scope into a JSObject.
8551 return MaterializeLocalScope(frame_);
8552 break;
8553 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00008554 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008555 // Return the with object.
8556 return Handle<JSObject>(CurrentContext()->extension());
8557 break;
8558 case ScopeIterator::ScopeTypeClosure:
8559 // Materialize the content of the closure scope into a JSObject.
8560 return MaterializeClosure(CurrentContext());
8561 break;
8562 }
8563 UNREACHABLE();
8564 return Handle<JSObject>();
8565 }
8566
8567 // Return the context for this scope. For the local context there might not
8568 // be an actual context.
8569 Handle<Context> CurrentContext() {
8570 if (at_local_ && context_->closure() != *function_) {
8571 return Handle<Context>();
8572 }
8573 return context_;
8574 }
8575
8576#ifdef DEBUG
8577 // Debug print of the content of the current scope.
8578 void DebugPrint() {
8579 switch (Type()) {
8580 case ScopeIterator::ScopeTypeGlobal:
8581 PrintF("Global:\n");
8582 CurrentContext()->Print();
8583 break;
8584
8585 case ScopeIterator::ScopeTypeLocal: {
8586 PrintF("Local:\n");
8587 Handle<Code> code(function_->code());
8588 ScopeInfo<> scope_info(*code);
8589 scope_info.Print();
8590 if (!CurrentContext().is_null()) {
8591 CurrentContext()->Print();
8592 if (CurrentContext()->has_extension()) {
8593 Handle<JSObject> extension =
8594 Handle<JSObject>(CurrentContext()->extension());
8595 if (extension->IsJSContextExtensionObject()) {
8596 extension->Print();
8597 }
8598 }
8599 }
8600 break;
8601 }
8602
8603 case ScopeIterator::ScopeTypeWith: {
8604 PrintF("With:\n");
8605 Handle<JSObject> extension =
8606 Handle<JSObject>(CurrentContext()->extension());
8607 extension->Print();
8608 break;
8609 }
8610
ager@chromium.orga1645e22009-09-09 19:27:10 +00008611 case ScopeIterator::ScopeTypeCatch: {
8612 PrintF("Catch:\n");
8613 Handle<JSObject> extension =
8614 Handle<JSObject>(CurrentContext()->extension());
8615 extension->Print();
8616 break;
8617 }
8618
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008619 case ScopeIterator::ScopeTypeClosure: {
8620 PrintF("Closure:\n");
8621 CurrentContext()->Print();
8622 if (CurrentContext()->has_extension()) {
8623 Handle<JSObject> extension =
8624 Handle<JSObject>(CurrentContext()->extension());
8625 if (extension->IsJSContextExtensionObject()) {
8626 extension->Print();
8627 }
8628 }
8629 break;
8630 }
8631
8632 default:
8633 UNREACHABLE();
8634 }
8635 PrintF("\n");
8636 }
8637#endif
8638
8639 private:
8640 JavaScriptFrame* frame_;
8641 Handle<JSFunction> function_;
8642 Handle<Context> context_;
8643 bool local_done_;
8644 bool at_local_;
8645
8646 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
8647};
8648
8649
8650static Object* Runtime_GetScopeCount(Arguments args) {
8651 HandleScope scope;
8652 ASSERT(args.length() == 2);
8653
8654 // Check arguments.
8655 Object* check = Runtime_CheckExecutionState(args);
8656 if (check->IsFailure()) return check;
8657 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8658
8659 // Get the frame where the debugging is performed.
8660 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8661 JavaScriptFrameIterator it(id);
8662 JavaScriptFrame* frame = it.frame();
8663
8664 // Count the visible scopes.
8665 int n = 0;
8666 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8667 n++;
8668 }
8669
8670 return Smi::FromInt(n);
8671}
8672
8673
8674static const int kScopeDetailsTypeIndex = 0;
8675static const int kScopeDetailsObjectIndex = 1;
8676static const int kScopeDetailsSize = 2;
8677
8678// Return an array with scope details
8679// args[0]: number: break id
8680// args[1]: number: frame index
8681// args[2]: number: scope index
8682//
8683// The array returned contains the following information:
8684// 0: Scope type
8685// 1: Scope object
8686static Object* Runtime_GetScopeDetails(Arguments args) {
8687 HandleScope scope;
8688 ASSERT(args.length() == 3);
8689
8690 // Check arguments.
8691 Object* check = Runtime_CheckExecutionState(args);
8692 if (check->IsFailure()) return check;
8693 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8694 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
8695
8696 // Get the frame where the debugging is performed.
8697 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8698 JavaScriptFrameIterator frame_it(id);
8699 JavaScriptFrame* frame = frame_it.frame();
8700
8701 // Find the requested scope.
8702 int n = 0;
8703 ScopeIterator it(frame);
8704 for (; !it.Done() && n < index; it.Next()) {
8705 n++;
8706 }
8707 if (it.Done()) {
8708 return Heap::undefined_value();
8709 }
8710
8711 // Calculate the size of the result.
8712 int details_size = kScopeDetailsSize;
8713 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8714
8715 // Fill in scope details.
8716 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
8717 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
8718
8719 return *Factory::NewJSArrayWithElements(details);
8720}
8721
8722
8723static Object* Runtime_DebugPrintScopes(Arguments args) {
8724 HandleScope scope;
8725 ASSERT(args.length() == 0);
8726
8727#ifdef DEBUG
8728 // Print the scopes for the top frame.
8729 StackFrameLocator locator;
8730 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
8731 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8732 it.DebugPrint();
8733 }
8734#endif
8735 return Heap::undefined_value();
8736}
8737
8738
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008739static Object* Runtime_GetCFrames(Arguments args) {
8740 HandleScope scope;
8741 ASSERT(args.length() == 1);
8742 Object* result = Runtime_CheckExecutionState(args);
8743 if (result->IsFailure()) return result;
8744
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008745#if V8_HOST_ARCH_64_BIT
8746 UNIMPLEMENTED();
8747 return Heap::undefined_value();
8748#else
8749
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008750 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008751 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
8752 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008753 if (frames_count == OS::kStackWalkError) {
8754 return Heap::undefined_value();
8755 }
8756
8757 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
8758 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
8759 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
8760 for (int i = 0; i < frames_count; i++) {
8761 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
8762 frame_value->SetProperty(
8763 *address_str,
8764 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
8765 NONE);
8766
8767 // Get the stack walk text for this frame.
8768 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008769 int frame_text_length = StrLength(frames[i].text);
8770 if (frame_text_length > 0) {
8771 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008772 frame_text = Factory::NewStringFromAscii(str);
8773 }
8774
8775 if (!frame_text.is_null()) {
8776 frame_value->SetProperty(*text_str, *frame_text, NONE);
8777 }
8778
8779 frames_array->set(i, *frame_value);
8780 }
8781 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008782#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008783}
8784
8785
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00008786static Object* Runtime_GetThreadCount(Arguments args) {
8787 HandleScope scope;
8788 ASSERT(args.length() == 1);
8789
8790 // Check arguments.
8791 Object* result = Runtime_CheckExecutionState(args);
8792 if (result->IsFailure()) return result;
8793
8794 // Count all archived V8 threads.
8795 int n = 0;
8796 for (ThreadState* thread = ThreadState::FirstInUse();
8797 thread != NULL;
8798 thread = thread->Next()) {
8799 n++;
8800 }
8801
8802 // Total number of threads is current thread and archived threads.
8803 return Smi::FromInt(n + 1);
8804}
8805
8806
8807static const int kThreadDetailsCurrentThreadIndex = 0;
8808static const int kThreadDetailsThreadIdIndex = 1;
8809static const int kThreadDetailsSize = 2;
8810
8811// Return an array with thread details
8812// args[0]: number: break id
8813// args[1]: number: thread index
8814//
8815// The array returned contains the following information:
8816// 0: Is current thread?
8817// 1: Thread id
8818static Object* Runtime_GetThreadDetails(Arguments args) {
8819 HandleScope scope;
8820 ASSERT(args.length() == 2);
8821
8822 // Check arguments.
8823 Object* check = Runtime_CheckExecutionState(args);
8824 if (check->IsFailure()) return check;
8825 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8826
8827 // Allocate array for result.
8828 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
8829
8830 // Thread index 0 is current thread.
8831 if (index == 0) {
8832 // Fill the details.
8833 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
8834 details->set(kThreadDetailsThreadIdIndex,
8835 Smi::FromInt(ThreadManager::CurrentId()));
8836 } else {
8837 // Find the thread with the requested index.
8838 int n = 1;
8839 ThreadState* thread = ThreadState::FirstInUse();
8840 while (index != n && thread != NULL) {
8841 thread = thread->Next();
8842 n++;
8843 }
8844 if (thread == NULL) {
8845 return Heap::undefined_value();
8846 }
8847
8848 // Fill the details.
8849 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
8850 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
8851 }
8852
8853 // Convert to JS array and return.
8854 return *Factory::NewJSArrayWithElements(details);
8855}
8856
8857
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008858static Object* Runtime_GetBreakLocations(Arguments args) {
8859 HandleScope scope;
8860 ASSERT(args.length() == 1);
8861
ager@chromium.org5aa501c2009-06-23 07:57:28 +00008862 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
8863 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008864 // Find the number of break points
8865 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
8866 if (break_locations->IsUndefined()) return Heap::undefined_value();
8867 // Return array as JS array
8868 return *Factory::NewJSArrayWithElements(
8869 Handle<FixedArray>::cast(break_locations));
8870}
8871
8872
8873// Set a break point in a function
8874// args[0]: function
8875// args[1]: number: break source position (within the function source)
8876// args[2]: number: break point object
8877static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
8878 HandleScope scope;
8879 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00008880 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
8881 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008882 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
8883 RUNTIME_ASSERT(source_position >= 0);
8884 Handle<Object> break_point_object_arg = args.at<Object>(2);
8885
8886 // Set break point.
8887 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
8888
8889 return Heap::undefined_value();
8890}
8891
8892
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00008893Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
8894 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008895 // Iterate the heap looking for SharedFunctionInfo generated from the
8896 // script. The inner most SharedFunctionInfo containing the source position
8897 // for the requested break point is found.
8898 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
8899 // which is found is not compiled it is compiled and the heap is iterated
8900 // again as the compilation might create inner functions from the newly
8901 // compiled function and the actual requested break point might be in one of
8902 // these functions.
8903 bool done = false;
8904 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00008905 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008906 Handle<SharedFunctionInfo> target;
8907 // The current candidate for the last function in script:
8908 Handle<SharedFunctionInfo> last;
8909 while (!done) {
8910 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008911 for (HeapObject* obj = iterator.next();
8912 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008913 if (obj->IsSharedFunctionInfo()) {
8914 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
8915 if (shared->script() == *script) {
8916 // If the SharedFunctionInfo found has the requested script data and
8917 // contains the source position it is a candidate.
8918 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00008919 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008920 start_position = shared->start_position();
8921 }
8922 if (start_position <= position &&
8923 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00008924 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008925 // candidate this is the new candidate.
8926 if (target.is_null()) {
8927 target_start_position = start_position;
8928 target = shared;
8929 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00008930 if (target_start_position == start_position &&
8931 shared->end_position() == target->end_position()) {
8932 // If a top-level function contain only one function
8933 // declartion the source for the top-level and the function is
8934 // the same. In that case prefer the non top-level function.
8935 if (!shared->is_toplevel()) {
8936 target_start_position = start_position;
8937 target = shared;
8938 }
8939 } else if (target_start_position <= start_position &&
8940 shared->end_position() <= target->end_position()) {
8941 // This containment check includes equality as a function inside
8942 // a top-level function can share either start or end position
8943 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008944 target_start_position = start_position;
8945 target = shared;
8946 }
8947 }
8948 }
8949
8950 // Keep track of the last function in the script.
8951 if (last.is_null() ||
8952 shared->end_position() > last->start_position()) {
8953 last = shared;
8954 }
8955 }
8956 }
8957 }
8958
8959 // Make sure some candidate is selected.
8960 if (target.is_null()) {
8961 if (!last.is_null()) {
8962 // Position after the last function - use last.
8963 target = last;
8964 } else {
8965 // Unable to find function - possibly script without any function.
8966 return Heap::undefined_value();
8967 }
8968 }
8969
8970 // If the candidate found is compiled we are done. NOTE: when lazy
8971 // compilation of inner functions is introduced some additional checking
8972 // needs to be done here to compile inner functions.
8973 done = target->is_compiled();
8974 if (!done) {
8975 // If the candidate is not compiled compile it to reveal any inner
8976 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008977 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008978 }
8979 }
8980
8981 return *target;
8982}
8983
8984
8985// Change the state of a break point in a script. NOTE: Regarding performance
8986// see the NOTE for GetScriptFromScriptData.
8987// args[0]: script to set break point in
8988// args[1]: number: break source position (within the script source)
8989// args[2]: number: break point object
8990static Object* Runtime_SetScriptBreakPoint(Arguments args) {
8991 HandleScope scope;
8992 ASSERT(args.length() == 3);
8993 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
8994 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
8995 RUNTIME_ASSERT(source_position >= 0);
8996 Handle<Object> break_point_object_arg = args.at<Object>(2);
8997
8998 // Get the script from the script wrapper.
8999 RUNTIME_ASSERT(wrapper->value()->IsScript());
9000 Handle<Script> script(Script::cast(wrapper->value()));
9001
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009002 Object* result = Runtime::FindSharedFunctionInfoInScript(
9003 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009004 if (!result->IsUndefined()) {
9005 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
9006 // Find position within function. The script position might be before the
9007 // source position of the first function.
9008 int position;
9009 if (shared->start_position() > source_position) {
9010 position = 0;
9011 } else {
9012 position = source_position - shared->start_position();
9013 }
9014 Debug::SetBreakPoint(shared, position, break_point_object_arg);
9015 }
9016 return Heap::undefined_value();
9017}
9018
9019
9020// Clear a break point
9021// args[0]: number: break point object
9022static Object* Runtime_ClearBreakPoint(Arguments args) {
9023 HandleScope scope;
9024 ASSERT(args.length() == 1);
9025 Handle<Object> break_point_object_arg = args.at<Object>(0);
9026
9027 // Clear break point.
9028 Debug::ClearBreakPoint(break_point_object_arg);
9029
9030 return Heap::undefined_value();
9031}
9032
9033
9034// Change the state of break on exceptions
9035// args[0]: boolean indicating uncaught exceptions
9036// args[1]: boolean indicating on/off
9037static Object* Runtime_ChangeBreakOnException(Arguments args) {
9038 HandleScope scope;
9039 ASSERT(args.length() == 2);
9040 ASSERT(args[0]->IsNumber());
9041 ASSERT(args[1]->IsBoolean());
9042
9043 // Update break point state
9044 ExceptionBreakType type =
9045 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
9046 bool enable = args[1]->ToBoolean()->IsTrue();
9047 Debug::ChangeBreakOnException(type, enable);
9048 return Heap::undefined_value();
9049}
9050
9051
9052// Prepare for stepping
9053// args[0]: break id for checking execution state
9054// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00009055// args[2]: number of times to perform the step, for step out it is the number
9056// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009057static Object* Runtime_PrepareStep(Arguments args) {
9058 HandleScope scope;
9059 ASSERT(args.length() == 3);
9060 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009061 Object* check = Runtime_CheckExecutionState(args);
9062 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009063 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
9064 return Top::Throw(Heap::illegal_argument_symbol());
9065 }
9066
9067 // Get the step action and check validity.
9068 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
9069 if (step_action != StepIn &&
9070 step_action != StepNext &&
9071 step_action != StepOut &&
9072 step_action != StepInMin &&
9073 step_action != StepMin) {
9074 return Top::Throw(Heap::illegal_argument_symbol());
9075 }
9076
9077 // Get the number of steps.
9078 int step_count = NumberToInt32(args[2]);
9079 if (step_count < 1) {
9080 return Top::Throw(Heap::illegal_argument_symbol());
9081 }
9082
ager@chromium.orga1645e22009-09-09 19:27:10 +00009083 // Clear all current stepping setup.
9084 Debug::ClearStepping();
9085
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009086 // Prepare step.
9087 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
9088 return Heap::undefined_value();
9089}
9090
9091
9092// Clear all stepping set by PrepareStep.
9093static Object* Runtime_ClearStepping(Arguments args) {
9094 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009095 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009096 Debug::ClearStepping();
9097 return Heap::undefined_value();
9098}
9099
9100
9101// Creates a copy of the with context chain. The copy of the context chain is
9102// is linked to the function context supplied.
9103static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
9104 Handle<Context> function_context) {
9105 // At the bottom of the chain. Return the function context to link to.
9106 if (context_chain->is_function_context()) {
9107 return function_context;
9108 }
9109
9110 // Recursively copy the with contexts.
9111 Handle<Context> previous(context_chain->previous());
9112 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
9113 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009114 CopyWithContextChain(function_context, previous),
9115 extension,
9116 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009117}
9118
9119
9120// Helper function to find or create the arguments object for
9121// Runtime_DebugEvaluate.
9122static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
9123 Handle<JSFunction> function,
9124 Handle<Code> code,
9125 const ScopeInfo<>* sinfo,
9126 Handle<Context> function_context) {
9127 // Try to find the value of 'arguments' to pass as parameter. If it is not
9128 // found (that is the debugged function does not reference 'arguments' and
9129 // does not support eval) then create an 'arguments' object.
9130 int index;
9131 if (sinfo->number_of_stack_slots() > 0) {
9132 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
9133 if (index != -1) {
9134 return Handle<Object>(frame->GetExpression(index));
9135 }
9136 }
9137
9138 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
9139 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
9140 NULL);
9141 if (index != -1) {
9142 return Handle<Object>(function_context->get(index));
9143 }
9144 }
9145
9146 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009147 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
9148 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009149
9150 AssertNoAllocation no_gc;
9151 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009152 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009153 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009154 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009155 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009156 return arguments;
9157}
9158
9159
9160// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00009161// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009162// extension part has all the parameters and locals of the function on the
9163// stack frame. A function which calls eval with the code to evaluate is then
9164// compiled in this context and called in this context. As this context
9165// replaces the context of the function on the stack frame a new (empty)
9166// function is created as well to be used as the closure for the context.
9167// This function and the context acts as replacements for the function on the
9168// stack frame presenting the same view of the values of parameters and
9169// local variables as if the piece of JavaScript was evaluated at the point
9170// where the function on the stack frame is currently stopped.
9171static Object* Runtime_DebugEvaluate(Arguments args) {
9172 HandleScope scope;
9173
9174 // Check the execution state and decode arguments frame and source to be
9175 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009176 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009177 Object* check_result = Runtime_CheckExecutionState(args);
9178 if (check_result->IsFailure()) return check_result;
9179 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9180 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009181 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
9182
9183 // Handle the processing of break.
9184 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009185
9186 // Get the frame where the debugging is performed.
9187 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9188 JavaScriptFrameIterator it(id);
9189 JavaScriptFrame* frame = it.frame();
9190 Handle<JSFunction> function(JSFunction::cast(frame->function()));
9191 Handle<Code> code(function->code());
9192 ScopeInfo<> sinfo(*code);
9193
9194 // Traverse the saved contexts chain to find the active context for the
9195 // selected frame.
9196 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009197 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009198 save = save->prev();
9199 }
9200 ASSERT(save != NULL);
9201 SaveContext savex;
9202 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009203
9204 // Create the (empty) function replacing the function on the stack frame for
9205 // the purpose of evaluating in the context created below. It is important
9206 // that this function does not describe any parameters and local variables
9207 // in the context. If it does then this will cause problems with the lookup
9208 // in Context::Lookup, where context slots for parameters and local variables
9209 // are looked at before the extension object.
9210 Handle<JSFunction> go_between =
9211 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
9212 go_between->set_context(function->context());
9213#ifdef DEBUG
9214 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
9215 ASSERT(go_between_sinfo.number_of_parameters() == 0);
9216 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
9217#endif
9218
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009219 // Materialize the content of the local scope into a JSObject.
9220 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009221
9222 // Allocate a new context for the debug evaluation and set the extension
9223 // object build.
9224 Handle<Context> context =
9225 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009226 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009227 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009228 Handle<Context> frame_context(Context::cast(frame->context()));
9229 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009230 context = CopyWithContextChain(frame_context, context);
9231
9232 // Wrap the evaluation statement in a new function compiled in the newly
9233 // created context. The function has one parameter which has to be called
9234 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00009235 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009236 // function(arguments,__source__) {return eval(__source__);}
9237 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00009238 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009239 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009240 Handle<String> function_source =
9241 Factory::NewStringFromAscii(Vector<const char>(source_str,
9242 source_str_length));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009243 Handle<SharedFunctionInfo> shared =
ager@chromium.org381abbb2009-02-25 13:23:22 +00009244 Compiler::CompileEval(function_source,
9245 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00009246 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00009247 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009248 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009249 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009250 Factory::NewFunctionFromSharedFunctionInfo(shared, context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009251
9252 // Invoke the result of the compilation to get the evaluation function.
9253 bool has_pending_exception;
9254 Handle<Object> receiver(frame->receiver());
9255 Handle<Object> evaluation_function =
9256 Execution::Call(compiled_function, receiver, 0, NULL,
9257 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009258 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009259
9260 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
9261 function_context);
9262
9263 // Invoke the evaluation function and return the result.
9264 const int argc = 2;
9265 Object** argv[argc] = { arguments.location(),
9266 Handle<Object>::cast(source).location() };
9267 Handle<Object> result =
9268 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
9269 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009270 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009271
9272 // Skip the global proxy as it has no properties and always delegates to the
9273 // real global object.
9274 if (result->IsJSGlobalProxy()) {
9275 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
9276 }
9277
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009278 return *result;
9279}
9280
9281
9282static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
9283 HandleScope scope;
9284
9285 // Check the execution state and decode arguments frame and source to be
9286 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009287 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009288 Object* check_result = Runtime_CheckExecutionState(args);
9289 if (check_result->IsFailure()) return check_result;
9290 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009291 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
9292
9293 // Handle the processing of break.
9294 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009295
9296 // Enter the top context from before the debugger was invoked.
9297 SaveContext save;
9298 SaveContext* top = &save;
9299 while (top != NULL && *top->context() == *Debug::debug_context()) {
9300 top = top->prev();
9301 }
9302 if (top != NULL) {
9303 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009304 }
9305
9306 // Get the global context now set to the top context from before the
9307 // debugger was invoked.
9308 Handle<Context> context = Top::global_context();
9309
9310 // Compile the source to be evaluated.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009311 Handle<SharedFunctionInfo> shared =
9312 Compiler::CompileEval(source,
9313 context,
9314 true,
9315 Compiler::DONT_VALIDATE_JSON);
9316 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009317 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009318 Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
9319 context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009320
9321 // Invoke the result of the compilation to get the evaluation function.
9322 bool has_pending_exception;
9323 Handle<Object> receiver = Top::global();
9324 Handle<Object> result =
9325 Execution::Call(compiled_function, receiver, 0, NULL,
9326 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009327 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009328 return *result;
9329}
9330
9331
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009332static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
9333 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009334 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009335
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009336 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009337 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009338
9339 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009340 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00009341 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
9342 // Get the script wrapper in a local handle before calling GetScriptWrapper,
9343 // because using
9344 // instances->set(i, *GetScriptWrapper(script))
9345 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
9346 // already have deferenced the instances handle.
9347 Handle<JSValue> wrapper = GetScriptWrapper(script);
9348 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009349 }
9350
9351 // Return result as a JS array.
9352 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
9353 Handle<JSArray>::cast(result)->SetContent(*instances);
9354 return *result;
9355}
9356
9357
9358// Helper function used by Runtime_DebugReferencedBy below.
9359static int DebugReferencedBy(JSObject* target,
9360 Object* instance_filter, int max_references,
9361 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009362 JSFunction* arguments_function) {
9363 NoHandleAllocation ha;
9364 AssertNoAllocation no_alloc;
9365
9366 // Iterate the heap.
9367 int count = 0;
9368 JSObject* last = NULL;
9369 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009370 HeapObject* heap_obj = NULL;
9371 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009372 (max_references == 0 || count < max_references)) {
9373 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009374 if (heap_obj->IsJSObject()) {
9375 // Skip context extension objects and argument arrays as these are
9376 // checked in the context of functions using them.
9377 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00009378 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009379 obj->map()->constructor() == arguments_function) {
9380 continue;
9381 }
9382
9383 // Check if the JS object has a reference to the object looked for.
9384 if (obj->ReferencesObject(target)) {
9385 // Check instance filter if supplied. This is normally used to avoid
9386 // references from mirror objects (see Runtime_IsInPrototypeChain).
9387 if (!instance_filter->IsUndefined()) {
9388 Object* V = obj;
9389 while (true) {
9390 Object* prototype = V->GetPrototype();
9391 if (prototype->IsNull()) {
9392 break;
9393 }
9394 if (instance_filter == prototype) {
9395 obj = NULL; // Don't add this object.
9396 break;
9397 }
9398 V = prototype;
9399 }
9400 }
9401
9402 if (obj != NULL) {
9403 // Valid reference found add to instance array if supplied an update
9404 // count.
9405 if (instances != NULL && count < instances_size) {
9406 instances->set(count, obj);
9407 }
9408 last = obj;
9409 count++;
9410 }
9411 }
9412 }
9413 }
9414
9415 // Check for circular reference only. This can happen when the object is only
9416 // referenced from mirrors and has a circular reference in which case the
9417 // object is not really alive and would have been garbage collected if not
9418 // referenced from the mirror.
9419 if (count == 1 && last == target) {
9420 count = 0;
9421 }
9422
9423 // Return the number of referencing objects found.
9424 return count;
9425}
9426
9427
9428// Scan the heap for objects with direct references to an object
9429// args[0]: the object to find references to
9430// args[1]: constructor function for instances to exclude (Mirror)
9431// args[2]: the the maximum number of objects to return
9432static Object* Runtime_DebugReferencedBy(Arguments args) {
9433 ASSERT(args.length() == 3);
9434
9435 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009436 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009437
9438 // Check parameters.
9439 CONVERT_CHECKED(JSObject, target, args[0]);
9440 Object* instance_filter = args[1];
9441 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
9442 instance_filter->IsJSObject());
9443 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
9444 RUNTIME_ASSERT(max_references >= 0);
9445
9446 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009447 JSObject* arguments_boilerplate =
9448 Top::context()->global_context()->arguments_boilerplate();
9449 JSFunction* arguments_function =
9450 JSFunction::cast(arguments_boilerplate->map()->constructor());
9451
9452 // Get the number of referencing objects.
9453 int count;
9454 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009455 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009456
9457 // Allocate an array to hold the result.
9458 Object* object = Heap::AllocateFixedArray(count);
9459 if (object->IsFailure()) return object;
9460 FixedArray* instances = FixedArray::cast(object);
9461
9462 // Fill the referencing objects.
9463 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009464 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009465
9466 // Return result as JS array.
9467 Object* result =
9468 Heap::AllocateJSObject(
9469 Top::context()->global_context()->array_function());
9470 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9471 return result;
9472}
9473
9474
9475// Helper function used by Runtime_DebugConstructedBy below.
9476static int DebugConstructedBy(JSFunction* constructor, int max_references,
9477 FixedArray* instances, int instances_size) {
9478 AssertNoAllocation no_alloc;
9479
9480 // Iterate the heap.
9481 int count = 0;
9482 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009483 HeapObject* heap_obj = NULL;
9484 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009485 (max_references == 0 || count < max_references)) {
9486 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009487 if (heap_obj->IsJSObject()) {
9488 JSObject* obj = JSObject::cast(heap_obj);
9489 if (obj->map()->constructor() == constructor) {
9490 // Valid reference found add to instance array if supplied an update
9491 // count.
9492 if (instances != NULL && count < instances_size) {
9493 instances->set(count, obj);
9494 }
9495 count++;
9496 }
9497 }
9498 }
9499
9500 // Return the number of referencing objects found.
9501 return count;
9502}
9503
9504
9505// Scan the heap for objects constructed by a specific function.
9506// args[0]: the constructor to find instances of
9507// args[1]: the the maximum number of objects to return
9508static Object* Runtime_DebugConstructedBy(Arguments args) {
9509 ASSERT(args.length() == 2);
9510
9511 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009512 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009513
9514 // Check parameters.
9515 CONVERT_CHECKED(JSFunction, constructor, args[0]);
9516 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
9517 RUNTIME_ASSERT(max_references >= 0);
9518
9519 // Get the number of referencing objects.
9520 int count;
9521 count = DebugConstructedBy(constructor, max_references, NULL, 0);
9522
9523 // Allocate an array to hold the result.
9524 Object* object = Heap::AllocateFixedArray(count);
9525 if (object->IsFailure()) return object;
9526 FixedArray* instances = FixedArray::cast(object);
9527
9528 // Fill the referencing objects.
9529 count = DebugConstructedBy(constructor, max_references, instances, count);
9530
9531 // Return result as JS array.
9532 Object* result =
9533 Heap::AllocateJSObject(
9534 Top::context()->global_context()->array_function());
9535 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9536 return result;
9537}
9538
9539
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009540// Find the effective prototype object as returned by __proto__.
9541// args[0]: the object to find the prototype for.
9542static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009543 ASSERT(args.length() == 1);
9544
9545 CONVERT_CHECKED(JSObject, obj, args[0]);
9546
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009547 // Use the __proto__ accessor.
9548 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009549}
9550
9551
9552static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009553 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009554 CPU::DebugBreak();
9555 return Heap::undefined_value();
9556}
9557
9558
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009559static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009560#ifdef DEBUG
9561 HandleScope scope;
9562 ASSERT(args.length() == 1);
9563 // Get the function and make sure it is compiled.
9564 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009565 Handle<SharedFunctionInfo> shared(func->shared());
9566 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009567 return Failure::Exception();
9568 }
9569 func->code()->PrintLn();
9570#endif // DEBUG
9571 return Heap::undefined_value();
9572}
ager@chromium.org9085a012009-05-11 19:22:57 +00009573
9574
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009575static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
9576#ifdef DEBUG
9577 HandleScope scope;
9578 ASSERT(args.length() == 1);
9579 // Get the function and make sure it is compiled.
9580 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009581 Handle<SharedFunctionInfo> shared(func->shared());
9582 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009583 return Failure::Exception();
9584 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009585 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009586#endif // DEBUG
9587 return Heap::undefined_value();
9588}
9589
9590
ager@chromium.org9085a012009-05-11 19:22:57 +00009591static Object* Runtime_FunctionGetInferredName(Arguments args) {
9592 NoHandleAllocation ha;
9593 ASSERT(args.length() == 1);
9594
9595 CONVERT_CHECKED(JSFunction, f, args[0]);
9596 return f->shared()->inferred_name();
9597}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009598
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009599
9600static int FindSharedFunctionInfosForScript(Script* script,
9601 FixedArray* buffer) {
9602 AssertNoAllocation no_allocations;
9603
9604 int counter = 0;
9605 int buffer_size = buffer->length();
9606 HeapIterator iterator;
9607 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
9608 ASSERT(obj != NULL);
9609 if (!obj->IsSharedFunctionInfo()) {
9610 continue;
9611 }
9612 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
9613 if (shared->script() != script) {
9614 continue;
9615 }
9616 if (counter < buffer_size) {
9617 buffer->set(counter, shared);
9618 }
9619 counter++;
9620 }
9621 return counter;
9622}
9623
9624// For a script finds all SharedFunctionInfo's in the heap that points
9625// to this script. Returns JSArray of SharedFunctionInfo wrapped
9626// in OpaqueReferences.
9627static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
9628 Arguments args) {
9629 ASSERT(args.length() == 1);
9630 HandleScope scope;
9631 CONVERT_CHECKED(JSValue, script_value, args[0]);
9632
9633 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
9634
9635 const int kBufferSize = 32;
9636
9637 Handle<FixedArray> array;
9638 array = Factory::NewFixedArray(kBufferSize);
9639 int number = FindSharedFunctionInfosForScript(*script, *array);
9640 if (number > kBufferSize) {
9641 array = Factory::NewFixedArray(number);
9642 FindSharedFunctionInfosForScript(*script, *array);
9643 }
9644
9645 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
9646 result->set_length(Smi::FromInt(number));
9647
9648 LiveEdit::WrapSharedFunctionInfos(result);
9649
9650 return *result;
9651}
9652
9653// For a script calculates compilation information about all its functions.
9654// The script source is explicitly specified by the second argument.
9655// The source of the actual script is not used, however it is important that
9656// all generated code keeps references to this particular instance of script.
9657// Returns a JSArray of compilation infos. The array is ordered so that
9658// each function with all its descendant is always stored in a continues range
9659// with the function itself going first. The root function is a script function.
9660static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
9661 ASSERT(args.length() == 2);
9662 HandleScope scope;
9663 CONVERT_CHECKED(JSValue, script, args[0]);
9664 CONVERT_ARG_CHECKED(String, source, 1);
9665 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
9666
9667 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
9668
9669 if (Top::has_pending_exception()) {
9670 return Failure::Exception();
9671 }
9672
9673 return result;
9674}
9675
9676// Changes the source of the script to a new_source and creates a new
9677// script representing the old version of the script source.
9678static Object* Runtime_LiveEditReplaceScript(Arguments args) {
9679 ASSERT(args.length() == 3);
9680 HandleScope scope;
9681 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
9682 CONVERT_ARG_CHECKED(String, new_source, 1);
9683 CONVERT_ARG_CHECKED(String, old_script_name, 2);
9684 Handle<Script> original_script =
9685 Handle<Script>(Script::cast(original_script_value->value()));
9686
9687 Handle<String> original_source(String::cast(original_script->source()));
9688
9689 original_script->set_source(*new_source);
9690 Handle<Script> old_script = Factory::NewScript(original_source);
9691 old_script->set_name(*old_script_name);
9692 old_script->set_line_offset(original_script->line_offset());
9693 old_script->set_column_offset(original_script->column_offset());
9694 old_script->set_data(original_script->data());
9695 old_script->set_type(original_script->type());
9696 old_script->set_context_data(original_script->context_data());
9697 old_script->set_compilation_type(original_script->compilation_type());
9698 old_script->set_eval_from_shared(original_script->eval_from_shared());
9699 old_script->set_eval_from_instructions_offset(
9700 original_script->eval_from_instructions_offset());
9701
ager@chromium.org357bf652010-04-12 11:30:10 +00009702 // Drop line ends so that they will be recalculated.
9703 original_script->set_line_ends(Heap::undefined_value());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009704
9705 Debugger::OnAfterCompile(old_script, Debugger::SEND_WHEN_DEBUGGING);
9706
9707 return *(GetScriptWrapper(old_script));
9708}
9709
9710// Replaces code of SharedFunctionInfo with a new one.
9711static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
9712 ASSERT(args.length() == 2);
9713 HandleScope scope;
9714 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
9715 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
9716
9717 LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
9718
9719 return Heap::undefined_value();
9720}
9721
9722// Connects SharedFunctionInfo to another script.
9723static Object* Runtime_LiveEditRelinkFunctionToScript(Arguments args) {
9724 ASSERT(args.length() == 2);
9725 HandleScope scope;
9726 CONVERT_ARG_CHECKED(JSArray, shared_info_array, 0);
9727 CONVERT_ARG_CHECKED(JSValue, script_value, 1);
9728 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
9729
9730 LiveEdit::RelinkFunctionToScript(shared_info_array, script);
9731
9732 return Heap::undefined_value();
9733}
9734
9735// Updates positions of a shared function info (first parameter) according
9736// to script source change. Text change is described in second parameter as
9737// array of groups of 3 numbers:
9738// (change_begin, change_end, change_end_new_position).
9739// Each group describes a change in text; groups are sorted by change_begin.
ager@chromium.org357bf652010-04-12 11:30:10 +00009740// Returns an array of pairs (new source position, breakpoint_object/array)
9741// so that JS side could update positions in breakpoint objects.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009742static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
9743 ASSERT(args.length() == 2);
9744 HandleScope scope;
9745 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
9746 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
9747
ager@chromium.org357bf652010-04-12 11:30:10 +00009748 Handle<Object> result =
9749 LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009750
ager@chromium.org357bf652010-04-12 11:30:10 +00009751 return *result;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009752}
9753
9754
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009755// For array of SharedFunctionInfo's (each wrapped in JSValue)
9756// checks that none of them have activations on stacks (of any thread).
9757// Returns array of the same length with corresponding results of
9758// LiveEdit::FunctionPatchabilityStatus type.
ager@chromium.org357bf652010-04-12 11:30:10 +00009759static Object* Runtime_LiveEditCheckAndDropActivations(Arguments args) {
9760 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009761 HandleScope scope;
9762 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +00009763 CONVERT_BOOLEAN_CHECKED(do_drop, args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009764
ager@chromium.org357bf652010-04-12 11:30:10 +00009765 return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009766}
9767
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009768// Compares 2 strings line-by-line and returns diff in form of JSArray of
9769// triplets (pos1, len1, len2) describing list of diff chunks.
9770static Object* Runtime_LiveEditCompareStringsLinewise(Arguments args) {
9771 ASSERT(args.length() == 2);
9772 HandleScope scope;
9773 CONVERT_ARG_CHECKED(String, s1, 0);
9774 CONVERT_ARG_CHECKED(String, s2, 1);
9775
9776 return *LiveEdit::CompareStringsLinewise(s1, s2);
9777}
9778
9779
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009780
fschneider@chromium.org086aac62010-03-17 13:18:24 +00009781// A testing entry. Returns statement position which is the closest to
9782// source_position.
9783static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
9784 ASSERT(args.length() == 2);
9785 HandleScope scope;
9786 CONVERT_ARG_CHECKED(JSFunction, function, 0);
9787 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9788
9789 Handle<Code> code(function->code());
9790
9791 RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
9792 int closest_pc = 0;
9793 int distance = kMaxInt;
9794 while (!it.done()) {
9795 int statement_position = static_cast<int>(it.rinfo()->data());
9796 // Check if this break point is closer that what was previously found.
9797 if (source_position <= statement_position &&
9798 statement_position - source_position < distance) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00009799 closest_pc =
9800 static_cast<int>(it.rinfo()->pc() - code->instruction_start());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00009801 distance = statement_position - source_position;
9802 // Check whether we can't get any closer.
9803 if (distance == 0) break;
9804 }
9805 it.next();
9806 }
9807
9808 return Smi::FromInt(closest_pc);
9809}
9810
9811
ager@chromium.org357bf652010-04-12 11:30:10 +00009812// Calls specified function with or without entering the debugger.
9813// This is used in unit tests to run code as if debugger is entered or simply
9814// to have a stack with C++ frame in the middle.
9815static Object* Runtime_ExecuteInDebugContext(Arguments args) {
9816 ASSERT(args.length() == 2);
9817 HandleScope scope;
9818 CONVERT_ARG_CHECKED(JSFunction, function, 0);
9819 CONVERT_BOOLEAN_CHECKED(without_debugger, args[1]);
9820
9821 Handle<Object> result;
9822 bool pending_exception;
9823 {
9824 if (without_debugger) {
9825 result = Execution::Call(function, Top::global(), 0, NULL,
9826 &pending_exception);
9827 } else {
9828 EnterDebugger enter_debugger;
9829 result = Execution::Call(function, Top::global(), 0, NULL,
9830 &pending_exception);
9831 }
9832 }
9833 if (!pending_exception) {
9834 return *result;
9835 } else {
9836 return Failure::Exception();
9837 }
9838}
9839
9840
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009841#endif // ENABLE_DEBUGGER_SUPPORT
9842
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009843#ifdef ENABLE_LOGGING_AND_PROFILING
9844
9845static Object* Runtime_ProfilerResume(Arguments args) {
9846 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +00009847 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009848
9849 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +00009850 CONVERT_CHECKED(Smi, smi_tag, args[1]);
9851 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009852 return Heap::undefined_value();
9853}
9854
9855
9856static Object* Runtime_ProfilerPause(Arguments args) {
9857 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +00009858 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009859
9860 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +00009861 CONVERT_CHECKED(Smi, smi_tag, args[1]);
9862 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009863 return Heap::undefined_value();
9864}
9865
9866#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009867
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009868// Finds the script object from the script data. NOTE: This operation uses
9869// heap traversal to find the function generated for the source position
9870// for the requested break point. For lazily compiled functions several heap
9871// traversals might be required rendering this operation as a rather slow
9872// operation. However for setting break points which is normally done through
9873// some kind of user interaction the performance is not crucial.
9874static Handle<Object> Runtime_GetScriptFromScriptName(
9875 Handle<String> script_name) {
9876 // Scan the heap for Script objects to find the script with the requested
9877 // script data.
9878 Handle<Script> script;
9879 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009880 HeapObject* obj = NULL;
9881 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009882 // If a script is found check if it has the script data requested.
9883 if (obj->IsScript()) {
9884 if (Script::cast(obj)->name()->IsString()) {
9885 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
9886 script = Handle<Script>(Script::cast(obj));
9887 }
9888 }
9889 }
9890 }
9891
9892 // If no script with the requested script data is found return undefined.
9893 if (script.is_null()) return Factory::undefined_value();
9894
9895 // Return the script found.
9896 return GetScriptWrapper(script);
9897}
9898
9899
9900// Get the script object from script data. NOTE: Regarding performance
9901// see the NOTE for GetScriptFromScriptData.
9902// args[0]: script data for the script to find the source for
9903static Object* Runtime_GetScript(Arguments args) {
9904 HandleScope scope;
9905
9906 ASSERT(args.length() == 1);
9907
9908 CONVERT_CHECKED(String, script_name, args[0]);
9909
9910 // Find the requested script.
9911 Handle<Object> result =
9912 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
9913 return *result;
9914}
9915
9916
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009917// Determines whether the given stack frame should be displayed in
9918// a stack trace. The caller is the error constructor that asked
9919// for the stack trace to be collected. The first time a construct
9920// call to this function is encountered it is skipped. The seen_caller
9921// in/out parameter is used to remember if the caller has been seen
9922// yet.
9923static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
9924 bool* seen_caller) {
9925 // Only display JS frames.
9926 if (!raw_frame->is_java_script())
9927 return false;
9928 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
9929 Object* raw_fun = frame->function();
9930 // Not sure when this can happen but skip it just in case.
9931 if (!raw_fun->IsJSFunction())
9932 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009933 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009934 *seen_caller = true;
9935 return false;
9936 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009937 // Skip all frames until we've seen the caller. Also, skip the most
9938 // obvious builtin calls. Some builtin calls (such as Number.ADD
9939 // which is invoked using 'call') are very difficult to recognize
9940 // so we're leaving them in for now.
9941 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009942}
9943
9944
9945// Collect the raw data for a stack trace. Returns an array of three
9946// element segments each containing a receiver, function and native
9947// code offset.
9948static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009949 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009950 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009951 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
9952
9953 HandleScope scope;
9954
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00009955 limit = Max(limit, 0); // Ensure that limit is not negative.
9956 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009957 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009958
9959 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009960 // If the caller parameter is a function we skip frames until we're
9961 // under it before starting to collect.
9962 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009963 int cursor = 0;
9964 int frames_seen = 0;
9965 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009966 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009967 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009968 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009969 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009970 Object* recv = frame->receiver();
9971 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009972 Address pc = frame->pc();
9973 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009974 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009975 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009976 if (cursor + 2 < elements->length()) {
9977 elements->set(cursor++, recv);
9978 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009979 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009980 } else {
9981 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009982 Handle<Object> recv_handle(recv);
9983 Handle<Object> fun_handle(fun);
9984 SetElement(result, cursor++, recv_handle);
9985 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009986 SetElement(result, cursor++, Handle<Smi>(offset));
9987 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009988 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009989 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009990 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009991
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009992 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009993 return *result;
9994}
9995
9996
ager@chromium.org3811b432009-10-28 14:53:37 +00009997// Returns V8 version as a string.
9998static Object* Runtime_GetV8Version(Arguments args) {
9999 ASSERT_EQ(args.length(), 0);
10000
10001 NoHandleAllocation ha;
10002
10003 const char* version_string = v8::V8::GetVersion();
10004
10005 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
10006}
10007
10008
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010009static Object* Runtime_Abort(Arguments args) {
10010 ASSERT(args.length() == 2);
10011 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
10012 Smi::cast(args[1])->value());
10013 Top::PrintStack();
10014 OS::Abort();
10015 UNREACHABLE();
10016 return NULL;
10017}
10018
10019
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010020static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
10021 ASSERT(args.length() == 0);
10022 HandleScope::DeleteExtensions();
10023 return Heap::undefined_value();
10024}
10025
10026
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010027static Object* CacheMiss(FixedArray* cache_obj, int index, Object* key_obj) {
10028 ASSERT(index % 2 == 0); // index of the key
10029 ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
10030 ASSERT(index < cache_obj->length());
10031
10032 HandleScope scope;
10033
10034 Handle<FixedArray> cache(cache_obj);
10035 Handle<Object> key(key_obj);
10036 Handle<JSFunction> factory(JSFunction::cast(
10037 cache->get(JSFunctionResultCache::kFactoryIndex)));
10038 // TODO(antonm): consider passing a receiver when constructing a cache.
10039 Handle<Object> receiver(Top::global_context()->global());
10040
10041 Handle<Object> value;
10042 {
10043 // This handle is nor shared, nor used later, so it's safe.
10044 Object** argv[] = { key.location() };
10045 bool pending_exception = false;
10046 value = Execution::Call(factory,
10047 receiver,
10048 1,
10049 argv,
10050 &pending_exception);
10051 if (pending_exception) return Failure::Exception();
10052 }
10053
10054 cache->set(index, *key);
10055 cache->set(index + 1, *value);
10056 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(index));
10057
10058 return *value;
10059}
10060
10061
10062static Object* Runtime_GetFromCache(Arguments args) {
10063 // This is only called from codegen, so checks might be more lax.
10064 CONVERT_CHECKED(FixedArray, cache, args[0]);
10065 Object* key = args[1];
10066
10067 const int finger_index =
10068 Smi::cast(cache->get(JSFunctionResultCache::kFingerIndex))->value();
10069
10070 Object* o = cache->get(finger_index);
10071 if (o == key) {
10072 // The fastest case: hit the same place again.
10073 return cache->get(finger_index + 1);
10074 }
10075
10076 for (int i = finger_index - 2;
10077 i >= JSFunctionResultCache::kEntriesIndex;
10078 i -= 2) {
10079 o = cache->get(i);
10080 if (o == key) {
10081 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10082 return cache->get(i + 1);
10083 }
10084 }
10085
10086 const int size =
10087 Smi::cast(cache->get(JSFunctionResultCache::kCacheSizeIndex))->value();
10088 ASSERT(size <= cache->length());
10089
10090 for (int i = size - 2; i > finger_index; i -= 2) {
10091 o = cache->get(i);
10092 if (o == key) {
10093 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10094 return cache->get(i + 1);
10095 }
10096 }
10097
10098 // Cache miss. If we have spare room, put new data into it, otherwise
10099 // evict post finger entry which must be least recently used.
10100 if (size < cache->length()) {
10101 cache->set(JSFunctionResultCache::kCacheSizeIndex, Smi::FromInt(size + 2));
10102 return CacheMiss(cache, size, key);
10103 } else {
antonm@chromium.org397e23c2010-04-21 12:00:05 +000010104 int target_index = finger_index + JSFunctionResultCache::kEntrySize;
10105 if (target_index == cache->length()) {
10106 target_index = JSFunctionResultCache::kEntriesIndex;
10107 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010108 return CacheMiss(cache, target_index, key);
10109 }
10110}
10111
kasper.lund44510672008-07-25 07:37:58 +000010112#ifdef DEBUG
10113// ListNatives is ONLY used by the fuzz-natives.js in debug mode
10114// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010115static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +000010116 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010117 HandleScope scope;
10118 Handle<JSArray> result = Factory::NewJSArray(0);
10119 int index = 0;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010120 bool inline_runtime_functions = false;
ager@chromium.orga1645e22009-09-09 19:27:10 +000010121#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010122 { \
10123 HandleScope inner; \
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010124 Handle<String> name; \
10125 /* Inline runtime functions have an underscore in front of the name. */ \
10126 if (inline_runtime_functions) { \
10127 name = Factory::NewStringFromAscii( \
10128 Vector<const char>("_" #Name, StrLength("_" #Name))); \
10129 } else { \
10130 name = Factory::NewStringFromAscii( \
10131 Vector<const char>(#Name, StrLength(#Name))); \
10132 } \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010133 Handle<JSArray> pair = Factory::NewJSArray(0); \
10134 SetElement(pair, 0, name); \
10135 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
10136 SetElement(result, index++, pair); \
10137 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010138 inline_runtime_functions = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010139 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010140 inline_runtime_functions = true;
10141 INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010142#undef ADD_ENTRY
10143 return *result;
10144}
kasper.lund44510672008-07-25 07:37:58 +000010145#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010146
10147
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010148static Object* Runtime_Log(Arguments args) {
10149 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +000010150 CONVERT_CHECKED(String, format, args[0]);
10151 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010152 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010153 Logger::LogRuntime(chars, elms);
10154 return Heap::undefined_value();
10155}
10156
10157
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010158static Object* Runtime_IS_VAR(Arguments args) {
10159 UNREACHABLE(); // implemented as macro in the parser
10160 return NULL;
10161}
10162
10163
10164// ----------------------------------------------------------------------------
10165// Implementation of Runtime
10166
ager@chromium.orga1645e22009-09-09 19:27:10 +000010167#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010168 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +000010169 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010170
10171static Runtime::Function Runtime_functions[] = {
10172 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010173 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010174};
10175
10176#undef F
10177
10178
10179Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
10180 ASSERT(0 <= fid && fid < kNofFunctions);
10181 return &Runtime_functions[fid];
10182}
10183
10184
10185Runtime::Function* Runtime::FunctionForName(const char* name) {
10186 for (Function* f = Runtime_functions; f->name != NULL; f++) {
10187 if (strcmp(f->name, name) == 0) {
10188 return f;
10189 }
10190 }
10191 return NULL;
10192}
10193
10194
10195void Runtime::PerformGC(Object* result) {
10196 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010197 if (failure->IsRetryAfterGC()) {
10198 // Try to do a garbage collection; ignore it if it fails. The C
10199 // entry stub will throw an out-of-memory exception in that case.
10200 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
10201 } else {
10202 // Handle last resort GC and make sure to allow future allocations
10203 // to grow the heap without causing GCs (if possible).
10204 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +000010205 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010206 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010207}
10208
10209
10210} } // namespace v8::internal