blob: 4a0fe7ae2277fa6d6a1459df45077b02050c76d9 [file] [log] [blame]
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001// Copyright 2006-2009 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include <stdlib.h>
29
30#include "v8.h"
31
32#include "accessors.h"
33#include "api.h"
34#include "arguments.h"
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000035#include "codegen.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000036#include "compiler.h"
37#include "cpu.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000038#include "dateparser-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000039#include "debug.h"
40#include "execution.h"
41#include "jsregexp.h"
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000042#include "liveedit.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000043#include "parser.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000044#include "platform.h"
45#include "runtime.h"
46#include "scopeinfo.h"
ager@chromium.org7c537e22008-10-16 08:43:32 +000047#include "smart-pointer.h"
ager@chromium.org18ad94b2009-09-02 08:22:29 +000048#include "stub-cache.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000049#include "v8threads.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000050
kasperl@chromium.org71affb52009-05-26 05:44:31 +000051namespace v8 {
52namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000053
54
ager@chromium.org3e875802009-06-29 08:26:34 +000055#define RUNTIME_ASSERT(value) \
56 if (!(value)) return Top::ThrowIllegalOperation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000057
58// Cast the given object to a value of the specified type and store
59// it in a variable with the given name. If the object is not of the
60// expected type call IllegalOperation and return.
61#define CONVERT_CHECKED(Type, name, obj) \
62 RUNTIME_ASSERT(obj->Is##Type()); \
63 Type* name = Type::cast(obj);
64
65#define CONVERT_ARG_CHECKED(Type, name, index) \
66 RUNTIME_ASSERT(args[index]->Is##Type()); \
67 Handle<Type> name = args.at<Type>(index);
68
kasper.lundbd3ec4e2008-07-09 11:06:54 +000069// Cast the given object to a boolean and store it in a variable with
70// the given name. If the object is not a boolean call IllegalOperation
71// and return.
72#define CONVERT_BOOLEAN_CHECKED(name, obj) \
73 RUNTIME_ASSERT(obj->IsBoolean()); \
74 bool name = (obj)->IsTrue();
75
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000076// Cast the given object to a Smi and store its value in an int variable
77// with the given name. If the object is not a Smi call IllegalOperation
78// and return.
79#define CONVERT_SMI_CHECKED(name, obj) \
80 RUNTIME_ASSERT(obj->IsSmi()); \
81 int name = Smi::cast(obj)->value();
82
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000083// Cast the given object to a double and store it in a variable with
84// the given name. If the object is not a number (as opposed to
85// the number not-a-number) call IllegalOperation and return.
86#define CONVERT_DOUBLE_CHECKED(name, obj) \
87 RUNTIME_ASSERT(obj->IsNumber()); \
88 double name = (obj)->Number();
89
90// Call the specified converter on the object *comand store the result in
91// a variable of the specified type with the given name. If the
92// object is not a Number call IllegalOperation and return.
93#define CONVERT_NUMBER_CHECKED(type, name, Type, obj) \
94 RUNTIME_ASSERT(obj->IsNumber()); \
95 type name = NumberTo##Type(obj);
96
97// Non-reentrant string buffer for efficient general use in this file.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000098static StaticResource<StringInputBuffer> runtime_string_input_buffer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000099
100
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000101static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
102 StackLimitCheck check;
103 if (check.HasOverflowed()) return Top::StackOverflow();
104
105 Object* result = Heap::CopyJSObject(boilerplate);
106 if (result->IsFailure()) return result;
107 JSObject* copy = JSObject::cast(result);
108
109 // Deep copy local properties.
110 if (copy->HasFastProperties()) {
111 FixedArray* properties = copy->properties();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000112 for (int i = 0; i < properties->length(); i++) {
113 Object* value = properties->get(i);
114 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000115 JSObject* js_object = JSObject::cast(value);
116 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000117 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000118 properties->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000119 }
120 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000121 int nof = copy->map()->inobject_properties();
122 for (int i = 0; i < nof; i++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000123 Object* value = copy->InObjectPropertyAt(i);
124 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000125 JSObject* js_object = JSObject::cast(value);
126 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000127 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000128 copy->InObjectPropertyAtPut(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000129 }
130 }
131 } else {
132 result = Heap::AllocateFixedArray(copy->NumberOfLocalProperties(NONE));
133 if (result->IsFailure()) return result;
134 FixedArray* names = FixedArray::cast(result);
135 copy->GetLocalPropertyNames(names, 0);
136 for (int i = 0; i < names->length(); i++) {
137 ASSERT(names->get(i)->IsString());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000138 String* key_string = String::cast(names->get(i));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000139 PropertyAttributes attributes =
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000140 copy->GetLocalPropertyAttribute(key_string);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000141 // Only deep copy fields from the object literal expression.
142 // In particular, don't try to copy the length attribute of
143 // an array.
144 if (attributes != NONE) continue;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000145 Object* value = copy->GetProperty(key_string, &attributes);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000146 ASSERT(!value->IsFailure());
147 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000148 JSObject* js_object = JSObject::cast(value);
149 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000150 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000151 result = copy->SetProperty(key_string, result, NONE);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000152 if (result->IsFailure()) return result;
153 }
154 }
155 }
156
157 // Deep copy local elements.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000158 // Pixel elements cannot be created using an object literal.
ager@chromium.org3811b432009-10-28 14:53:37 +0000159 ASSERT(!copy->HasPixelElements() && !copy->HasExternalArrayElements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000160 switch (copy->GetElementsKind()) {
161 case JSObject::FAST_ELEMENTS: {
162 FixedArray* elements = FixedArray::cast(copy->elements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000163 for (int i = 0; i < elements->length(); i++) {
164 Object* value = elements->get(i);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000165 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000166 JSObject* js_object = JSObject::cast(value);
167 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000168 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000169 elements->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000170 }
171 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000172 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000173 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000174 case JSObject::DICTIONARY_ELEMENTS: {
175 NumberDictionary* element_dictionary = copy->element_dictionary();
176 int capacity = element_dictionary->Capacity();
177 for (int i = 0; i < capacity; i++) {
178 Object* k = element_dictionary->KeyAt(i);
179 if (element_dictionary->IsKey(k)) {
180 Object* value = element_dictionary->ValueAt(i);
181 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000182 JSObject* js_object = JSObject::cast(value);
183 result = DeepCopyBoilerplate(js_object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000184 if (result->IsFailure()) return result;
185 element_dictionary->ValueAtPut(i, result);
186 }
187 }
188 }
189 break;
190 }
191 default:
192 UNREACHABLE();
193 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000194 }
195 return copy;
196}
197
198
199static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
200 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
201 return DeepCopyBoilerplate(boilerplate);
202}
203
204
205static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000206 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000207 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000208}
209
210
ager@chromium.org236ad962008-09-25 09:45:57 +0000211static Handle<Map> ComputeObjectLiteralMap(
212 Handle<Context> context,
213 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000214 bool* is_result_from_cache) {
ager@chromium.org32912102009-01-16 10:38:43 +0000215 int number_of_properties = constant_properties->length() / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000216 if (FLAG_canonicalize_object_literal_maps) {
217 // First find prefix of consecutive symbol keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000218 int number_of_symbol_keys = 0;
219 while ((number_of_symbol_keys < number_of_properties) &&
220 (constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
221 number_of_symbol_keys++;
222 }
223 // Based on the number of prefix symbols key we decide whether
224 // to use the map cache in the global context.
225 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000226 if ((number_of_symbol_keys == number_of_properties) &&
227 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000228 // Create the fixed array with the key.
229 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
230 for (int i = 0; i < number_of_symbol_keys; i++) {
231 keys->set(i, constant_properties->get(i*2));
232 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000233 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000234 return Factory::ObjectLiteralMapFromCache(context, keys);
235 }
236 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000237 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000238 return Factory::CopyMap(
239 Handle<Map>(context->object_function()->initial_map()),
240 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000241}
242
243
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000244static Handle<Object> CreateLiteralBoilerplate(
245 Handle<FixedArray> literals,
246 Handle<FixedArray> constant_properties);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000247
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000248
249static Handle<Object> CreateObjectLiteralBoilerplate(
250 Handle<FixedArray> literals,
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000251 Handle<FixedArray> constant_properties,
252 bool should_have_fast_elements) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000253 // Get the global context from the literals array. This is the
254 // context in which the function was created and we use the object
255 // function from this context to create the object literal. We do
256 // not use the object function from the current global context
257 // because this might be the object function from another context
258 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000259 Handle<Context> context =
260 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
261
262 bool is_result_from_cache;
263 Handle<Map> map = ComputeObjectLiteralMap(context,
264 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000265 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000266
ager@chromium.org236ad962008-09-25 09:45:57 +0000267 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000268
269 // Normalize the elements of the boilerplate to save space if needed.
270 if (!should_have_fast_elements) NormalizeElements(boilerplate);
271
ager@chromium.org32912102009-01-16 10:38:43 +0000272 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000273 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000274 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000275 length / 2,
ager@chromium.org236ad962008-09-25 09:45:57 +0000276 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000277 for (int index = 0; index < length; index +=2) {
278 Handle<Object> key(constant_properties->get(index+0));
279 Handle<Object> value(constant_properties->get(index+1));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000280 if (value->IsFixedArray()) {
281 // The value contains the constant_properties of a
282 // simple object literal.
283 Handle<FixedArray> array = Handle<FixedArray>::cast(value);
284 value = CreateLiteralBoilerplate(literals, array);
285 if (value.is_null()) return value;
286 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000287 Handle<Object> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000288 uint32_t element_index = 0;
289 if (key->IsSymbol()) {
290 // If key is a symbol it is not an array element.
291 Handle<String> name(String::cast(*key));
292 ASSERT(!name->AsArrayIndex(&element_index));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000293 result = SetProperty(boilerplate, name, value, NONE);
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000294 } else if (key->ToArrayIndex(&element_index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000295 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000296 result = SetElement(boilerplate, element_index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000297 } else {
298 // Non-uint32 number.
299 ASSERT(key->IsNumber());
300 double num = key->Number();
301 char arr[100];
302 Vector<char> buffer(arr, ARRAY_SIZE(arr));
303 const char* str = DoubleToCString(num, buffer);
304 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000305 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000306 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000307 // If setting the property on the boilerplate throws an
308 // exception, the exception is converted to an empty handle in
309 // the handle based operations. In that case, we need to
310 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000311 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000312 }
313 }
314
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000315 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000316}
317
318
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000319static Handle<Object> CreateArrayLiteralBoilerplate(
320 Handle<FixedArray> literals,
321 Handle<FixedArray> elements) {
322 // Create the JSArray.
323 Handle<JSFunction> constructor(
324 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
325 Handle<Object> object = Factory::NewJSObject(constructor);
326
327 Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
328
329 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
330 for (int i = 0; i < content->length(); i++) {
331 if (content->get(i)->IsFixedArray()) {
332 // The value contains the constant_properties of a
333 // simple object literal.
334 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
335 Handle<Object> result =
336 CreateLiteralBoilerplate(literals, fa);
337 if (result.is_null()) return result;
338 content->set(i, *result);
339 }
340 }
341
342 // Set the elements.
343 Handle<JSArray>::cast(object)->SetContent(*content);
344 return object;
345}
346
347
348static Handle<Object> CreateLiteralBoilerplate(
349 Handle<FixedArray> literals,
350 Handle<FixedArray> array) {
351 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
352 switch (CompileTimeValue::GetType(array)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000353 case CompileTimeValue::OBJECT_LITERAL_FAST_ELEMENTS:
354 return CreateObjectLiteralBoilerplate(literals, elements, true);
355 case CompileTimeValue::OBJECT_LITERAL_SLOW_ELEMENTS:
356 return CreateObjectLiteralBoilerplate(literals, elements, false);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000357 case CompileTimeValue::ARRAY_LITERAL:
358 return CreateArrayLiteralBoilerplate(literals, elements);
359 default:
360 UNREACHABLE();
361 return Handle<Object>::null();
362 }
363}
364
365
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000366static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000367 // Takes a FixedArray of elements containing the literal elements of
368 // the array literal and produces JSArray with those elements.
369 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000370 // which contains the context from which to get the Array function
371 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000372 HandleScope scope;
373 ASSERT(args.length() == 3);
374 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
375 CONVERT_SMI_CHECKED(literals_index, args[1]);
376 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000377
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000378 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
379 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000380
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000381 // Update the functions literal and return the boilerplate.
382 literals->set(literals_index, *object);
383 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000384}
385
386
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000387static Object* Runtime_CreateObjectLiteral(Arguments args) {
388 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000389 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000390 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
391 CONVERT_SMI_CHECKED(literals_index, args[1]);
392 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000393 CONVERT_SMI_CHECKED(fast_elements, args[3]);
394 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000395
396 // Check if boilerplate exists. If not, create it first.
397 Handle<Object> boilerplate(literals->get(literals_index));
398 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000399 boilerplate = CreateObjectLiteralBoilerplate(literals,
400 constant_properties,
401 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000402 if (boilerplate.is_null()) return Failure::Exception();
403 // Update the functions literal and return the boilerplate.
404 literals->set(literals_index, *boilerplate);
405 }
406 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
407}
408
409
410static Object* Runtime_CreateObjectLiteralShallow(Arguments args) {
411 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000412 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000413 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
414 CONVERT_SMI_CHECKED(literals_index, args[1]);
415 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000416 CONVERT_SMI_CHECKED(fast_elements, args[3]);
417 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000418
419 // Check if boilerplate exists. If not, create it first.
420 Handle<Object> boilerplate(literals->get(literals_index));
421 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000422 boilerplate = CreateObjectLiteralBoilerplate(literals,
423 constant_properties,
424 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000425 if (boilerplate.is_null()) return Failure::Exception();
426 // Update the functions literal and return the boilerplate.
427 literals->set(literals_index, *boilerplate);
428 }
429 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
430}
431
432
433static Object* Runtime_CreateArrayLiteral(Arguments args) {
434 HandleScope scope;
435 ASSERT(args.length() == 3);
436 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
437 CONVERT_SMI_CHECKED(literals_index, args[1]);
438 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
439
440 // Check if boilerplate exists. If not, create it first.
441 Handle<Object> boilerplate(literals->get(literals_index));
442 if (*boilerplate == Heap::undefined_value()) {
443 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
444 if (boilerplate.is_null()) return Failure::Exception();
445 // Update the functions literal and return the boilerplate.
446 literals->set(literals_index, *boilerplate);
447 }
448 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
449}
450
451
452static Object* Runtime_CreateArrayLiteralShallow(Arguments args) {
453 HandleScope scope;
454 ASSERT(args.length() == 3);
455 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
456 CONVERT_SMI_CHECKED(literals_index, args[1]);
457 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
458
459 // Check if boilerplate exists. If not, create it first.
460 Handle<Object> boilerplate(literals->get(literals_index));
461 if (*boilerplate == Heap::undefined_value()) {
462 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
463 if (boilerplate.is_null()) return Failure::Exception();
464 // Update the functions literal and return the boilerplate.
465 literals->set(literals_index, *boilerplate);
466 }
467 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
468}
469
470
ager@chromium.org32912102009-01-16 10:38:43 +0000471static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
472 ASSERT(args.length() == 2);
473 CONVERT_CHECKED(String, key, args[0]);
474 Object* value = args[1];
475 // Create a catch context extension object.
476 JSFunction* constructor =
477 Top::context()->global_context()->context_extension_function();
478 Object* object = Heap::AllocateJSObject(constructor);
479 if (object->IsFailure()) return object;
480 // Assign the exception value to the catch variable and make sure
481 // that the catch variable is DontDelete.
482 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
483 if (value->IsFailure()) return value;
484 return object;
485}
486
487
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000488static Object* Runtime_ClassOf(Arguments args) {
489 NoHandleAllocation ha;
490 ASSERT(args.length() == 1);
491 Object* obj = args[0];
492 if (!obj->IsJSObject()) return Heap::null_value();
493 return JSObject::cast(obj)->class_name();
494}
495
ager@chromium.org7c537e22008-10-16 08:43:32 +0000496
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000497static Object* Runtime_IsInPrototypeChain(Arguments args) {
498 NoHandleAllocation ha;
499 ASSERT(args.length() == 2);
500 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
501 Object* O = args[0];
502 Object* V = args[1];
503 while (true) {
504 Object* prototype = V->GetPrototype();
505 if (prototype->IsNull()) return Heap::false_value();
506 if (O == prototype) return Heap::true_value();
507 V = prototype;
508 }
509}
510
511
ager@chromium.org9085a012009-05-11 19:22:57 +0000512// Inserts an object as the hidden prototype of another object.
513static Object* Runtime_SetHiddenPrototype(Arguments args) {
514 NoHandleAllocation ha;
515 ASSERT(args.length() == 2);
516 CONVERT_CHECKED(JSObject, jsobject, args[0]);
517 CONVERT_CHECKED(JSObject, proto, args[1]);
518
519 // Sanity checks. The old prototype (that we are replacing) could
520 // theoretically be null, but if it is not null then check that we
521 // didn't already install a hidden prototype here.
522 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
523 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
524 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
525
526 // Allocate up front before we start altering state in case we get a GC.
527 Object* map_or_failure = proto->map()->CopyDropTransitions();
528 if (map_or_failure->IsFailure()) return map_or_failure;
529 Map* new_proto_map = Map::cast(map_or_failure);
530
531 map_or_failure = jsobject->map()->CopyDropTransitions();
532 if (map_or_failure->IsFailure()) return map_or_failure;
533 Map* new_map = Map::cast(map_or_failure);
534
535 // Set proto's prototype to be the old prototype of the object.
536 new_proto_map->set_prototype(jsobject->GetPrototype());
537 proto->set_map(new_proto_map);
538 new_proto_map->set_is_hidden_prototype();
539
540 // Set the object's prototype to proto.
541 new_map->set_prototype(proto);
542 jsobject->set_map(new_map);
543
544 return Heap::undefined_value();
545}
546
547
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000548static Object* Runtime_IsConstructCall(Arguments args) {
549 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000550 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000551 JavaScriptFrameIterator it;
552 return Heap::ToBoolean(it.frame()->IsConstructor());
553}
554
555
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000556// Recursively traverses hidden prototypes if property is not found
557static void GetOwnPropertyImplementation(JSObject* obj,
558 String* name,
559 LookupResult* result) {
560 obj->LocalLookupRealNamedProperty(name, result);
561
562 if (!result->IsProperty()) {
563 Object* proto = obj->GetPrototype();
564 if (proto->IsJSObject() &&
565 JSObject::cast(proto)->map()->is_hidden_prototype())
566 GetOwnPropertyImplementation(JSObject::cast(proto),
567 name, result);
568 }
569}
570
571
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000572// Enumerator used as indices into the array returned from GetOwnProperty
573enum PropertyDescriptorIndices {
574 IS_ACCESSOR_INDEX,
575 VALUE_INDEX,
576 GETTER_INDEX,
577 SETTER_INDEX,
578 WRITABLE_INDEX,
579 ENUMERABLE_INDEX,
580 CONFIGURABLE_INDEX,
581 DESCRIPTOR_SIZE
582};
583
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000584// Returns an array with the property description:
585// if args[1] is not a property on args[0]
586// returns undefined
587// if args[1] is a data property on args[0]
588// [false, value, Writeable, Enumerable, Configurable]
589// if args[1] is an accessor on args[0]
590// [true, GetFunction, SetFunction, Enumerable, Configurable]
591static Object* Runtime_GetOwnProperty(Arguments args) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000592 ASSERT(args.length() == 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000593 HandleScope scope;
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000594 Handle<FixedArray> elms = Factory::NewFixedArray(DESCRIPTOR_SIZE);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000595 Handle<JSArray> desc = Factory::NewJSArrayWithElements(elms);
596 LookupResult result;
597 CONVERT_CHECKED(JSObject, obj, args[0]);
598 CONVERT_CHECKED(String, name, args[1]);
599
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000600 // This could be an element.
601 uint32_t index;
602 if (name->AsArrayIndex(&index)) {
603 if (!obj->HasLocalElement(index)) {
604 return Heap::undefined_value();
605 }
606
607 // Special handling of string objects according to ECMAScript 5 15.5.5.2.
608 // Note that this might be a string object with elements other than the
609 // actual string value. This is covered by the subsequent cases.
610 if (obj->IsStringObjectWithCharacterAt(index)) {
611 JSValue* js_value = JSValue::cast(obj);
612 String* str = String::cast(js_value->value());
613 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
614 elms->set(VALUE_INDEX, str->SubString(index, index+1));
615 elms->set(WRITABLE_INDEX, Heap::false_value());
616 elms->set(ENUMERABLE_INDEX, Heap::false_value());
617 elms->set(CONFIGURABLE_INDEX, Heap::false_value());
618 return *desc;
619 }
620
621 // This can potentially be an element in the elements dictionary or
622 // a fast element.
623 if (obj->HasDictionaryElements()) {
624 NumberDictionary* dictionary = obj->element_dictionary();
625 int entry = dictionary->FindEntry(index);
626 PropertyDetails details = dictionary->DetailsAt(entry);
627 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
628 elms->set(VALUE_INDEX, dictionary->ValueAt(entry));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000629 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!details.IsReadOnly()));
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000630 elms->set(ENUMERABLE_INDEX, Heap::ToBoolean(!details.IsDontEnum()));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000631 elms->set(CONFIGURABLE_INDEX, Heap::ToBoolean(!details.IsDontDelete()));
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000632 return *desc;
633 } else {
634 // Elements that are stored as array elements always has:
635 // writable: true, configurable: true, enumerable: true.
636 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
637 elms->set(VALUE_INDEX, obj->GetElement(index));
638 elms->set(WRITABLE_INDEX, Heap::true_value());
639 elms->set(ENUMERABLE_INDEX, Heap::true_value());
640 elms->set(CONFIGURABLE_INDEX, Heap::true_value());
641 return *desc;
642 }
643 }
644
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000645 // Use recursive implementation to also traverse hidden prototypes
646 GetOwnPropertyImplementation(obj, name, &result);
647
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000648 if (!result.IsProperty()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000649 return Heap::undefined_value();
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000650 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000651 if (result.type() == CALLBACKS) {
652 Object* structure = result.GetCallbackObject();
ager@chromium.org5c838252010-02-19 08:53:10 +0000653 if (structure->IsProxy() || structure->IsAccessorInfo()) {
654 // Property that is internally implemented as a callback or
655 // an API defined callback.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000656 Object* value = obj->GetPropertyWithCallback(
657 obj, structure, name, result.holder());
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000658 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
659 elms->set(VALUE_INDEX, value);
660 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!result.IsReadOnly()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000661 } else if (structure->IsFixedArray()) {
662 // __defineGetter__/__defineSetter__ callback.
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000663 elms->set(IS_ACCESSOR_INDEX, Heap::true_value());
664 elms->set(GETTER_INDEX, FixedArray::cast(structure)->get(0));
665 elms->set(SETTER_INDEX, FixedArray::cast(structure)->get(1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000666 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000667 return Heap::undefined_value();
668 }
669 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000670 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
671 elms->set(VALUE_INDEX, result.GetLazyValue());
672 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!result.IsReadOnly()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000673 }
674
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000675 elms->set(ENUMERABLE_INDEX, Heap::ToBoolean(!result.IsDontEnum()));
676 elms->set(CONFIGURABLE_INDEX, Heap::ToBoolean(!result.IsDontDelete()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000677 return *desc;
678}
679
680
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +0000681static Object* Runtime_PreventExtensions(Arguments args) {
682 ASSERT(args.length() == 1);
683 CONVERT_CHECKED(JSObject, obj, args[0]);
684 return obj->PreventExtensions();
685}
686
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000687static Object* Runtime_IsExtensible(Arguments args) {
688 ASSERT(args.length() == 1);
689 CONVERT_CHECKED(JSObject, obj, args[0]);
690 return obj->map()->is_extensible() ? Heap::true_value()
691 : Heap::false_value();
692}
693
694
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000695static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000696 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000697 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000698 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
699 CONVERT_ARG_CHECKED(String, pattern, 1);
700 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000701 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
702 if (result.is_null()) return Failure::Exception();
703 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000704}
705
706
707static Object* Runtime_CreateApiFunction(Arguments args) {
708 HandleScope scope;
709 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000710 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000711 return *Factory::CreateApiFunction(data);
712}
713
714
715static Object* Runtime_IsTemplate(Arguments args) {
716 ASSERT(args.length() == 1);
717 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000718 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000719 return Heap::ToBoolean(result);
720}
721
722
723static Object* Runtime_GetTemplateField(Arguments args) {
724 ASSERT(args.length() == 2);
725 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000726 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000727 int index = field->value();
728 int offset = index * kPointerSize + HeapObject::kHeaderSize;
729 InstanceType type = templ->map()->instance_type();
730 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
731 type == OBJECT_TEMPLATE_INFO_TYPE);
732 RUNTIME_ASSERT(offset > 0);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000733 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000734 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
735 } else {
736 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
737 }
738 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000739}
740
741
ager@chromium.org870a0b62008-11-04 11:43:05 +0000742static Object* Runtime_DisableAccessChecks(Arguments args) {
743 ASSERT(args.length() == 1);
744 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000745 Map* old_map = object->map();
746 bool needs_access_checks = old_map->is_access_check_needed();
747 if (needs_access_checks) {
748 // Copy map so it won't interfere constructor's initial map.
749 Object* new_map = old_map->CopyDropTransitions();
750 if (new_map->IsFailure()) return new_map;
751
752 Map::cast(new_map)->set_is_access_check_needed(false);
753 object->set_map(Map::cast(new_map));
754 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000755 return needs_access_checks ? Heap::true_value() : Heap::false_value();
756}
757
758
759static Object* Runtime_EnableAccessChecks(Arguments args) {
760 ASSERT(args.length() == 1);
761 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000762 Map* old_map = object->map();
763 if (!old_map->is_access_check_needed()) {
764 // Copy map so it won't interfere constructor's initial map.
765 Object* new_map = old_map->CopyDropTransitions();
766 if (new_map->IsFailure()) return new_map;
767
768 Map::cast(new_map)->set_is_access_check_needed(true);
769 object->set_map(Map::cast(new_map));
770 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000771 return Heap::undefined_value();
772}
773
774
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000775static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
776 HandleScope scope;
777 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
778 Handle<Object> args[2] = { type_handle, name };
779 Handle<Object> error =
780 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
781 return Top::Throw(*error);
782}
783
784
785static Object* Runtime_DeclareGlobals(Arguments args) {
786 HandleScope scope;
787 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
788
ager@chromium.org3811b432009-10-28 14:53:37 +0000789 Handle<Context> context = args.at<Context>(0);
790 CONVERT_ARG_CHECKED(FixedArray, pairs, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000791 bool is_eval = Smi::cast(args[2])->value() == 1;
792
793 // Compute the property attributes. According to ECMA-262, section
794 // 13, page 71, the property must be read-only and
795 // non-deletable. However, neither SpiderMonkey nor KJS creates the
796 // property as read-only, so we don't either.
797 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
798
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000799 // Traverse the name/value pairs and set the properties.
800 int length = pairs->length();
801 for (int i = 0; i < length; i += 2) {
802 HandleScope scope;
803 Handle<String> name(String::cast(pairs->get(i)));
804 Handle<Object> value(pairs->get(i + 1));
805
806 // We have to declare a global const property. To capture we only
807 // assign to it when evaluating the assignment for "const x =
808 // <expr>" the initial value is the hole.
809 bool is_const_property = value->IsTheHole();
810
811 if (value->IsUndefined() || is_const_property) {
812 // Lookup the property in the global object, and don't set the
813 // value of the variable if the property is already there.
814 LookupResult lookup;
815 global->Lookup(*name, &lookup);
816 if (lookup.IsProperty()) {
817 // Determine if the property is local by comparing the holder
818 // against the global object. The information will be used to
819 // avoid throwing re-declaration errors when declaring
820 // variables or constants that exist in the prototype chain.
821 bool is_local = (*global == lookup.holder());
822 // Get the property attributes and determine if the property is
823 // read-only.
824 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
825 bool is_read_only = (attributes & READ_ONLY) != 0;
826 if (lookup.type() == INTERCEPTOR) {
827 // If the interceptor says the property is there, we
828 // just return undefined without overwriting the property.
829 // Otherwise, we continue to setting the property.
830 if (attributes != ABSENT) {
831 // Check if the existing property conflicts with regards to const.
832 if (is_local && (is_read_only || is_const_property)) {
833 const char* type = (is_read_only) ? "const" : "var";
834 return ThrowRedeclarationError(type, name);
835 };
836 // The property already exists without conflicting: Go to
837 // the next declaration.
838 continue;
839 }
840 // Fall-through and introduce the absent property by using
841 // SetProperty.
842 } else {
843 if (is_local && (is_read_only || is_const_property)) {
844 const char* type = (is_read_only) ? "const" : "var";
845 return ThrowRedeclarationError(type, name);
846 }
847 // The property already exists without conflicting: Go to
848 // the next declaration.
849 continue;
850 }
851 }
852 } else {
853 // Copy the function and update its context. Use it as value.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000854 Handle<SharedFunctionInfo> shared =
855 Handle<SharedFunctionInfo>::cast(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000856 Handle<JSFunction> function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000857 Factory::NewFunctionFromSharedFunctionInfo(shared, context, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000858 value = function;
859 }
860
861 LookupResult lookup;
862 global->LocalLookup(*name, &lookup);
863
864 PropertyAttributes attributes = is_const_property
865 ? static_cast<PropertyAttributes>(base | READ_ONLY)
866 : base;
867
868 if (lookup.IsProperty()) {
869 // There's a local property that we need to overwrite because
870 // we're either declaring a function or there's an interceptor
871 // that claims the property is absent.
872
873 // Check for conflicting re-declarations. We cannot have
874 // conflicting types in case of intercepted properties because
875 // they are absent.
876 if (lookup.type() != INTERCEPTOR &&
877 (lookup.IsReadOnly() || is_const_property)) {
878 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
879 return ThrowRedeclarationError(type, name);
880 }
881 SetProperty(global, name, value, attributes);
882 } else {
883 // If a property with this name does not already exist on the
884 // global object add the property locally. We take special
885 // precautions to always add it as a local property even in case
886 // of callbacks in the prototype chain (this rules out using
887 // SetProperty). Also, we must use the handle-based version to
888 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000889 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000890 }
891 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000892
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000893 return Heap::undefined_value();
894}
895
896
897static Object* Runtime_DeclareContextSlot(Arguments args) {
898 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000899 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000900
ager@chromium.org7c537e22008-10-16 08:43:32 +0000901 CONVERT_ARG_CHECKED(Context, context, 0);
902 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000903 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000904 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000905 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000906 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000907
908 // Declarations are always done in the function context.
909 context = Handle<Context>(context->fcontext());
910
911 int index;
912 PropertyAttributes attributes;
913 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000914 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000915 context->Lookup(name, flags, &index, &attributes);
916
917 if (attributes != ABSENT) {
918 // The name was declared before; check for conflicting
919 // re-declarations: This is similar to the code in parser.cc in
920 // the AstBuildingParser::Declare function.
921 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
922 // Functions are not read-only.
923 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
924 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
925 return ThrowRedeclarationError(type, name);
926 }
927
928 // Initialize it if necessary.
929 if (*initial_value != NULL) {
930 if (index >= 0) {
931 // The variable or constant context slot should always be in
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000932 // the function context or the arguments object.
933 if (holder->IsContext()) {
934 ASSERT(holder.is_identical_to(context));
935 if (((attributes & READ_ONLY) == 0) ||
936 context->get(index)->IsTheHole()) {
937 context->set(index, *initial_value);
938 }
939 } else {
940 Handle<JSObject>::cast(holder)->SetElement(index, *initial_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000941 }
942 } else {
943 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000944 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000945 SetProperty(context_ext, name, initial_value, mode);
946 }
947 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000948
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000949 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000950 // The property is not in the function context. It needs to be
951 // "declared" in the function context's extension context, or in the
952 // global context.
953 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000954 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000955 // The function context's extension context exists - use it.
956 context_ext = Handle<JSObject>(context->extension());
957 } else {
958 // The function context's extension context does not exists - allocate
959 // it.
960 context_ext = Factory::NewJSObject(Top::context_extension_function());
961 // And store it in the extension slot.
962 context->set_extension(*context_ext);
963 }
964 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000965
ager@chromium.org7c537e22008-10-16 08:43:32 +0000966 // Declare the property by setting it to the initial value if provided,
967 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
968 // constant declarations).
969 ASSERT(!context_ext->HasLocalProperty(*name));
970 Handle<Object> value(Heap::undefined_value());
971 if (*initial_value != NULL) value = initial_value;
972 SetProperty(context_ext, name, value, mode);
973 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
974 }
975
976 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000977}
978
979
980static Object* Runtime_InitializeVarGlobal(Arguments args) {
981 NoHandleAllocation nha;
982
983 // Determine if we need to assign to the variable if it already
984 // exists (based on the number of arguments).
985 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
986 bool assign = args.length() == 2;
987
988 CONVERT_ARG_CHECKED(String, name, 0);
989 GlobalObject* global = Top::context()->global();
990
991 // According to ECMA-262, section 12.2, page 62, the property must
992 // not be deletable.
993 PropertyAttributes attributes = DONT_DELETE;
994
995 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000996 // there, there is a property with this name in the prototype chain.
997 // We follow Safari and Firefox behavior and only set the property
998 // locally if there is an explicit initialization value that we have
999 // to assign to the property. When adding the property we take
1000 // special precautions to always add it as a local property even in
1001 // case of callbacks in the prototype chain (this rules out using
1002 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
1003 // this.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001004 // Note that objects can have hidden prototypes, so we need to traverse
1005 // the whole chain of hidden prototypes to do a 'local' lookup.
1006 JSObject* real_holder = global;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001007 LookupResult lookup;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001008 while (true) {
1009 real_holder->LocalLookup(*name, &lookup);
1010 if (lookup.IsProperty()) {
1011 // Determine if this is a redeclaration of something read-only.
1012 if (lookup.IsReadOnly()) {
1013 // If we found readonly property on one of hidden prototypes,
1014 // just shadow it.
1015 if (real_holder != Top::context()->global()) break;
1016 return ThrowRedeclarationError("const", name);
1017 }
1018
1019 // Determine if this is a redeclaration of an intercepted read-only
1020 // property and figure out if the property exists at all.
1021 bool found = true;
1022 PropertyType type = lookup.type();
1023 if (type == INTERCEPTOR) {
1024 HandleScope handle_scope;
1025 Handle<JSObject> holder(real_holder);
1026 PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
1027 real_holder = *holder;
1028 if (intercepted == ABSENT) {
1029 // The interceptor claims the property isn't there. We need to
1030 // make sure to introduce it.
1031 found = false;
1032 } else if ((intercepted & READ_ONLY) != 0) {
1033 // The property is present, but read-only. Since we're trying to
1034 // overwrite it with a variable declaration we must throw a
1035 // re-declaration error. However if we found readonly property
1036 // on one of hidden prototypes, just shadow it.
1037 if (real_holder != Top::context()->global()) break;
1038 return ThrowRedeclarationError("const", name);
1039 }
1040 }
1041
1042 if (found && !assign) {
1043 // The global property is there and we're not assigning any value
1044 // to it. Just return.
1045 return Heap::undefined_value();
1046 }
1047
1048 // Assign the value (or undefined) to the property.
1049 Object* value = (assign) ? args[1] : Heap::undefined_value();
1050 return real_holder->SetProperty(&lookup, *name, value, attributes);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001051 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001052
1053 Object* proto = real_holder->GetPrototype();
1054 if (!proto->IsJSObject())
1055 break;
1056
1057 if (!JSObject::cast(proto)->map()->is_hidden_prototype())
1058 break;
1059
1060 real_holder = JSObject::cast(proto);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001061 }
1062
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001063 global = Top::context()->global();
1064 if (assign) {
1065 return global->IgnoreAttributesAndSetLocalProperty(*name,
1066 args[1],
1067 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001068 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001069 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001070}
1071
1072
1073static Object* Runtime_InitializeConstGlobal(Arguments args) {
1074 // All constants are declared with an initial value. The name
1075 // of the constant is the first argument and the initial value
1076 // is the second.
1077 RUNTIME_ASSERT(args.length() == 2);
1078 CONVERT_ARG_CHECKED(String, name, 0);
1079 Handle<Object> value = args.at<Object>(1);
1080
1081 // Get the current global object from top.
1082 GlobalObject* global = Top::context()->global();
1083
1084 // According to ECMA-262, section 12.2, page 62, the property must
1085 // not be deletable. Since it's a const, it must be READ_ONLY too.
1086 PropertyAttributes attributes =
1087 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
1088
1089 // Lookup the property locally in the global object. If it isn't
1090 // there, we add the property and take special precautions to always
1091 // add it as a local property even in case of callbacks in the
1092 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001093 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001094 LookupResult lookup;
1095 global->LocalLookup(*name, &lookup);
1096 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001097 return global->IgnoreAttributesAndSetLocalProperty(*name,
1098 *value,
1099 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001100 }
1101
1102 // Determine if this is a redeclaration of something not
1103 // read-only. In case the result is hidden behind an interceptor we
1104 // need to ask it for the property attributes.
1105 if (!lookup.IsReadOnly()) {
1106 if (lookup.type() != INTERCEPTOR) {
1107 return ThrowRedeclarationError("var", name);
1108 }
1109
1110 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
1111
1112 // Throw re-declaration error if the intercepted property is present
1113 // but not read-only.
1114 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
1115 return ThrowRedeclarationError("var", name);
1116 }
1117
1118 // Restore global object from context (in case of GC) and continue
1119 // with setting the value because the property is either absent or
1120 // read-only. We also have to do redo the lookup.
1121 global = Top::context()->global();
1122
1123 // BUG 1213579: Handle the case where we have to set a read-only
1124 // property through an interceptor and only do it if it's
1125 // uninitialized, e.g. the hole. Nirk...
1126 global->SetProperty(*name, *value, attributes);
1127 return *value;
1128 }
1129
1130 // Set the value, but only we're assigning the initial value to a
1131 // constant. For now, we determine this by checking if the
1132 // current value is the hole.
1133 PropertyType type = lookup.type();
1134 if (type == FIELD) {
1135 FixedArray* properties = global->properties();
1136 int index = lookup.GetFieldIndex();
1137 if (properties->get(index)->IsTheHole()) {
1138 properties->set(index, *value);
1139 }
1140 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001141 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
1142 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001143 }
1144 } else {
1145 // Ignore re-initialization of constants that have already been
1146 // assigned a function value.
1147 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
1148 }
1149
1150 // Use the set value as the result of the operation.
1151 return *value;
1152}
1153
1154
1155static Object* Runtime_InitializeConstContextSlot(Arguments args) {
1156 HandleScope scope;
1157 ASSERT(args.length() == 3);
1158
1159 Handle<Object> value(args[0]);
1160 ASSERT(!value->IsTheHole());
1161 CONVERT_ARG_CHECKED(Context, context, 1);
1162 Handle<String> name(String::cast(args[2]));
1163
1164 // Initializations are always done in the function context.
1165 context = Handle<Context>(context->fcontext());
1166
1167 int index;
1168 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001169 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001170 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001171 context->Lookup(name, flags, &index, &attributes);
1172
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001173 // In most situations, the property introduced by the const
1174 // declaration should be present in the context extension object.
1175 // However, because declaration and initialization are separate, the
1176 // property might have been deleted (if it was introduced by eval)
1177 // before we reach the initialization point.
1178 //
1179 // Example:
1180 //
1181 // function f() { eval("delete x; const x;"); }
1182 //
1183 // In that case, the initialization behaves like a normal assignment
1184 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001185 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001186 // Property was found in a context.
1187 if (holder->IsContext()) {
1188 // The holder cannot be the function context. If it is, there
1189 // should have been a const redeclaration error when declaring
1190 // the const property.
1191 ASSERT(!holder.is_identical_to(context));
1192 if ((attributes & READ_ONLY) == 0) {
1193 Handle<Context>::cast(holder)->set(index, *value);
1194 }
1195 } else {
1196 // The holder is an arguments object.
1197 ASSERT((attributes & READ_ONLY) == 0);
1198 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001199 }
1200 return *value;
1201 }
1202
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001203 // The property could not be found, we introduce it in the global
1204 // context.
1205 if (attributes == ABSENT) {
1206 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1207 SetProperty(global, name, value, NONE);
1208 return *value;
1209 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001210
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001211 // The property was present in a context extension object.
1212 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001213
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001214 if (*context_ext == context->extension()) {
1215 // This is the property that was introduced by the const
1216 // declaration. Set it if it hasn't been set before. NOTE: We
1217 // cannot use GetProperty() to get the current value as it
1218 // 'unholes' the value.
1219 LookupResult lookup;
1220 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1221 ASSERT(lookup.IsProperty()); // the property was declared
1222 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1223
1224 PropertyType type = lookup.type();
1225 if (type == FIELD) {
1226 FixedArray* properties = context_ext->properties();
1227 int index = lookup.GetFieldIndex();
1228 if (properties->get(index)->IsTheHole()) {
1229 properties->set(index, *value);
1230 }
1231 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001232 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
1233 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001234 }
1235 } else {
1236 // We should not reach here. Any real, named property should be
1237 // either a field or a dictionary slot.
1238 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001239 }
1240 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001241 // The property was found in a different context extension object.
1242 // Set it if it is not a read-only property.
1243 if ((attributes & READ_ONLY) == 0) {
1244 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1245 // Setting a property might throw an exception. Exceptions
1246 // are converted to empty handles in handle operations. We
1247 // need to convert back to exceptions here.
1248 if (set.is_null()) {
1249 ASSERT(Top::has_pending_exception());
1250 return Failure::Exception();
1251 }
1252 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001253 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001254
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001255 return *value;
1256}
1257
1258
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001259static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1260 Arguments args) {
1261 HandleScope scope;
1262 ASSERT(args.length() == 2);
1263 CONVERT_ARG_CHECKED(JSObject, object, 0);
1264 CONVERT_SMI_CHECKED(properties, args[1]);
1265 if (object->HasFastProperties()) {
1266 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1267 }
1268 return *object;
1269}
1270
1271
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001272static Object* Runtime_RegExpExec(Arguments args) {
1273 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001274 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001275 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1276 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001277 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001278 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001279 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001280 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001281 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001282 RUNTIME_ASSERT(index >= 0);
1283 RUNTIME_ASSERT(index <= subject->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001284 Counters::regexp_entry_runtime.Increment();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001285 Handle<Object> result = RegExpImpl::Exec(regexp,
1286 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001287 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001288 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001289 if (result.is_null()) return Failure::Exception();
1290 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001291}
1292
1293
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00001294static Object* Runtime_RegExpConstructResult(Arguments args) {
1295 ASSERT(args.length() == 3);
1296 CONVERT_SMI_CHECKED(elements_count, args[0]);
1297 if (elements_count > JSArray::kMaxFastElementsLength) {
1298 return Top::ThrowIllegalOperation();
1299 }
1300 Object* new_object = Heap::AllocateFixedArrayWithHoles(elements_count);
1301 if (new_object->IsFailure()) return new_object;
1302 FixedArray* elements = FixedArray::cast(new_object);
1303 new_object = Heap::AllocateRaw(JSRegExpResult::kSize,
1304 NEW_SPACE,
1305 OLD_POINTER_SPACE);
1306 if (new_object->IsFailure()) return new_object;
1307 {
1308 AssertNoAllocation no_gc;
1309 HandleScope scope;
1310 reinterpret_cast<HeapObject*>(new_object)->
1311 set_map(Top::global_context()->regexp_result_map());
1312 }
1313 JSArray* array = JSArray::cast(new_object);
1314 array->set_properties(Heap::empty_fixed_array());
1315 array->set_elements(elements);
1316 array->set_length(Smi::FromInt(elements_count));
1317 // Write in-object properties after the length of the array.
1318 array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, args[1]);
1319 array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, args[2]);
1320 return array;
1321}
1322
1323
lrn@chromium.org25156de2010-04-06 13:10:27 +00001324static Object* Runtime_RegExpInitializeObject(Arguments args) {
1325 AssertNoAllocation no_alloc;
1326 ASSERT(args.length() == 5);
1327 CONVERT_CHECKED(JSRegExp, regexp, args[0]);
1328 CONVERT_CHECKED(String, source, args[1]);
1329
1330 Object* global = args[2];
1331 if (!global->IsTrue()) global = Heap::false_value();
1332
1333 Object* ignoreCase = args[3];
1334 if (!ignoreCase->IsTrue()) ignoreCase = Heap::false_value();
1335
1336 Object* multiline = args[4];
1337 if (!multiline->IsTrue()) multiline = Heap::false_value();
1338
1339 Map* map = regexp->map();
1340 Object* constructor = map->constructor();
1341 if (constructor->IsJSFunction() &&
1342 JSFunction::cast(constructor)->initial_map() == map) {
1343 // If we still have the original map, set in-object properties directly.
1344 regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, source);
1345 // TODO(lrn): Consider skipping write barrier on booleans as well.
1346 // Both true and false should be in oldspace at all times.
1347 regexp->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, global);
1348 regexp->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex, ignoreCase);
1349 regexp->InObjectPropertyAtPut(JSRegExp::kMultilineFieldIndex, multiline);
1350 regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex,
1351 Smi::FromInt(0),
1352 SKIP_WRITE_BARRIER);
1353 return regexp;
1354 }
1355
1356 // Map has changed, so use generic, but slower, method.
1357 PropertyAttributes final =
1358 static_cast<PropertyAttributes>(READ_ONLY | DONT_ENUM | DONT_DELETE);
1359 PropertyAttributes writable =
1360 static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
1361 regexp->IgnoreAttributesAndSetLocalProperty(Heap::source_symbol(),
1362 source,
1363 final);
1364 regexp->IgnoreAttributesAndSetLocalProperty(Heap::global_symbol(),
1365 global,
1366 final);
1367 regexp->IgnoreAttributesAndSetLocalProperty(Heap::ignore_case_symbol(),
1368 ignoreCase,
1369 final);
1370 regexp->IgnoreAttributesAndSetLocalProperty(Heap::multiline_symbol(),
1371 multiline,
1372 final);
1373 regexp->IgnoreAttributesAndSetLocalProperty(Heap::last_index_symbol(),
1374 Smi::FromInt(0),
1375 writable);
1376 return regexp;
1377}
1378
1379
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001380static Object* Runtime_FinishArrayPrototypeSetup(Arguments args) {
1381 HandleScope scope;
1382 ASSERT(args.length() == 1);
1383 CONVERT_ARG_CHECKED(JSArray, prototype, 0);
1384 // This is necessary to enable fast checks for absence of elements
1385 // on Array.prototype and below.
1386 prototype->set_elements(Heap::empty_fixed_array());
1387 return Smi::FromInt(0);
1388}
1389
1390
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001391static Handle<JSFunction> InstallBuiltin(Handle<JSObject> holder,
1392 const char* name,
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001393 Builtins::Name builtin_name) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001394 Handle<String> key = Factory::LookupAsciiSymbol(name);
1395 Handle<Code> code(Builtins::builtin(builtin_name));
1396 Handle<JSFunction> optimized = Factory::NewFunction(key,
1397 JS_OBJECT_TYPE,
1398 JSObject::kHeaderSize,
1399 code,
1400 false);
1401 optimized->shared()->DontAdaptArguments();
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001402 SetProperty(holder, key, optimized, NONE);
1403 return optimized;
1404}
1405
1406
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001407static Object* Runtime_SpecialArrayFunctions(Arguments args) {
1408 HandleScope scope;
1409 ASSERT(args.length() == 1);
1410 CONVERT_ARG_CHECKED(JSObject, holder, 0);
1411
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001412 InstallBuiltin(holder, "pop", Builtins::ArrayPop);
1413 InstallBuiltin(holder, "push", Builtins::ArrayPush);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001414 InstallBuiltin(holder, "shift", Builtins::ArrayShift);
1415 InstallBuiltin(holder, "unshift", Builtins::ArrayUnshift);
1416 InstallBuiltin(holder, "slice", Builtins::ArraySlice);
1417 InstallBuiltin(holder, "splice", Builtins::ArraySplice);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001418 InstallBuiltin(holder, "concat", Builtins::ArrayConcat);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001419
1420 return *holder;
1421}
1422
1423
ager@chromium.org357bf652010-04-12 11:30:10 +00001424static Object* Runtime_GetGlobalReceiver(Arguments args) {
1425 // Returns a real global receiver, not one of builtins object.
1426 Context* global_context = Top::context()->global()->global_context();
1427 return global_context->global()->global_receiver();
1428}
1429
1430
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001431static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1432 HandleScope scope;
1433 ASSERT(args.length() == 4);
1434 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1435 int index = Smi::cast(args[1])->value();
1436 Handle<String> pattern = args.at<String>(2);
1437 Handle<String> flags = args.at<String>(3);
1438
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001439 // Get the RegExp function from the context in the literals array.
1440 // This is the RegExp function from the context in which the
1441 // function was created. We do not use the RegExp function from the
1442 // current global context because this might be the RegExp function
1443 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001444 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001445 Handle<JSFunction>(
1446 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001447 // Compute the regular expression literal.
1448 bool has_pending_exception;
1449 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001450 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1451 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001452 if (has_pending_exception) {
1453 ASSERT(Top::has_pending_exception());
1454 return Failure::Exception();
1455 }
1456 literals->set(index, *regexp);
1457 return *regexp;
1458}
1459
1460
1461static Object* Runtime_FunctionGetName(Arguments args) {
1462 NoHandleAllocation ha;
1463 ASSERT(args.length() == 1);
1464
1465 CONVERT_CHECKED(JSFunction, f, args[0]);
1466 return f->shared()->name();
1467}
1468
1469
ager@chromium.org236ad962008-09-25 09:45:57 +00001470static Object* Runtime_FunctionSetName(Arguments args) {
1471 NoHandleAllocation ha;
1472 ASSERT(args.length() == 2);
1473
1474 CONVERT_CHECKED(JSFunction, f, args[0]);
1475 CONVERT_CHECKED(String, name, args[1]);
1476 f->shared()->set_name(name);
1477 return Heap::undefined_value();
1478}
1479
1480
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001481static Object* Runtime_FunctionRemovePrototype(Arguments args) {
1482 NoHandleAllocation ha;
1483 ASSERT(args.length() == 1);
1484
1485 CONVERT_CHECKED(JSFunction, f, args[0]);
1486 Object* obj = f->RemovePrototype();
1487 if (obj->IsFailure()) return obj;
1488
1489 return Heap::undefined_value();
1490}
1491
1492
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001493static Object* Runtime_FunctionGetScript(Arguments args) {
1494 HandleScope scope;
1495 ASSERT(args.length() == 1);
1496
1497 CONVERT_CHECKED(JSFunction, fun, args[0]);
1498 Handle<Object> script = Handle<Object>(fun->shared()->script());
1499 if (!script->IsScript()) return Heap::undefined_value();
1500
1501 return *GetScriptWrapper(Handle<Script>::cast(script));
1502}
1503
1504
1505static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1506 NoHandleAllocation ha;
1507 ASSERT(args.length() == 1);
1508
1509 CONVERT_CHECKED(JSFunction, f, args[0]);
1510 return f->shared()->GetSourceCode();
1511}
1512
1513
1514static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1515 NoHandleAllocation ha;
1516 ASSERT(args.length() == 1);
1517
1518 CONVERT_CHECKED(JSFunction, fun, args[0]);
1519 int pos = fun->shared()->start_position();
1520 return Smi::FromInt(pos);
1521}
1522
1523
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001524static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1525 ASSERT(args.length() == 2);
1526
1527 CONVERT_CHECKED(JSFunction, fun, args[0]);
1528 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1529
1530 Code* code = fun->code();
1531 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1532
1533 Address pc = code->address() + offset;
1534 return Smi::FromInt(fun->code()->SourcePosition(pc));
1535}
1536
1537
1538
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001539static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1540 NoHandleAllocation ha;
1541 ASSERT(args.length() == 2);
1542
1543 CONVERT_CHECKED(JSFunction, fun, args[0]);
1544 CONVERT_CHECKED(String, name, args[1]);
1545 fun->SetInstanceClassName(name);
1546 return Heap::undefined_value();
1547}
1548
1549
1550static Object* Runtime_FunctionSetLength(Arguments args) {
1551 NoHandleAllocation ha;
1552 ASSERT(args.length() == 2);
1553
1554 CONVERT_CHECKED(JSFunction, fun, args[0]);
1555 CONVERT_CHECKED(Smi, length, args[1]);
1556 fun->shared()->set_length(length->value());
1557 return length;
1558}
1559
1560
1561static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001562 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001563 ASSERT(args.length() == 2);
1564
1565 CONVERT_CHECKED(JSFunction, fun, args[0]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001566 ASSERT(fun->should_have_prototype());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001567 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1568 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001569 return args[0]; // return TOS
1570}
1571
1572
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001573static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1574 NoHandleAllocation ha;
1575 ASSERT(args.length() == 1);
1576
1577 CONVERT_CHECKED(JSFunction, f, args[0]);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001578 return f->shared()->IsApiFunction() ? Heap::true_value()
1579 : Heap::false_value();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001580}
1581
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001582static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1583 NoHandleAllocation ha;
1584 ASSERT(args.length() == 1);
1585
1586 CONVERT_CHECKED(JSFunction, f, args[0]);
1587 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1588}
1589
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001590
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001591static Object* Runtime_SetCode(Arguments args) {
1592 HandleScope scope;
1593 ASSERT(args.length() == 2);
1594
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001595 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001596 Handle<Object> code = args.at<Object>(1);
1597
1598 Handle<Context> context(target->context());
1599
1600 if (!code->IsNull()) {
1601 RUNTIME_ASSERT(code->IsJSFunction());
1602 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001603 Handle<SharedFunctionInfo> shared(fun->shared());
1604 SetExpectedNofProperties(target, shared->expected_nof_properties());
1605
1606 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001607 return Failure::Exception();
1608 }
1609 // Set the code, formal parameter count, and the length of the target
1610 // function.
1611 target->set_code(fun->code());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001612 target->shared()->set_length(shared->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001613 target->shared()->set_formal_parameter_count(
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001614 shared->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001615 // Set the source code of the target function to undefined.
1616 // SetCode is only used for built-in constructors like String,
1617 // Array, and Object, and some web code
1618 // doesn't like seeing source code for constructors.
1619 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001620 // Clear the optimization hints related to the compiled code as these are no
1621 // longer valid when the code is overwritten.
1622 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001623 context = Handle<Context>(fun->context());
1624
1625 // Make sure we get a fresh copy of the literal vector to avoid
1626 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001627 int number_of_literals = fun->NumberOfLiterals();
1628 Handle<FixedArray> literals =
1629 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001630 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001631 // Insert the object, regexp and array functions in the literals
1632 // array prefix. These are the functions that will be used when
1633 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001634 literals->set(JSFunction::kLiteralGlobalContextIndex,
1635 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001636 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001637 // It's okay to skip the write barrier here because the literals
1638 // are guaranteed to be in old space.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001639 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001640 }
1641
1642 target->set_context(*context);
1643 return *target;
1644}
1645
1646
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001647static Object* CharFromCode(Object* char_code) {
1648 uint32_t code;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001649 if (char_code->ToArrayIndex(&code)) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001650 if (code <= 0xffff) {
1651 return Heap::LookupSingleCharacterStringFromCode(code);
1652 }
1653 }
1654 return Heap::empty_string();
1655}
1656
1657
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001658static Object* Runtime_StringCharCodeAt(Arguments args) {
1659 NoHandleAllocation ha;
1660 ASSERT(args.length() == 2);
1661
1662 CONVERT_CHECKED(String, subject, args[0]);
1663 Object* index = args[1];
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001664 RUNTIME_ASSERT(index->IsNumber());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001665
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001666 uint32_t i = 0;
1667 if (index->IsSmi()) {
1668 int value = Smi::cast(index)->value();
1669 if (value < 0) return Heap::nan_value();
1670 i = value;
1671 } else {
1672 ASSERT(index->IsHeapNumber());
1673 double value = HeapNumber::cast(index)->value();
1674 i = static_cast<uint32_t>(DoubleToInteger(value));
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001675 }
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001676
1677 // Flatten the string. If someone wants to get a char at an index
1678 // in a cons string, it is likely that more indices will be
1679 // accessed.
1680 Object* flat = subject->TryFlatten();
1681 if (flat->IsFailure()) return flat;
1682 subject = String::cast(flat);
1683
1684 if (i >= static_cast<uint32_t>(subject->length())) {
1685 return Heap::nan_value();
1686 }
1687
1688 return Smi::FromInt(subject->Get(i));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001689}
1690
1691
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001692static Object* Runtime_CharFromCode(Arguments args) {
1693 NoHandleAllocation ha;
1694 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001695 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001696}
1697
lrn@chromium.org25156de2010-04-06 13:10:27 +00001698
1699class FixedArrayBuilder {
1700 public:
1701 explicit FixedArrayBuilder(int initial_capacity)
1702 : array_(Factory::NewFixedArrayWithHoles(initial_capacity)),
1703 length_(0) {
1704 // Require a non-zero initial size. Ensures that doubling the size to
1705 // extend the array will work.
1706 ASSERT(initial_capacity > 0);
1707 }
1708
1709 explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
1710 : array_(backing_store),
1711 length_(0) {
1712 // Require a non-zero initial size. Ensures that doubling the size to
1713 // extend the array will work.
1714 ASSERT(backing_store->length() > 0);
1715 }
1716
1717 bool HasCapacity(int elements) {
1718 int length = array_->length();
1719 int required_length = length_ + elements;
1720 return (length >= required_length);
1721 }
1722
1723 void EnsureCapacity(int elements) {
1724 int length = array_->length();
1725 int required_length = length_ + elements;
1726 if (length < required_length) {
1727 int new_length = length;
1728 do {
1729 new_length *= 2;
1730 } while (new_length < required_length);
1731 Handle<FixedArray> extended_array =
1732 Factory::NewFixedArrayWithHoles(new_length);
1733 array_->CopyTo(0, *extended_array, 0, length_);
1734 array_ = extended_array;
1735 }
1736 }
1737
1738 void Add(Object* value) {
1739 ASSERT(length_ < capacity());
1740 array_->set(length_, value);
1741 length_++;
1742 }
1743
1744 void Add(Smi* value) {
1745 ASSERT(length_ < capacity());
1746 array_->set(length_, value);
1747 length_++;
1748 }
1749
1750 Handle<FixedArray> array() {
1751 return array_;
1752 }
1753
1754 int length() {
1755 return length_;
1756 }
1757
1758 int capacity() {
1759 return array_->length();
1760 }
1761
1762 Handle<JSArray> ToJSArray() {
1763 Handle<JSArray> result_array = Factory::NewJSArrayWithElements(array_);
1764 result_array->set_length(Smi::FromInt(length_));
1765 return result_array;
1766 }
1767
1768 Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
1769 target_array->set_elements(*array_);
1770 target_array->set_length(Smi::FromInt(length_));
1771 return target_array;
1772 }
1773
1774 private:
1775 Handle<FixedArray> array_;
1776 int length_;
1777};
1778
1779
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001780// Forward declarations.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001781const int kStringBuilderConcatHelperLengthBits = 11;
1782const int kStringBuilderConcatHelperPositionBits = 19;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001783
1784template <typename schar>
1785static inline void StringBuilderConcatHelper(String*,
1786 schar*,
1787 FixedArray*,
1788 int);
1789
lrn@chromium.org25156de2010-04-06 13:10:27 +00001790typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
1791 StringBuilderSubstringLength;
1792typedef BitField<int,
1793 kStringBuilderConcatHelperLengthBits,
1794 kStringBuilderConcatHelperPositionBits>
1795 StringBuilderSubstringPosition;
1796
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001797
1798class ReplacementStringBuilder {
1799 public:
1800 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
lrn@chromium.org25156de2010-04-06 13:10:27 +00001801 : array_builder_(estimated_part_count),
1802 subject_(subject),
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001803 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001804 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001805 // Require a non-zero initial size. Ensures that doubling the size to
1806 // extend the array will work.
1807 ASSERT(estimated_part_count > 0);
1808 }
1809
lrn@chromium.org25156de2010-04-06 13:10:27 +00001810 static inline void AddSubjectSlice(FixedArrayBuilder* builder,
1811 int from,
1812 int to) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001813 ASSERT(from >= 0);
1814 int length = to - from;
1815 ASSERT(length > 0);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001816 if (StringBuilderSubstringLength::is_valid(length) &&
1817 StringBuilderSubstringPosition::is_valid(from)) {
1818 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1819 StringBuilderSubstringPosition::encode(from);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001820 builder->Add(Smi::FromInt(encoded_slice));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001821 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001822 // Otherwise encode as two smis.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001823 builder->Add(Smi::FromInt(-length));
1824 builder->Add(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001825 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00001826 }
1827
1828
1829 void EnsureCapacity(int elements) {
1830 array_builder_.EnsureCapacity(elements);
1831 }
1832
1833
1834 void AddSubjectSlice(int from, int to) {
1835 AddSubjectSlice(&array_builder_, from, to);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001836 IncrementCharacterCount(to - from);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001837 }
1838
1839
1840 void AddString(Handle<String> string) {
1841 int length = string->length();
1842 ASSERT(length > 0);
1843 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001844 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001845 is_ascii_ = false;
1846 }
1847 IncrementCharacterCount(length);
1848 }
1849
1850
1851 Handle<String> ToString() {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001852 if (array_builder_.length() == 0) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001853 return Factory::empty_string();
1854 }
1855
1856 Handle<String> joined_string;
1857 if (is_ascii_) {
1858 joined_string = NewRawAsciiString(character_count_);
1859 AssertNoAllocation no_alloc;
1860 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1861 char* char_buffer = seq->GetChars();
1862 StringBuilderConcatHelper(*subject_,
1863 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001864 *array_builder_.array(),
1865 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001866 } else {
1867 // Non-ASCII.
1868 joined_string = NewRawTwoByteString(character_count_);
1869 AssertNoAllocation no_alloc;
1870 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1871 uc16* char_buffer = seq->GetChars();
1872 StringBuilderConcatHelper(*subject_,
1873 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001874 *array_builder_.array(),
1875 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001876 }
1877 return joined_string;
1878 }
1879
1880
1881 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001882 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001883 V8::FatalProcessOutOfMemory("String.replace result too large.");
1884 }
1885 character_count_ += by;
1886 }
1887
lrn@chromium.org25156de2010-04-06 13:10:27 +00001888 Handle<JSArray> GetParts() {
1889 Handle<JSArray> result =
1890 Factory::NewJSArrayWithElements(array_builder_.array());
1891 result->set_length(Smi::FromInt(array_builder_.length()));
1892 return result;
1893 }
kmillikin@chromium.orgd9825192010-03-30 08:36:16 +00001894
lrn@chromium.org25156de2010-04-06 13:10:27 +00001895 private:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001896 Handle<String> NewRawAsciiString(int size) {
1897 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1898 }
1899
1900
1901 Handle<String> NewRawTwoByteString(int size) {
1902 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1903 }
1904
1905
1906 void AddElement(Object* element) {
1907 ASSERT(element->IsSmi() || element->IsString());
lrn@chromium.org25156de2010-04-06 13:10:27 +00001908 ASSERT(array_builder_.capacity() > array_builder_.length());
1909 array_builder_.Add(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001910 }
1911
lrn@chromium.org25156de2010-04-06 13:10:27 +00001912 FixedArrayBuilder array_builder_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001913 Handle<String> subject_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001914 int character_count_;
1915 bool is_ascii_;
1916};
1917
1918
1919class CompiledReplacement {
1920 public:
1921 CompiledReplacement()
1922 : parts_(1), replacement_substrings_(0) {}
1923
1924 void Compile(Handle<String> replacement,
1925 int capture_count,
1926 int subject_length);
1927
1928 void Apply(ReplacementStringBuilder* builder,
1929 int match_from,
1930 int match_to,
1931 Handle<JSArray> last_match_info);
1932
1933 // Number of distinct parts of the replacement pattern.
1934 int parts() {
1935 return parts_.length();
1936 }
1937 private:
1938 enum PartType {
1939 SUBJECT_PREFIX = 1,
1940 SUBJECT_SUFFIX,
1941 SUBJECT_CAPTURE,
1942 REPLACEMENT_SUBSTRING,
1943 REPLACEMENT_STRING,
1944
1945 NUMBER_OF_PART_TYPES
1946 };
1947
1948 struct ReplacementPart {
1949 static inline ReplacementPart SubjectMatch() {
1950 return ReplacementPart(SUBJECT_CAPTURE, 0);
1951 }
1952 static inline ReplacementPart SubjectCapture(int capture_index) {
1953 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1954 }
1955 static inline ReplacementPart SubjectPrefix() {
1956 return ReplacementPart(SUBJECT_PREFIX, 0);
1957 }
1958 static inline ReplacementPart SubjectSuffix(int subject_length) {
1959 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1960 }
1961 static inline ReplacementPart ReplacementString() {
1962 return ReplacementPart(REPLACEMENT_STRING, 0);
1963 }
1964 static inline ReplacementPart ReplacementSubString(int from, int to) {
1965 ASSERT(from >= 0);
1966 ASSERT(to > from);
1967 return ReplacementPart(-from, to);
1968 }
1969
1970 // If tag <= 0 then it is the negation of a start index of a substring of
1971 // the replacement pattern, otherwise it's a value from PartType.
1972 ReplacementPart(int tag, int data)
1973 : tag(tag), data(data) {
1974 // Must be non-positive or a PartType value.
1975 ASSERT(tag < NUMBER_OF_PART_TYPES);
1976 }
1977 // Either a value of PartType or a non-positive number that is
1978 // the negation of an index into the replacement string.
1979 int tag;
1980 // The data value's interpretation depends on the value of tag:
1981 // tag == SUBJECT_PREFIX ||
1982 // tag == SUBJECT_SUFFIX: data is unused.
1983 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1984 // tag == REPLACEMENT_SUBSTRING ||
1985 // tag == REPLACEMENT_STRING: data is index into array of substrings
1986 // of the replacement string.
1987 // tag <= 0: Temporary representation of the substring of the replacement
1988 // string ranging over -tag .. data.
1989 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1990 // substring objects.
1991 int data;
1992 };
1993
1994 template<typename Char>
1995 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1996 Vector<Char> characters,
1997 int capture_count,
1998 int subject_length) {
1999 int length = characters.length();
2000 int last = 0;
2001 for (int i = 0; i < length; i++) {
2002 Char c = characters[i];
2003 if (c == '$') {
2004 int next_index = i + 1;
2005 if (next_index == length) { // No next character!
2006 break;
2007 }
2008 Char c2 = characters[next_index];
2009 switch (c2) {
2010 case '$':
2011 if (i > last) {
2012 // There is a substring before. Include the first "$".
2013 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
2014 last = next_index + 1; // Continue after the second "$".
2015 } else {
2016 // Let the next substring start with the second "$".
2017 last = next_index;
2018 }
2019 i = next_index;
2020 break;
2021 case '`':
2022 if (i > last) {
2023 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2024 }
2025 parts->Add(ReplacementPart::SubjectPrefix());
2026 i = next_index;
2027 last = i + 1;
2028 break;
2029 case '\'':
2030 if (i > last) {
2031 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2032 }
2033 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
2034 i = next_index;
2035 last = i + 1;
2036 break;
2037 case '&':
2038 if (i > last) {
2039 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2040 }
2041 parts->Add(ReplacementPart::SubjectMatch());
2042 i = next_index;
2043 last = i + 1;
2044 break;
2045 case '0':
2046 case '1':
2047 case '2':
2048 case '3':
2049 case '4':
2050 case '5':
2051 case '6':
2052 case '7':
2053 case '8':
2054 case '9': {
2055 int capture_ref = c2 - '0';
2056 if (capture_ref > capture_count) {
2057 i = next_index;
2058 continue;
2059 }
2060 int second_digit_index = next_index + 1;
2061 if (second_digit_index < length) {
2062 // Peek ahead to see if we have two digits.
2063 Char c3 = characters[second_digit_index];
2064 if ('0' <= c3 && c3 <= '9') { // Double digits.
2065 int double_digit_ref = capture_ref * 10 + c3 - '0';
2066 if (double_digit_ref <= capture_count) {
2067 next_index = second_digit_index;
2068 capture_ref = double_digit_ref;
2069 }
2070 }
2071 }
2072 if (capture_ref > 0) {
2073 if (i > last) {
2074 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2075 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002076 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002077 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
2078 last = next_index + 1;
2079 }
2080 i = next_index;
2081 break;
2082 }
2083 default:
2084 i = next_index;
2085 break;
2086 }
2087 }
2088 }
2089 if (length > last) {
2090 if (last == 0) {
2091 parts->Add(ReplacementPart::ReplacementString());
2092 } else {
2093 parts->Add(ReplacementPart::ReplacementSubString(last, length));
2094 }
2095 }
2096 }
2097
2098 ZoneList<ReplacementPart> parts_;
2099 ZoneList<Handle<String> > replacement_substrings_;
2100};
2101
2102
2103void CompiledReplacement::Compile(Handle<String> replacement,
2104 int capture_count,
2105 int subject_length) {
2106 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00002107 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002108 AssertNoAllocation no_alloc;
2109 ParseReplacementPattern(&parts_,
2110 replacement->ToAsciiVector(),
2111 capture_count,
2112 subject_length);
2113 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00002114 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002115 AssertNoAllocation no_alloc;
2116
2117 ParseReplacementPattern(&parts_,
2118 replacement->ToUC16Vector(),
2119 capture_count,
2120 subject_length);
2121 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002122 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002123 int substring_index = 0;
2124 for (int i = 0, n = parts_.length(); i < n; i++) {
2125 int tag = parts_[i].tag;
2126 if (tag <= 0) { // A replacement string slice.
2127 int from = -tag;
2128 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002129 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002130 parts_[i].tag = REPLACEMENT_SUBSTRING;
2131 parts_[i].data = substring_index;
2132 substring_index++;
2133 } else if (tag == REPLACEMENT_STRING) {
2134 replacement_substrings_.Add(replacement);
2135 parts_[i].data = substring_index;
2136 substring_index++;
2137 }
2138 }
2139}
2140
2141
2142void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
2143 int match_from,
2144 int match_to,
2145 Handle<JSArray> last_match_info) {
2146 for (int i = 0, n = parts_.length(); i < n; i++) {
2147 ReplacementPart part = parts_[i];
2148 switch (part.tag) {
2149 case SUBJECT_PREFIX:
2150 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
2151 break;
2152 case SUBJECT_SUFFIX: {
2153 int subject_length = part.data;
2154 if (match_to < subject_length) {
2155 builder->AddSubjectSlice(match_to, subject_length);
2156 }
2157 break;
2158 }
2159 case SUBJECT_CAPTURE: {
2160 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002161 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002162 int from = RegExpImpl::GetCapture(match_info, capture * 2);
2163 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
2164 if (from >= 0 && to > from) {
2165 builder->AddSubjectSlice(from, to);
2166 }
2167 break;
2168 }
2169 case REPLACEMENT_SUBSTRING:
2170 case REPLACEMENT_STRING:
2171 builder->AddString(replacement_substrings_[part.data]);
2172 break;
2173 default:
2174 UNREACHABLE();
2175 }
2176 }
2177}
2178
2179
2180
2181static Object* StringReplaceRegExpWithString(String* subject,
2182 JSRegExp* regexp,
2183 String* replacement,
2184 JSArray* last_match_info) {
2185 ASSERT(subject->IsFlat());
2186 ASSERT(replacement->IsFlat());
2187
2188 HandleScope handles;
2189
2190 int length = subject->length();
2191 Handle<String> subject_handle(subject);
2192 Handle<JSRegExp> regexp_handle(regexp);
2193 Handle<String> replacement_handle(replacement);
2194 Handle<JSArray> last_match_info_handle(last_match_info);
2195 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2196 subject_handle,
2197 0,
2198 last_match_info_handle);
2199 if (match.is_null()) {
2200 return Failure::Exception();
2201 }
2202 if (match->IsNull()) {
2203 return *subject_handle;
2204 }
2205
2206 int capture_count = regexp_handle->CaptureCount();
2207
2208 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002209 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002210 CompiledReplacement compiled_replacement;
2211 compiled_replacement.Compile(replacement_handle,
2212 capture_count,
2213 length);
2214
2215 bool is_global = regexp_handle->GetFlags().is_global();
2216
2217 // Guessing the number of parts that the final result string is built
2218 // from. Global regexps can match any number of times, so we guess
2219 // conservatively.
2220 int expected_parts =
2221 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
2222 ReplacementStringBuilder builder(subject_handle, expected_parts);
2223
2224 // Index of end of last match.
2225 int prev = 0;
2226
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002227 // Number of parts added by compiled replacement plus preceeding
2228 // string and possibly suffix after last match. It is possible for
2229 // all components to use two elements when encoded as two smis.
2230 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002231 bool matched = true;
2232 do {
2233 ASSERT(last_match_info_handle->HasFastElements());
2234 // Increase the capacity of the builder before entering local handle-scope,
2235 // so its internal buffer can safely allocate a new handle if it grows.
2236 builder.EnsureCapacity(parts_added_per_loop);
2237
2238 HandleScope loop_scope;
2239 int start, end;
2240 {
2241 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002242 FixedArray* match_info_array =
2243 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002244
2245 ASSERT_EQ(capture_count * 2 + 2,
2246 RegExpImpl::GetLastCaptureCount(match_info_array));
2247 start = RegExpImpl::GetCapture(match_info_array, 0);
2248 end = RegExpImpl::GetCapture(match_info_array, 1);
2249 }
2250
2251 if (prev < start) {
2252 builder.AddSubjectSlice(prev, start);
2253 }
2254 compiled_replacement.Apply(&builder,
2255 start,
2256 end,
2257 last_match_info_handle);
2258 prev = end;
2259
2260 // Only continue checking for global regexps.
2261 if (!is_global) break;
2262
2263 // Continue from where the match ended, unless it was an empty match.
2264 int next = end;
2265 if (start == end) {
2266 next = end + 1;
2267 if (next > length) break;
2268 }
2269
2270 match = RegExpImpl::Exec(regexp_handle,
2271 subject_handle,
2272 next,
2273 last_match_info_handle);
2274 if (match.is_null()) {
2275 return Failure::Exception();
2276 }
2277 matched = !match->IsNull();
2278 } while (matched);
2279
2280 if (prev < length) {
2281 builder.AddSubjectSlice(prev, length);
2282 }
2283
2284 return *(builder.ToString());
2285}
2286
2287
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00002288template <typename ResultSeqString>
2289static Object* StringReplaceRegExpWithEmptyString(String* subject,
2290 JSRegExp* regexp,
2291 JSArray* last_match_info) {
2292 ASSERT(subject->IsFlat());
2293
2294 HandleScope handles;
2295
2296 Handle<String> subject_handle(subject);
2297 Handle<JSRegExp> regexp_handle(regexp);
2298 Handle<JSArray> last_match_info_handle(last_match_info);
2299 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2300 subject_handle,
2301 0,
2302 last_match_info_handle);
2303 if (match.is_null()) return Failure::Exception();
2304 if (match->IsNull()) return *subject_handle;
2305
2306 ASSERT(last_match_info_handle->HasFastElements());
2307
2308 HandleScope loop_scope;
2309 int start, end;
2310 {
2311 AssertNoAllocation match_info_array_is_not_in_a_handle;
2312 FixedArray* match_info_array =
2313 FixedArray::cast(last_match_info_handle->elements());
2314
2315 start = RegExpImpl::GetCapture(match_info_array, 0);
2316 end = RegExpImpl::GetCapture(match_info_array, 1);
2317 }
2318
2319 int length = subject->length();
2320 int new_length = length - (end - start);
2321 if (new_length == 0) {
2322 return Heap::empty_string();
2323 }
2324 Handle<ResultSeqString> answer;
2325 if (ResultSeqString::kHasAsciiEncoding) {
2326 answer =
2327 Handle<ResultSeqString>::cast(Factory::NewRawAsciiString(new_length));
2328 } else {
2329 answer =
2330 Handle<ResultSeqString>::cast(Factory::NewRawTwoByteString(new_length));
2331 }
2332
2333 // If the regexp isn't global, only match once.
2334 if (!regexp_handle->GetFlags().is_global()) {
2335 if (start > 0) {
2336 String::WriteToFlat(*subject_handle,
2337 answer->GetChars(),
2338 0,
2339 start);
2340 }
2341 if (end < length) {
2342 String::WriteToFlat(*subject_handle,
2343 answer->GetChars() + start,
2344 end,
2345 length);
2346 }
2347 return *answer;
2348 }
2349
2350 int prev = 0; // Index of end of last match.
2351 int next = 0; // Start of next search (prev unless last match was empty).
2352 int position = 0;
2353
2354 do {
2355 if (prev < start) {
2356 // Add substring subject[prev;start] to answer string.
2357 String::WriteToFlat(*subject_handle,
2358 answer->GetChars() + position,
2359 prev,
2360 start);
2361 position += start - prev;
2362 }
2363 prev = end;
2364 next = end;
2365 // Continue from where the match ended, unless it was an empty match.
2366 if (start == end) {
2367 next++;
2368 if (next > length) break;
2369 }
2370 match = RegExpImpl::Exec(regexp_handle,
2371 subject_handle,
2372 next,
2373 last_match_info_handle);
2374 if (match.is_null()) return Failure::Exception();
2375 if (match->IsNull()) break;
2376
2377 ASSERT(last_match_info_handle->HasFastElements());
2378 HandleScope loop_scope;
2379 {
2380 AssertNoAllocation match_info_array_is_not_in_a_handle;
2381 FixedArray* match_info_array =
2382 FixedArray::cast(last_match_info_handle->elements());
2383 start = RegExpImpl::GetCapture(match_info_array, 0);
2384 end = RegExpImpl::GetCapture(match_info_array, 1);
2385 }
2386 } while (true);
2387
2388 if (prev < length) {
2389 // Add substring subject[prev;length] to answer string.
2390 String::WriteToFlat(*subject_handle,
2391 answer->GetChars() + position,
2392 prev,
2393 length);
2394 position += length - prev;
2395 }
2396
2397 if (position == 0) {
2398 return Heap::empty_string();
2399 }
2400
2401 // Shorten string and fill
2402 int string_size = ResultSeqString::SizeFor(position);
2403 int allocated_string_size = ResultSeqString::SizeFor(new_length);
2404 int delta = allocated_string_size - string_size;
2405
2406 answer->set_length(position);
2407 if (delta == 0) return *answer;
2408
2409 Address end_of_string = answer->address() + string_size;
2410 Heap::CreateFillerObjectAt(end_of_string, delta);
2411
2412 return *answer;
2413}
2414
2415
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002416static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2417 ASSERT(args.length() == 4);
2418
2419 CONVERT_CHECKED(String, subject, args[0]);
2420 if (!subject->IsFlat()) {
2421 Object* flat_subject = subject->TryFlatten();
2422 if (flat_subject->IsFailure()) {
2423 return flat_subject;
2424 }
2425 subject = String::cast(flat_subject);
2426 }
2427
2428 CONVERT_CHECKED(String, replacement, args[2]);
2429 if (!replacement->IsFlat()) {
2430 Object* flat_replacement = replacement->TryFlatten();
2431 if (flat_replacement->IsFailure()) {
2432 return flat_replacement;
2433 }
2434 replacement = String::cast(flat_replacement);
2435 }
2436
2437 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2438 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2439
2440 ASSERT(last_match_info->HasFastElements());
2441
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00002442 if (replacement->length() == 0) {
2443 if (subject->HasOnlyAsciiChars()) {
2444 return StringReplaceRegExpWithEmptyString<SeqAsciiString>(
2445 subject, regexp, last_match_info);
2446 } else {
2447 return StringReplaceRegExpWithEmptyString<SeqTwoByteString>(
2448 subject, regexp, last_match_info);
2449 }
2450 }
2451
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002452 return StringReplaceRegExpWithString(subject,
2453 regexp,
2454 replacement,
2455 last_match_info);
2456}
2457
2458
ager@chromium.org7c537e22008-10-16 08:43:32 +00002459// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2460// limit, we can fix the size of tables.
2461static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002462// Reduce alphabet to this size.
2463static const int kBMAlphabetSize = 0x100;
2464// For patterns below this length, the skip length of Boyer-Moore is too short
2465// to compensate for the algorithmic overhead compared to simple brute force.
2466static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002467
ager@chromium.org7c537e22008-10-16 08:43:32 +00002468// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2469// shift. Only allows the last kBMMaxShift characters of the needle
2470// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002471class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002472 public:
2473 BMGoodSuffixBuffers() {}
2474 inline void init(int needle_length) {
2475 ASSERT(needle_length > 1);
2476 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2477 int len = needle_length - start;
2478 biased_suffixes_ = suffixes_ - start;
2479 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2480 for (int i = 0; i <= len; i++) {
2481 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002482 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002483 }
2484 inline int& suffix(int index) {
2485 ASSERT(biased_suffixes_ + index >= suffixes_);
2486 return biased_suffixes_[index];
2487 }
2488 inline int& shift(int index) {
2489 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2490 return biased_good_suffix_shift_[index];
2491 }
2492 private:
2493 int suffixes_[kBMMaxShift + 1];
2494 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002495 int* biased_suffixes_;
2496 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002497 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2498};
2499
2500// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002501static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002502static BMGoodSuffixBuffers bmgs_buffers;
2503
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002504// State of the string match tables.
2505// SIMPLE: No usable content in the buffers.
2506// BOYER_MOORE_HORSPOOL: The bad_char_occurences table has been populated.
2507// BOYER_MOORE: The bmgs_buffers tables have also been populated.
2508// Whenever starting with a new needle, one should call InitializeStringSearch
2509// to determine which search strategy to use, and in the case of a long-needle
2510// strategy, the call also initializes the algorithm to SIMPLE.
2511enum StringSearchAlgorithm { SIMPLE_SEARCH, BOYER_MOORE_HORSPOOL, BOYER_MOORE };
2512static StringSearchAlgorithm algorithm;
2513
2514
ager@chromium.org7c537e22008-10-16 08:43:32 +00002515// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002516template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002517static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern) {
2518 // Only preprocess at most kBMMaxShift last characters of pattern.
2519 int start = pattern.length() < kBMMaxShift ? 0
2520 : pattern.length() - kBMMaxShift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002521 // Run forwards to populate bad_char_table, so that *last* instance
2522 // of character equivalence class is the one registered.
2523 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002524 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2525 : kBMAlphabetSize;
2526 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002527 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002528 } else {
2529 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002530 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002531 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002532 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002533 for (int i = start; i < pattern.length() - 1; i++) {
2534 pchar c = pattern[i];
2535 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002536 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002537 }
2538}
2539
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002540
ager@chromium.org7c537e22008-10-16 08:43:32 +00002541template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002542static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002543 int m = pattern.length();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002544 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002545 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002546 // Compute Good Suffix tables.
2547 bmgs_buffers.init(m);
2548
2549 bmgs_buffers.shift(m-1) = 1;
2550 bmgs_buffers.suffix(m) = m + 1;
2551 pchar last_char = pattern[m - 1];
2552 int suffix = m + 1;
2553 for (int i = m; i > start;) {
2554 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2555 if (bmgs_buffers.shift(suffix) == len) {
2556 bmgs_buffers.shift(suffix) = suffix - i;
2557 }
2558 suffix = bmgs_buffers.suffix(suffix);
2559 }
2560 i--;
2561 suffix--;
2562 bmgs_buffers.suffix(i) = suffix;
2563 if (suffix == m) {
2564 // No suffix to extend, so we check against last_char only.
2565 while (i > start && pattern[i - 1] != last_char) {
2566 if (bmgs_buffers.shift(m) == len) {
2567 bmgs_buffers.shift(m) = m - i;
2568 }
2569 i--;
2570 bmgs_buffers.suffix(i) = m;
2571 }
2572 if (i > start) {
2573 i--;
2574 suffix--;
2575 bmgs_buffers.suffix(i) = suffix;
2576 }
2577 }
2578 }
2579 if (suffix < m) {
2580 for (int i = start; i <= m; i++) {
2581 if (bmgs_buffers.shift(i) == len) {
2582 bmgs_buffers.shift(i) = suffix - start;
2583 }
2584 if (i == suffix) {
2585 suffix = bmgs_buffers.suffix(suffix);
2586 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002587 }
2588 }
2589}
2590
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002591
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002592template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002593static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002594 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002595 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002596 }
2597 if (sizeof(pchar) == 1) {
2598 if (char_code > String::kMaxAsciiCharCode) {
2599 return -1;
2600 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002601 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002602 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002603 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002604}
2605
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002606
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002607// Restricted simplified Boyer-Moore string matching.
2608// Uses only the bad-shift table of Boyer-Moore and only uses it
2609// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002610template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002611static int BoyerMooreHorspool(Vector<const schar> subject,
2612 Vector<const pchar> pattern,
2613 int start_index,
2614 bool* complete) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002615 ASSERT(algorithm <= BOYER_MOORE_HORSPOOL);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002616 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002617 int m = pattern.length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002618
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002619 int badness = -m;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002620
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002621 // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002622 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002623 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002624 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002625 // Perform search
2626 for (idx = start_index; idx <= n - m;) {
2627 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002628 int c;
2629 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002630 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002631 int shift = j - bc_occ;
2632 idx += shift;
2633 badness += 1 - shift; // at most zero, so badness cannot increase.
2634 if (idx > n - m) {
2635 *complete = true;
2636 return -1;
2637 }
2638 }
2639 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002640 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002641 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002642 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002643 return idx;
2644 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002645 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002646 // Badness increases by the number of characters we have
2647 // checked, and decreases by the number of characters we
2648 // can skip by shifting. It's a measure of how we are doing
2649 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002650 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002651 if (badness > 0) {
2652 *complete = false;
2653 return idx;
2654 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002655 }
2656 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002657 *complete = true;
2658 return -1;
2659}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002660
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002661
2662template <typename schar, typename pchar>
2663static int BoyerMooreIndexOf(Vector<const schar> subject,
2664 Vector<const pchar> pattern,
2665 int idx) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002666 ASSERT(algorithm <= BOYER_MOORE);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002667 int n = subject.length();
2668 int m = pattern.length();
2669 // Only preprocess at most kBMMaxShift last characters of pattern.
2670 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2671
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002672 pchar last_char = pattern[m - 1];
2673 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002674 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002675 int j = m - 1;
2676 schar c;
2677 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002678 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002679 idx += shift;
2680 if (idx > n - m) {
2681 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002682 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002683 }
2684 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2685 if (j < 0) {
2686 return idx;
2687 } else if (j < start) {
2688 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002689 // Fall back on BMH shift.
2690 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002691 } else {
2692 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002693 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002694 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002695 if (gs_shift > shift) {
2696 shift = gs_shift;
2697 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002698 idx += shift;
2699 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002700 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002701
2702 return -1;
2703}
2704
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002705
2706template <typename schar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002707static inline int SingleCharIndexOf(Vector<const schar> string,
2708 schar pattern_char,
2709 int start_index) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002710 if (sizeof(schar) == 1) {
2711 const schar* pos = reinterpret_cast<const schar*>(
2712 memchr(string.start() + start_index,
2713 pattern_char,
2714 string.length() - start_index));
2715 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002716 return static_cast<int>(pos - string.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002717 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002718 for (int i = start_index, n = string.length(); i < n; i++) {
2719 if (pattern_char == string[i]) {
2720 return i;
2721 }
2722 }
2723 return -1;
2724}
2725
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002726
2727template <typename schar>
2728static int SingleCharLastIndexOf(Vector<const schar> string,
2729 schar pattern_char,
2730 int start_index) {
2731 for (int i = start_index; i >= 0; i--) {
2732 if (pattern_char == string[i]) {
2733 return i;
2734 }
2735 }
2736 return -1;
2737}
2738
2739
ager@chromium.org7c537e22008-10-16 08:43:32 +00002740// Trivial string search for shorter strings.
2741// On return, if "complete" is set to true, the return value is the
2742// final result of searching for the patter in the subject.
2743// If "complete" is set to false, the return value is the index where
2744// further checking should start, i.e., it's guaranteed that the pattern
2745// does not occur at a position prior to the returned index.
2746template <typename pchar, typename schar>
2747static int SimpleIndexOf(Vector<const schar> subject,
2748 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002749 int idx,
2750 bool* complete) {
2751 // Badness is a count of how much work we have done. When we have
2752 // done enough work we decide it's probably worth switching to a better
2753 // algorithm.
2754 int badness = -10 - (pattern.length() << 2);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002755
ager@chromium.org7c537e22008-10-16 08:43:32 +00002756 // We know our pattern is at least 2 characters, we cache the first so
2757 // the common case of the first character not matching is faster.
2758 pchar pattern_first_char = pattern[0];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002759 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2760 badness++;
2761 if (badness > 0) {
2762 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002763 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002764 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002765 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2766 const schar* pos = reinterpret_cast<const schar*>(
2767 memchr(subject.start() + i,
2768 pattern_first_char,
2769 n - i + 1));
2770 if (pos == NULL) {
2771 *complete = true;
2772 return -1;
2773 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002774 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002775 } else {
2776 if (subject[i] != pattern_first_char) continue;
2777 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002778 int j = 1;
2779 do {
2780 if (pattern[j] != subject[i+j]) {
2781 break;
2782 }
2783 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002784 } while (j < pattern.length());
2785 if (j == pattern.length()) {
2786 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002787 return i;
2788 }
2789 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002790 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002791 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002792 return -1;
2793}
2794
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002795// Simple indexOf that never bails out. For short patterns only.
2796template <typename pchar, typename schar>
2797static int SimpleIndexOf(Vector<const schar> subject,
2798 Vector<const pchar> pattern,
2799 int idx) {
2800 pchar pattern_first_char = pattern[0];
2801 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002802 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2803 const schar* pos = reinterpret_cast<const schar*>(
2804 memchr(subject.start() + i,
2805 pattern_first_char,
2806 n - i + 1));
2807 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002808 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002809 } else {
2810 if (subject[i] != pattern_first_char) continue;
2811 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002812 int j = 1;
2813 do {
2814 if (pattern[j] != subject[i+j]) {
2815 break;
2816 }
2817 j++;
2818 } while (j < pattern.length());
2819 if (j == pattern.length()) {
2820 return i;
2821 }
2822 }
2823 return -1;
2824}
2825
2826
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002827// Strategy for searching for a string in another string.
2828enum StringSearchStrategy { SEARCH_FAIL, SEARCH_SHORT, SEARCH_LONG };
ager@chromium.org7c537e22008-10-16 08:43:32 +00002829
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002830
2831template <typename pchar>
2832static inline StringSearchStrategy InitializeStringSearch(
2833 Vector<const pchar> pat, bool ascii_subject) {
2834 ASSERT(pat.length() > 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002835 // We have an ASCII haystack and a non-ASCII needle. Check if there
2836 // really is a non-ASCII character in the needle and bail out if there
2837 // is.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002838 if (ascii_subject && sizeof(pchar) > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002839 for (int i = 0; i < pat.length(); i++) {
2840 uc16 c = pat[i];
2841 if (c > String::kMaxAsciiCharCode) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002842 return SEARCH_FAIL;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002843 }
2844 }
2845 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002846 if (pat.length() < kBMMinPatternLength) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002847 return SEARCH_SHORT;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002848 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002849 algorithm = SIMPLE_SEARCH;
2850 return SEARCH_LONG;
2851}
2852
2853
2854// Dispatch long needle searches to different algorithms.
2855template <typename schar, typename pchar>
2856static int ComplexIndexOf(Vector<const schar> sub,
2857 Vector<const pchar> pat,
2858 int start_index) {
2859 ASSERT(pat.length() >= kBMMinPatternLength);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002860 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002861 bool complete;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002862 int idx = start_index;
2863 switch (algorithm) {
2864 case SIMPLE_SEARCH:
2865 idx = SimpleIndexOf(sub, pat, idx, &complete);
2866 if (complete) return idx;
2867 BoyerMoorePopulateBadCharTable(pat);
2868 algorithm = BOYER_MOORE_HORSPOOL;
2869 // FALLTHROUGH.
2870 case BOYER_MOORE_HORSPOOL:
2871 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
2872 if (complete) return idx;
2873 // Build the Good Suffix table and continue searching.
2874 BoyerMoorePopulateGoodSuffixTable(pat);
2875 algorithm = BOYER_MOORE;
2876 // FALLTHROUGH.
2877 case BOYER_MOORE:
2878 return BoyerMooreIndexOf(sub, pat, idx);
2879 }
2880 UNREACHABLE();
2881 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002882}
2883
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002884
2885// Dispatch to different search strategies for a single search.
2886// If searching multiple times on the same needle, the search
2887// strategy should only be computed once and then dispatch to different
2888// loops.
2889template <typename schar, typename pchar>
2890static int StringSearch(Vector<const schar> sub,
2891 Vector<const pchar> pat,
2892 int start_index) {
2893 bool ascii_subject = (sizeof(schar) == 1);
2894 StringSearchStrategy strategy = InitializeStringSearch(pat, ascii_subject);
2895 switch (strategy) {
2896 case SEARCH_FAIL: return -1;
2897 case SEARCH_SHORT: return SimpleIndexOf(sub, pat, start_index);
2898 case SEARCH_LONG: return ComplexIndexOf(sub, pat, start_index);
2899 }
2900 UNREACHABLE();
2901 return -1;
2902}
2903
2904
ager@chromium.org7c537e22008-10-16 08:43:32 +00002905// Perform string match of pattern on subject, starting at start index.
2906// Caller must ensure that 0 <= start_index <= sub->length(),
2907// and should check that pat->length() + start_index <= sub->length()
2908int Runtime::StringMatch(Handle<String> sub,
2909 Handle<String> pat,
2910 int start_index) {
2911 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002912 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002913
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002914 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002915 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002916
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002917 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002918 if (start_index + pattern_length > subject_length) return -1;
2919
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002920 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002921 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002922 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002923
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002924 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002925 // character patterns linear search is necessary, so any smart
2926 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002927 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002928 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002929 String* seq_sub = *sub;
2930 if (seq_sub->IsConsString()) {
2931 seq_sub = ConsString::cast(seq_sub)->first();
2932 }
2933 if (seq_sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002934 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002935 if (pchar > String::kMaxAsciiCharCode) {
2936 return -1;
2937 }
2938 Vector<const char> ascii_vector =
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002939 seq_sub->ToAsciiVector().SubVector(start_index, subject_length);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002940 const void* pos = memchr(ascii_vector.start(),
2941 static_cast<const char>(pchar),
2942 static_cast<size_t>(ascii_vector.length()));
2943 if (pos == NULL) {
2944 return -1;
2945 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002946 return static_cast<int>(reinterpret_cast<const char*>(pos)
2947 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002948 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002949 return SingleCharIndexOf(seq_sub->ToUC16Vector(),
2950 pat->Get(0),
2951 start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002952 }
2953
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002954 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002955 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002956 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002957
ager@chromium.org7c537e22008-10-16 08:43:32 +00002958 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002959 // Extract flattened substrings of cons strings before determining asciiness.
2960 String* seq_sub = *sub;
2961 if (seq_sub->IsConsString()) {
2962 seq_sub = ConsString::cast(seq_sub)->first();
2963 }
2964 String* seq_pat = *pat;
2965 if (seq_pat->IsConsString()) {
2966 seq_pat = ConsString::cast(seq_pat)->first();
2967 }
2968
ager@chromium.org7c537e22008-10-16 08:43:32 +00002969 // dispatch on type of strings
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002970 if (seq_pat->IsAsciiRepresentation()) {
2971 Vector<const char> pat_vector = seq_pat->ToAsciiVector();
2972 if (seq_sub->IsAsciiRepresentation()) {
2973 return StringSearch(seq_sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002974 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002975 return StringSearch(seq_sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002976 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002977 Vector<const uc16> pat_vector = seq_pat->ToUC16Vector();
2978 if (seq_sub->IsAsciiRepresentation()) {
2979 return StringSearch(seq_sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002980 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002981 return StringSearch(seq_sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002982}
2983
2984
2985static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002986 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002987 ASSERT(args.length() == 3);
2988
ager@chromium.org7c537e22008-10-16 08:43:32 +00002989 CONVERT_ARG_CHECKED(String, sub, 0);
2990 CONVERT_ARG_CHECKED(String, pat, 1);
2991
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002992 Object* index = args[2];
2993 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002994 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002995
ager@chromium.org870a0b62008-11-04 11:43:05 +00002996 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002997 int position = Runtime::StringMatch(sub, pat, start_index);
2998 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002999}
3000
3001
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003002template <typename schar, typename pchar>
3003static int StringMatchBackwards(Vector<const schar> sub,
3004 Vector<const pchar> pat,
3005 int idx) {
3006 ASSERT(pat.length() >= 1);
3007 ASSERT(idx + pat.length() <= sub.length());
3008
3009 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
3010 for (int i = 0; i < pat.length(); i++) {
3011 uc16 c = pat[i];
3012 if (c > String::kMaxAsciiCharCode) {
3013 return -1;
3014 }
3015 }
3016 }
3017
3018 pchar pattern_first_char = pat[0];
3019 for (int i = idx; i >= 0; i--) {
3020 if (sub[i] != pattern_first_char) continue;
3021 int j = 1;
3022 while (j < pat.length()) {
3023 if (pat[j] != sub[i+j]) {
3024 break;
3025 }
3026 j++;
3027 }
3028 if (j == pat.length()) {
3029 return i;
3030 }
3031 }
3032 return -1;
3033}
3034
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003035static Object* Runtime_StringLastIndexOf(Arguments args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003036 HandleScope scope; // create a new handle scope
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003037 ASSERT(args.length() == 3);
3038
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003039 CONVERT_ARG_CHECKED(String, sub, 0);
3040 CONVERT_ARG_CHECKED(String, pat, 1);
3041
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003042 Object* index = args[2];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003043 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003044 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003045
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003046 uint32_t pat_length = pat->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003047 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003048
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003049 if (start_index + pat_length > sub_length) {
3050 start_index = sub_length - pat_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00003051 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003052
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003053 if (pat_length == 0) {
3054 return Smi::FromInt(start_index);
3055 }
3056
3057 if (!sub->IsFlat()) {
3058 FlattenString(sub);
3059 }
3060
3061 if (pat_length == 1) {
3062 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
3063 if (sub->IsAsciiRepresentation()) {
3064 uc16 pchar = pat->Get(0);
3065 if (pchar > String::kMaxAsciiCharCode) {
3066 return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003067 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003068 return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
3069 static_cast<char>(pat->Get(0)),
3070 start_index));
3071 } else {
3072 return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
3073 pat->Get(0),
3074 start_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003075 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003076 }
3077
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003078 if (!pat->IsFlat()) {
3079 FlattenString(pat);
3080 }
3081
3082 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
3083
3084 int position = -1;
3085
3086 if (pat->IsAsciiRepresentation()) {
3087 Vector<const char> pat_vector = pat->ToAsciiVector();
3088 if (sub->IsAsciiRepresentation()) {
3089 position = StringMatchBackwards(sub->ToAsciiVector(),
3090 pat_vector,
3091 start_index);
3092 } else {
3093 position = StringMatchBackwards(sub->ToUC16Vector(),
3094 pat_vector,
3095 start_index);
3096 }
3097 } else {
3098 Vector<const uc16> pat_vector = pat->ToUC16Vector();
3099 if (sub->IsAsciiRepresentation()) {
3100 position = StringMatchBackwards(sub->ToAsciiVector(),
3101 pat_vector,
3102 start_index);
3103 } else {
3104 position = StringMatchBackwards(sub->ToUC16Vector(),
3105 pat_vector,
3106 start_index);
3107 }
3108 }
3109
3110 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003111}
3112
3113
3114static Object* Runtime_StringLocaleCompare(Arguments args) {
3115 NoHandleAllocation ha;
3116 ASSERT(args.length() == 2);
3117
3118 CONVERT_CHECKED(String, str1, args[0]);
3119 CONVERT_CHECKED(String, str2, args[1]);
3120
3121 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003122 int str1_length = str1->length();
3123 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003124
3125 // Decide trivial cases without flattening.
3126 if (str1_length == 0) {
3127 if (str2_length == 0) return Smi::FromInt(0); // Equal.
3128 return Smi::FromInt(-str2_length);
3129 } else {
3130 if (str2_length == 0) return Smi::FromInt(str1_length);
3131 }
3132
3133 int end = str1_length < str2_length ? str1_length : str2_length;
3134
3135 // No need to flatten if we are going to find the answer on the first
3136 // character. At this point we know there is at least one character
3137 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003138 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003139 if (d != 0) return Smi::FromInt(d);
3140
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003141 str1->TryFlatten();
3142 str2->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003143
3144 static StringInputBuffer buf1;
3145 static StringInputBuffer buf2;
3146
3147 buf1.Reset(str1);
3148 buf2.Reset(str2);
3149
3150 for (int i = 0; i < end; i++) {
3151 uint16_t char1 = buf1.GetNext();
3152 uint16_t char2 = buf2.GetNext();
3153 if (char1 != char2) return Smi::FromInt(char1 - char2);
3154 }
3155
3156 return Smi::FromInt(str1_length - str2_length);
3157}
3158
3159
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003160static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003161 NoHandleAllocation ha;
3162 ASSERT(args.length() == 3);
3163
3164 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00003165 Object* from = args[1];
3166 Object* to = args[2];
3167 int start, end;
3168 // We have a fast integer-only case here to avoid a conversion to double in
3169 // the common case where from and to are Smis.
3170 if (from->IsSmi() && to->IsSmi()) {
3171 start = Smi::cast(from)->value();
3172 end = Smi::cast(to)->value();
3173 } else {
3174 CONVERT_DOUBLE_CHECKED(from_number, from);
3175 CONVERT_DOUBLE_CHECKED(to_number, to);
3176 start = FastD2I(from_number);
3177 end = FastD2I(to_number);
3178 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003179 RUNTIME_ASSERT(end >= start);
3180 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003181 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003182 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003183 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003184}
3185
3186
ager@chromium.org41826e72009-03-30 13:30:57 +00003187static Object* Runtime_StringMatch(Arguments args) {
3188 ASSERT_EQ(3, args.length());
3189
3190 CONVERT_ARG_CHECKED(String, subject, 0);
3191 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
3192 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
3193 HandleScope handles;
3194
3195 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
3196
3197 if (match.is_null()) {
3198 return Failure::Exception();
3199 }
3200 if (match->IsNull()) {
3201 return Heap::null_value();
3202 }
3203 int length = subject->length();
3204
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00003205 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00003206 ZoneList<int> offsets(8);
3207 do {
3208 int start;
3209 int end;
3210 {
3211 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003212 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00003213 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
3214 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
3215 }
3216 offsets.Add(start);
3217 offsets.Add(end);
3218 int index = start < end ? end : end + 1;
3219 if (index > length) break;
3220 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
3221 if (match.is_null()) {
3222 return Failure::Exception();
3223 }
3224 } while (!match->IsNull());
3225 int matches = offsets.length() / 2;
3226 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
3227 for (int i = 0; i < matches ; i++) {
3228 int from = offsets.at(i * 2);
3229 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003230 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00003231 }
3232 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
3233 result->set_length(Smi::FromInt(matches));
3234 return *result;
3235}
3236
3237
lrn@chromium.org25156de2010-04-06 13:10:27 +00003238// Two smis before and after the match, for very long strings.
3239const int kMaxBuilderEntriesPerRegExpMatch = 5;
3240
3241
3242static void SetLastMatchInfoNoCaptures(Handle<String> subject,
3243 Handle<JSArray> last_match_info,
3244 int match_start,
3245 int match_end) {
3246 // Fill last_match_info with a single capture.
3247 last_match_info->EnsureSize(2 + RegExpImpl::kLastMatchOverhead);
3248 AssertNoAllocation no_gc;
3249 FixedArray* elements = FixedArray::cast(last_match_info->elements());
3250 RegExpImpl::SetLastCaptureCount(elements, 2);
3251 RegExpImpl::SetLastInput(elements, *subject);
3252 RegExpImpl::SetLastSubject(elements, *subject);
3253 RegExpImpl::SetCapture(elements, 0, match_start);
3254 RegExpImpl::SetCapture(elements, 1, match_end);
3255}
3256
3257
3258template <typename schar>
3259static bool SearchCharMultiple(Vector<schar> subject,
3260 String* pattern,
3261 schar pattern_char,
3262 FixedArrayBuilder* builder,
3263 int* match_pos) {
3264 // Position of last match.
3265 int pos = *match_pos;
3266 int subject_length = subject.length();
3267 while (pos < subject_length) {
3268 int match_end = pos + 1;
3269 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3270 *match_pos = pos;
3271 return false;
3272 }
3273 int new_pos = SingleCharIndexOf(subject, pattern_char, match_end);
3274 if (new_pos >= 0) {
3275 // Match has been found.
3276 if (new_pos > match_end) {
3277 ReplacementStringBuilder::AddSubjectSlice(builder, match_end, new_pos);
3278 }
3279 pos = new_pos;
3280 builder->Add(pattern);
3281 } else {
3282 break;
3283 }
3284 }
3285 if (pos + 1 < subject_length) {
3286 ReplacementStringBuilder::AddSubjectSlice(builder, pos + 1, subject_length);
3287 }
3288 *match_pos = pos;
3289 return true;
3290}
3291
3292
3293static bool SearchCharMultiple(Handle<String> subject,
3294 Handle<String> pattern,
3295 Handle<JSArray> last_match_info,
3296 FixedArrayBuilder* builder) {
3297 ASSERT(subject->IsFlat());
3298 ASSERT_EQ(1, pattern->length());
3299 uc16 pattern_char = pattern->Get(0);
3300 // Treating position before first as initial "previous match position".
3301 int match_pos = -1;
3302
3303 for (;;) { // Break when search complete.
3304 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3305 AssertNoAllocation no_gc;
3306 if (subject->IsAsciiRepresentation()) {
3307 if (pattern_char > String::kMaxAsciiCharCode) {
3308 break;
3309 }
3310 Vector<const char> subject_vector = subject->ToAsciiVector();
3311 char pattern_ascii_char = static_cast<char>(pattern_char);
3312 bool complete = SearchCharMultiple<const char>(subject_vector,
3313 *pattern,
3314 pattern_ascii_char,
3315 builder,
3316 &match_pos);
3317 if (complete) break;
3318 } else {
3319 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3320 bool complete = SearchCharMultiple<const uc16>(subject_vector,
3321 *pattern,
3322 pattern_char,
3323 builder,
3324 &match_pos);
3325 if (complete) break;
3326 }
3327 }
3328
3329 if (match_pos >= 0) {
3330 SetLastMatchInfoNoCaptures(subject,
3331 last_match_info,
3332 match_pos,
3333 match_pos + 1);
3334 return true;
3335 }
3336 return false; // No matches at all.
3337}
3338
3339
3340template <typename schar, typename pchar>
3341static bool SearchStringMultiple(Vector<schar> subject,
3342 String* pattern,
3343 Vector<pchar> pattern_string,
3344 FixedArrayBuilder* builder,
3345 int* match_pos) {
3346 int pos = *match_pos;
3347 int subject_length = subject.length();
3348 int pattern_length = pattern_string.length();
3349 int max_search_start = subject_length - pattern_length;
3350 bool is_ascii = (sizeof(schar) == 1);
3351 StringSearchStrategy strategy =
3352 InitializeStringSearch(pattern_string, is_ascii);
3353 switch (strategy) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003354 case SEARCH_FAIL: break;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003355 case SEARCH_SHORT:
3356 while (pos <= max_search_start) {
3357 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3358 *match_pos = pos;
3359 return false;
3360 }
3361 // Position of end of previous match.
3362 int match_end = pos + pattern_length;
3363 int new_pos = SimpleIndexOf(subject, pattern_string, match_end);
3364 if (new_pos >= 0) {
3365 // A match.
3366 if (new_pos > match_end) {
3367 ReplacementStringBuilder::AddSubjectSlice(builder,
3368 match_end,
3369 new_pos);
3370 }
3371 pos = new_pos;
3372 builder->Add(pattern);
3373 } else {
3374 break;
3375 }
3376 }
3377 break;
3378 case SEARCH_LONG:
3379 while (pos <= max_search_start) {
3380 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003381 *match_pos = pos;
3382 return false;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003383 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003384 int match_end = pos + pattern_length;
3385 int new_pos = ComplexIndexOf(subject, pattern_string, match_end);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003386 if (new_pos >= 0) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003387 // A match has been found.
3388 if (new_pos > match_end) {
3389 ReplacementStringBuilder::AddSubjectSlice(builder,
3390 match_end,
3391 new_pos);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003392 }
3393 pos = new_pos;
3394 builder->Add(pattern);
3395 } else {
3396 break;
3397 }
3398 }
3399 break;
3400 }
3401 if (pos < max_search_start) {
3402 ReplacementStringBuilder::AddSubjectSlice(builder,
3403 pos + pattern_length,
3404 subject_length);
3405 }
3406 *match_pos = pos;
3407 return true;
3408}
3409
3410
3411static bool SearchStringMultiple(Handle<String> subject,
3412 Handle<String> pattern,
3413 Handle<JSArray> last_match_info,
3414 FixedArrayBuilder* builder) {
3415 ASSERT(subject->IsFlat());
3416 ASSERT(pattern->IsFlat());
3417 ASSERT(pattern->length() > 1);
3418
3419 // Treating as if a previous match was before first character.
3420 int match_pos = -pattern->length();
3421
3422 for (;;) { // Break when search complete.
3423 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3424 AssertNoAllocation no_gc;
3425 if (subject->IsAsciiRepresentation()) {
3426 Vector<const char> subject_vector = subject->ToAsciiVector();
3427 if (pattern->IsAsciiRepresentation()) {
3428 if (SearchStringMultiple(subject_vector,
3429 *pattern,
3430 pattern->ToAsciiVector(),
3431 builder,
3432 &match_pos)) break;
3433 } else {
3434 if (SearchStringMultiple(subject_vector,
3435 *pattern,
3436 pattern->ToUC16Vector(),
3437 builder,
3438 &match_pos)) break;
3439 }
3440 } else {
3441 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3442 if (pattern->IsAsciiRepresentation()) {
3443 if (SearchStringMultiple(subject_vector,
3444 *pattern,
3445 pattern->ToAsciiVector(),
3446 builder,
3447 &match_pos)) break;
3448 } else {
3449 if (SearchStringMultiple(subject_vector,
3450 *pattern,
3451 pattern->ToUC16Vector(),
3452 builder,
3453 &match_pos)) break;
3454 }
3455 }
3456 }
3457
3458 if (match_pos >= 0) {
3459 SetLastMatchInfoNoCaptures(subject,
3460 last_match_info,
3461 match_pos,
3462 match_pos + pattern->length());
3463 return true;
3464 }
3465 return false; // No matches at all.
3466}
3467
3468
3469static RegExpImpl::IrregexpResult SearchRegExpNoCaptureMultiple(
3470 Handle<String> subject,
3471 Handle<JSRegExp> regexp,
3472 Handle<JSArray> last_match_array,
3473 FixedArrayBuilder* builder) {
3474 ASSERT(subject->IsFlat());
3475 int match_start = -1;
3476 int match_end = 0;
3477 int pos = 0;
3478 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3479 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3480
3481 OffsetsVector registers(required_registers);
3482 Vector<int> register_vector(registers.vector(), registers.length());
3483 int subject_length = subject->length();
3484
3485 for (;;) { // Break on failure, return on exception.
3486 RegExpImpl::IrregexpResult result =
3487 RegExpImpl::IrregexpExecOnce(regexp,
3488 subject,
3489 pos,
3490 register_vector);
3491 if (result == RegExpImpl::RE_SUCCESS) {
3492 match_start = register_vector[0];
3493 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3494 if (match_end < match_start) {
3495 ReplacementStringBuilder::AddSubjectSlice(builder,
3496 match_end,
3497 match_start);
3498 }
3499 match_end = register_vector[1];
3500 HandleScope loop_scope;
3501 builder->Add(*Factory::NewSubString(subject, match_start, match_end));
3502 if (match_start != match_end) {
3503 pos = match_end;
3504 } else {
3505 pos = match_end + 1;
3506 if (pos > subject_length) break;
3507 }
3508 } else if (result == RegExpImpl::RE_FAILURE) {
3509 break;
3510 } else {
3511 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3512 return result;
3513 }
3514 }
3515
3516 if (match_start >= 0) {
3517 if (match_end < subject_length) {
3518 ReplacementStringBuilder::AddSubjectSlice(builder,
3519 match_end,
3520 subject_length);
3521 }
3522 SetLastMatchInfoNoCaptures(subject,
3523 last_match_array,
3524 match_start,
3525 match_end);
3526 return RegExpImpl::RE_SUCCESS;
3527 } else {
3528 return RegExpImpl::RE_FAILURE; // No matches at all.
3529 }
3530}
3531
3532
3533static RegExpImpl::IrregexpResult SearchRegExpMultiple(
3534 Handle<String> subject,
3535 Handle<JSRegExp> regexp,
3536 Handle<JSArray> last_match_array,
3537 FixedArrayBuilder* builder) {
3538
3539 ASSERT(subject->IsFlat());
3540 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3541 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3542
3543 OffsetsVector registers(required_registers);
3544 Vector<int> register_vector(registers.vector(), registers.length());
3545
3546 RegExpImpl::IrregexpResult result =
3547 RegExpImpl::IrregexpExecOnce(regexp,
3548 subject,
3549 0,
3550 register_vector);
3551
3552 int capture_count = regexp->CaptureCount();
3553 int subject_length = subject->length();
3554
3555 // Position to search from.
3556 int pos = 0;
3557 // End of previous match. Differs from pos if match was empty.
3558 int match_end = 0;
3559 if (result == RegExpImpl::RE_SUCCESS) {
3560 // Need to keep a copy of the previous match for creating last_match_info
3561 // at the end, so we have two vectors that we swap between.
3562 OffsetsVector registers2(required_registers);
3563 Vector<int> prev_register_vector(registers2.vector(), registers2.length());
3564
3565 do {
3566 int match_start = register_vector[0];
3567 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3568 if (match_end < match_start) {
3569 ReplacementStringBuilder::AddSubjectSlice(builder,
3570 match_end,
3571 match_start);
3572 }
3573 match_end = register_vector[1];
3574
3575 {
3576 // Avoid accumulating new handles inside loop.
3577 HandleScope temp_scope;
3578 // Arguments array to replace function is match, captures, index and
3579 // subject, i.e., 3 + capture count in total.
3580 Handle<FixedArray> elements = Factory::NewFixedArray(3 + capture_count);
3581 elements->set(0, *Factory::NewSubString(subject,
3582 match_start,
3583 match_end));
3584 for (int i = 1; i <= capture_count; i++) {
3585 int start = register_vector[i * 2];
3586 if (start >= 0) {
3587 int end = register_vector[i * 2 + 1];
3588 ASSERT(start <= end);
3589 Handle<String> substring = Factory::NewSubString(subject,
3590 start,
3591 end);
3592 elements->set(i, *substring);
3593 } else {
3594 ASSERT(register_vector[i * 2 + 1] < 0);
3595 elements->set(i, Heap::undefined_value());
3596 }
3597 }
3598 elements->set(capture_count + 1, Smi::FromInt(match_start));
3599 elements->set(capture_count + 2, *subject);
3600 builder->Add(*Factory::NewJSArrayWithElements(elements));
3601 }
3602 // Swap register vectors, so the last successful match is in
3603 // prev_register_vector.
3604 Vector<int> tmp = prev_register_vector;
3605 prev_register_vector = register_vector;
3606 register_vector = tmp;
3607
3608 if (match_end > match_start) {
3609 pos = match_end;
3610 } else {
3611 pos = match_end + 1;
3612 if (pos > subject_length) {
3613 break;
3614 }
3615 }
3616
3617 result = RegExpImpl::IrregexpExecOnce(regexp,
3618 subject,
3619 pos,
3620 register_vector);
3621 } while (result == RegExpImpl::RE_SUCCESS);
3622
3623 if (result != RegExpImpl::RE_EXCEPTION) {
3624 // Finished matching, with at least one match.
3625 if (match_end < subject_length) {
3626 ReplacementStringBuilder::AddSubjectSlice(builder,
3627 match_end,
3628 subject_length);
3629 }
3630
3631 int last_match_capture_count = (capture_count + 1) * 2;
3632 int last_match_array_size =
3633 last_match_capture_count + RegExpImpl::kLastMatchOverhead;
3634 last_match_array->EnsureSize(last_match_array_size);
3635 AssertNoAllocation no_gc;
3636 FixedArray* elements = FixedArray::cast(last_match_array->elements());
3637 RegExpImpl::SetLastCaptureCount(elements, last_match_capture_count);
3638 RegExpImpl::SetLastSubject(elements, *subject);
3639 RegExpImpl::SetLastInput(elements, *subject);
3640 for (int i = 0; i < last_match_capture_count; i++) {
3641 RegExpImpl::SetCapture(elements, i, prev_register_vector[i]);
3642 }
3643 return RegExpImpl::RE_SUCCESS;
3644 }
3645 }
3646 // No matches at all, return failure or exception result directly.
3647 return result;
3648}
3649
3650
3651static Object* Runtime_RegExpExecMultiple(Arguments args) {
3652 ASSERT(args.length() == 4);
3653 HandleScope handles;
3654
3655 CONVERT_ARG_CHECKED(String, subject, 1);
3656 if (!subject->IsFlat()) { FlattenString(subject); }
3657 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
3658 CONVERT_ARG_CHECKED(JSArray, last_match_info, 2);
3659 CONVERT_ARG_CHECKED(JSArray, result_array, 3);
3660
3661 ASSERT(last_match_info->HasFastElements());
3662 ASSERT(regexp->GetFlags().is_global());
3663 Handle<FixedArray> result_elements;
3664 if (result_array->HasFastElements()) {
3665 result_elements =
3666 Handle<FixedArray>(FixedArray::cast(result_array->elements()));
3667 } else {
3668 result_elements = Factory::NewFixedArrayWithHoles(16);
3669 }
3670 FixedArrayBuilder builder(result_elements);
3671
3672 if (regexp->TypeTag() == JSRegExp::ATOM) {
3673 Handle<String> pattern(
3674 String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex)));
3675 int pattern_length = pattern->length();
3676 if (pattern_length == 1) {
3677 if (SearchCharMultiple(subject, pattern, last_match_info, &builder)) {
3678 return *builder.ToJSArray(result_array);
3679 }
3680 return Heap::null_value();
3681 }
3682
3683 if (!pattern->IsFlat()) FlattenString(pattern);
3684 if (SearchStringMultiple(subject, pattern, last_match_info, &builder)) {
3685 return *builder.ToJSArray(result_array);
3686 }
3687 return Heap::null_value();
3688 }
3689
3690 ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
3691
3692 RegExpImpl::IrregexpResult result;
3693 if (regexp->CaptureCount() == 0) {
3694 result = SearchRegExpNoCaptureMultiple(subject,
3695 regexp,
3696 last_match_info,
3697 &builder);
3698 } else {
3699 result = SearchRegExpMultiple(subject, regexp, last_match_info, &builder);
3700 }
3701 if (result == RegExpImpl::RE_SUCCESS) return *builder.ToJSArray(result_array);
3702 if (result == RegExpImpl::RE_FAILURE) return Heap::null_value();
3703 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3704 return Failure::Exception();
3705}
3706
3707
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003708static Object* Runtime_NumberToRadixString(Arguments args) {
3709 NoHandleAllocation ha;
3710 ASSERT(args.length() == 2);
3711
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003712 // Fast case where the result is a one character string.
3713 if (args[0]->IsSmi() && args[1]->IsSmi()) {
3714 int value = Smi::cast(args[0])->value();
3715 int radix = Smi::cast(args[1])->value();
3716 if (value >= 0 && value < radix) {
3717 RUNTIME_ASSERT(radix <= 36);
3718 // Character array used for conversion.
3719 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
3720 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
3721 }
3722 }
3723
3724 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003725 CONVERT_DOUBLE_CHECKED(value, args[0]);
3726 if (isnan(value)) {
3727 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3728 }
3729 if (isinf(value)) {
3730 if (value < 0) {
3731 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3732 }
3733 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3734 }
3735 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
3736 int radix = FastD2I(radix_number);
3737 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3738 char* str = DoubleToRadixCString(value, radix);
3739 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3740 DeleteArray(str);
3741 return result;
3742}
3743
3744
3745static Object* Runtime_NumberToFixed(Arguments args) {
3746 NoHandleAllocation ha;
3747 ASSERT(args.length() == 2);
3748
3749 CONVERT_DOUBLE_CHECKED(value, args[0]);
3750 if (isnan(value)) {
3751 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3752 }
3753 if (isinf(value)) {
3754 if (value < 0) {
3755 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3756 }
3757 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3758 }
3759 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3760 int f = FastD2I(f_number);
3761 RUNTIME_ASSERT(f >= 0);
3762 char* str = DoubleToFixedCString(value, f);
3763 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3764 DeleteArray(str);
3765 return res;
3766}
3767
3768
3769static Object* Runtime_NumberToExponential(Arguments args) {
3770 NoHandleAllocation ha;
3771 ASSERT(args.length() == 2);
3772
3773 CONVERT_DOUBLE_CHECKED(value, args[0]);
3774 if (isnan(value)) {
3775 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3776 }
3777 if (isinf(value)) {
3778 if (value < 0) {
3779 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3780 }
3781 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3782 }
3783 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3784 int f = FastD2I(f_number);
3785 RUNTIME_ASSERT(f >= -1 && f <= 20);
3786 char* str = DoubleToExponentialCString(value, f);
3787 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3788 DeleteArray(str);
3789 return res;
3790}
3791
3792
3793static Object* Runtime_NumberToPrecision(Arguments args) {
3794 NoHandleAllocation ha;
3795 ASSERT(args.length() == 2);
3796
3797 CONVERT_DOUBLE_CHECKED(value, args[0]);
3798 if (isnan(value)) {
3799 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3800 }
3801 if (isinf(value)) {
3802 if (value < 0) {
3803 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3804 }
3805 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3806 }
3807 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3808 int f = FastD2I(f_number);
3809 RUNTIME_ASSERT(f >= 1 && f <= 21);
3810 char* str = DoubleToPrecisionCString(value, f);
3811 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3812 DeleteArray(str);
3813 return res;
3814}
3815
3816
3817// Returns a single character string where first character equals
3818// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003819static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003820 if (index < static_cast<uint32_t>(string->length())) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003821 string->TryFlatten();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003822 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003823 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003824 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003825 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003826}
3827
3828
3829Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
3830 // Handle [] indexing on Strings
3831 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003832 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
3833 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003834 }
3835
3836 // Handle [] indexing on String objects
3837 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003838 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
3839 Handle<Object> result =
3840 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
3841 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003842 }
3843
3844 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003845 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003846 return prototype->GetElement(index);
3847 }
3848
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003849 return GetElement(object, index);
3850}
3851
3852
3853Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003854 return object->GetElement(index);
3855}
3856
3857
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003858Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
3859 HandleScope scope;
3860
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003861 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003862 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003863 Handle<Object> error =
3864 Factory::NewTypeError("non_object_property_load",
3865 HandleVector(args, 2));
3866 return Top::Throw(*error);
3867 }
3868
3869 // Check if the given key is an array index.
3870 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003871 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003872 return GetElementOrCharAt(object, index);
3873 }
3874
3875 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003876 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003877 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003878 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003879 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003880 bool has_pending_exception = false;
3881 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003882 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003883 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003884 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003885 }
3886
ager@chromium.org32912102009-01-16 10:38:43 +00003887 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003888 // the element if so.
3889 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003890 return GetElementOrCharAt(object, index);
3891 } else {
3892 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003893 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003894 }
3895}
3896
3897
3898static Object* Runtime_GetProperty(Arguments args) {
3899 NoHandleAllocation ha;
3900 ASSERT(args.length() == 2);
3901
3902 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003903 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003904
3905 return Runtime::GetObjectProperty(object, key);
3906}
3907
3908
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003909// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003910static Object* Runtime_KeyedGetProperty(Arguments args) {
3911 NoHandleAllocation ha;
3912 ASSERT(args.length() == 2);
3913
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003914 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00003915 // itself.
3916 //
3917 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00003918 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00003919 // global proxy object never has properties. This is the case
3920 // because the global proxy object forwards everything to its hidden
3921 // prototype including local lookups.
3922 //
3923 // Additionally, we need to make sure that we do not cache results
3924 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003925 if (args[0]->IsJSObject() &&
3926 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00003927 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003928 args[1]->IsString()) {
3929 JSObject* receiver = JSObject::cast(args[0]);
3930 String* key = String::cast(args[1]);
3931 if (receiver->HasFastProperties()) {
3932 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003933 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003934 int offset = KeyedLookupCache::Lookup(receiver_map, key);
3935 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003936 Object* value = receiver->FastPropertyAt(offset);
3937 return value->IsTheHole() ? Heap::undefined_value() : value;
3938 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003939 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003940 LookupResult result;
3941 receiver->LocalLookup(key, &result);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00003942 if (result.IsProperty() && result.type() == FIELD) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003943 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003944 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003945 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003946 }
3947 } else {
3948 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003949 StringDictionary* dictionary = receiver->property_dictionary();
3950 int entry = dictionary->FindEntry(key);
3951 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003952 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003953 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003954 if (!receiver->IsGlobalObject()) return value;
3955 value = JSGlobalPropertyCell::cast(value)->value();
3956 if (!value->IsTheHole()) return value;
3957 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003958 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003959 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003960 } else if (args[0]->IsString() && args[1]->IsSmi()) {
3961 // Fast case for string indexing using [] with a smi index.
3962 HandleScope scope;
3963 Handle<String> str = args.at<String>(0);
3964 int index = Smi::cast(args[1])->value();
3965 Handle<Object> result = GetCharAt(str, index);
3966 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003967 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003968
3969 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003970 return Runtime::GetObjectProperty(args.at<Object>(0),
3971 args.at<Object>(1));
3972}
3973
3974
ager@chromium.org5c838252010-02-19 08:53:10 +00003975static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
3976 ASSERT(args.length() == 5);
3977 HandleScope scope;
3978 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3979 CONVERT_CHECKED(String, name, args[1]);
3980 CONVERT_CHECKED(Smi, flag_setter, args[2]);
3981 CONVERT_CHECKED(JSFunction, fun, args[3]);
3982 CONVERT_CHECKED(Smi, flag_attr, args[4]);
3983 int unchecked = flag_attr->value();
3984 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3985 RUNTIME_ASSERT(!obj->IsNull());
3986 LookupResult result;
3987 obj->LocalLookupRealNamedProperty(name, &result);
3988
3989 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3990 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
3991 // delete it to avoid running into trouble in DefineAccessor, which
3992 // handles this incorrectly if the property is readonly (does nothing)
3993 if (result.IsProperty() &&
3994 (result.type() == FIELD || result.type() == NORMAL
3995 || result.type() == CONSTANT_FUNCTION)) {
3996 obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
3997 }
3998 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
3999}
4000
4001static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
4002 ASSERT(args.length() == 4);
4003 HandleScope scope;
4004 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
4005 CONVERT_ARG_CHECKED(String, name, 1);
4006 Handle<Object> obj_value = args.at<Object>(2);
4007
4008 CONVERT_CHECKED(Smi, flag, args[3]);
4009 int unchecked = flag->value();
4010 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4011
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004012 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
4013
4014 // Check if this is an element.
4015 uint32_t index;
4016 bool is_element = name->AsArrayIndex(&index);
4017
4018 // Special case for elements if any of the flags are true.
4019 // If elements are in fast case we always implicitly assume that:
4020 // DONT_DELETE: false, DONT_ENUM: false, READ_ONLY: false.
4021 if (((unchecked & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) &&
4022 is_element) {
4023 // Normalize the elements to enable attributes on the property.
4024 js_object->NormalizeElements();
4025 NumberDictionary* dictionary = js_object->element_dictionary();
4026 // Make sure that we never go back to fast case.
4027 dictionary->set_requires_slow_elements();
4028 PropertyDetails details = PropertyDetails(attr, NORMAL);
4029 dictionary->Set(index, *obj_value, details);
4030 }
4031
ager@chromium.org5c838252010-02-19 08:53:10 +00004032 LookupResult result;
4033 js_object->LocalLookupRealNamedProperty(*name, &result);
4034
ager@chromium.org5c838252010-02-19 08:53:10 +00004035 // Take special care when attributes are different and there is already
4036 // a property. For simplicity we normalize the property which enables us
4037 // to not worry about changing the instance_descriptor and creating a new
4038 // map. The current version of SetObjectProperty does not handle attributes
4039 // correctly in the case where a property is a field and is reset with
4040 // new attributes.
4041 if (result.IsProperty() && attr != result.GetAttributes()) {
4042 // New attributes - normalize to avoid writing to instance descriptor
4043 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
4044 // Use IgnoreAttributes version since a readonly property may be
4045 // overridden and SetProperty does not allow this.
4046 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
4047 *obj_value,
4048 attr);
4049 }
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004050
ager@chromium.org5c838252010-02-19 08:53:10 +00004051 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
4052}
4053
4054
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004055Object* Runtime::SetObjectProperty(Handle<Object> object,
4056 Handle<Object> key,
4057 Handle<Object> value,
4058 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004059 HandleScope scope;
4060
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004061 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004062 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004063 Handle<Object> error =
4064 Factory::NewTypeError("non_object_property_store",
4065 HandleVector(args, 2));
4066 return Top::Throw(*error);
4067 }
4068
4069 // If the object isn't a JavaScript object, we ignore the store.
4070 if (!object->IsJSObject()) return *value;
4071
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004072 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
4073
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004074 // Check if the given key is an array index.
4075 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004076 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004077 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
4078 // of a string using [] notation. We need to support this too in
4079 // JavaScript.
4080 // In the case of a String object we just need to redirect the assignment to
4081 // the underlying string if the index is in range. Since the underlying
4082 // string does nothing with the assignment then we can ignore such
4083 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004084 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004085 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004086 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004087
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004088 Handle<Object> result = SetElement(js_object, index, value);
4089 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004090 return *value;
4091 }
4092
4093 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004094 Handle<Object> result;
4095 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004096 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004097 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004098 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004099 key_string->TryFlatten();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004100 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004101 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004102 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004103 return *value;
4104 }
4105
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004106 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004107 bool has_pending_exception = false;
4108 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4109 if (has_pending_exception) return Failure::Exception();
4110 Handle<String> name = Handle<String>::cast(converted);
4111
4112 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004113 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004114 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004115 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004116 }
4117}
4118
4119
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004120Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
4121 Handle<Object> key,
4122 Handle<Object> value,
4123 PropertyAttributes attr) {
4124 HandleScope scope;
4125
4126 // Check if the given key is an array index.
4127 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004128 if (key->ToArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004129 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
4130 // of a string using [] notation. We need to support this too in
4131 // JavaScript.
4132 // In the case of a String object we just need to redirect the assignment to
4133 // the underlying string if the index is in range. Since the underlying
4134 // string does nothing with the assignment then we can ignore such
4135 // assignments.
4136 if (js_object->IsStringObjectWithCharacterAt(index)) {
4137 return *value;
4138 }
4139
4140 return js_object->SetElement(index, *value);
4141 }
4142
4143 if (key->IsString()) {
4144 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004145 return js_object->SetElement(index, *value);
4146 } else {
4147 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004148 key_string->TryFlatten();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004149 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
4150 *value,
4151 attr);
4152 }
4153 }
4154
4155 // Call-back into JavaScript to convert the key to a string.
4156 bool has_pending_exception = false;
4157 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4158 if (has_pending_exception) return Failure::Exception();
4159 Handle<String> name = Handle<String>::cast(converted);
4160
4161 if (name->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004162 return js_object->SetElement(index, *value);
4163 } else {
4164 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
4165 }
4166}
4167
4168
ager@chromium.orge2902be2009-06-08 12:21:35 +00004169Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
4170 Handle<Object> key) {
4171 HandleScope scope;
4172
4173 // Check if the given key is an array index.
4174 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004175 if (key->ToArrayIndex(&index)) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00004176 // In Firefox/SpiderMonkey, Safari and Opera you can access the
4177 // characters of a string using [] notation. In the case of a
4178 // String object we just need to redirect the deletion to the
4179 // underlying string if the index is in range. Since the
4180 // underlying string does nothing with the deletion, we can ignore
4181 // such deletions.
4182 if (js_object->IsStringObjectWithCharacterAt(index)) {
4183 return Heap::true_value();
4184 }
4185
4186 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
4187 }
4188
4189 Handle<String> key_string;
4190 if (key->IsString()) {
4191 key_string = Handle<String>::cast(key);
4192 } else {
4193 // Call-back into JavaScript to convert the key to a string.
4194 bool has_pending_exception = false;
4195 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4196 if (has_pending_exception) return Failure::Exception();
4197 key_string = Handle<String>::cast(converted);
4198 }
4199
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004200 key_string->TryFlatten();
ager@chromium.orge2902be2009-06-08 12:21:35 +00004201 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
4202}
4203
4204
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004205static Object* Runtime_SetProperty(Arguments args) {
4206 NoHandleAllocation ha;
4207 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
4208
4209 Handle<Object> object = args.at<Object>(0);
4210 Handle<Object> key = args.at<Object>(1);
4211 Handle<Object> value = args.at<Object>(2);
4212
4213 // Compute attributes.
4214 PropertyAttributes attributes = NONE;
4215 if (args.length() == 4) {
4216 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004217 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004218 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004219 RUNTIME_ASSERT(
4220 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4221 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004222 }
4223 return Runtime::SetObjectProperty(object, key, value, attributes);
4224}
4225
4226
4227// Set a local property, even if it is READ_ONLY. If the property does not
4228// exist, it will be added with attributes NONE.
4229static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
4230 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004231 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004232 CONVERT_CHECKED(JSObject, object, args[0]);
4233 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004234 // Compute attributes.
4235 PropertyAttributes attributes = NONE;
4236 if (args.length() == 4) {
4237 CONVERT_CHECKED(Smi, value_obj, args[3]);
4238 int unchecked_value = value_obj->value();
4239 // Only attribute bits should be set.
4240 RUNTIME_ASSERT(
4241 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4242 attributes = static_cast<PropertyAttributes>(unchecked_value);
4243 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004244
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004245 return object->
4246 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004247}
4248
4249
4250static Object* Runtime_DeleteProperty(Arguments args) {
4251 NoHandleAllocation ha;
4252 ASSERT(args.length() == 2);
4253
4254 CONVERT_CHECKED(JSObject, object, args[0]);
4255 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00004256 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004257}
4258
4259
ager@chromium.org9085a012009-05-11 19:22:57 +00004260static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
4261 Handle<String> key) {
4262 if (object->HasLocalProperty(*key)) return Heap::true_value();
4263 // Handle hidden prototypes. If there's a hidden prototype above this thing
4264 // then we have to check it for properties, because they are supposed to
4265 // look like they are on this object.
4266 Handle<Object> proto(object->GetPrototype());
4267 if (proto->IsJSObject() &&
4268 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
4269 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
4270 }
4271 return Heap::false_value();
4272}
4273
4274
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004275static Object* Runtime_HasLocalProperty(Arguments args) {
4276 NoHandleAllocation ha;
4277 ASSERT(args.length() == 2);
4278 CONVERT_CHECKED(String, key, args[1]);
4279
ager@chromium.org9085a012009-05-11 19:22:57 +00004280 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004281 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00004282 if (obj->IsJSObject()) {
4283 JSObject* object = JSObject::cast(obj);
4284 // Fast case - no interceptors.
4285 if (object->HasRealNamedProperty(key)) return Heap::true_value();
4286 // Slow case. Either it's not there or we have an interceptor. We should
4287 // have handles for this kind of deal.
4288 HandleScope scope;
4289 return HasLocalPropertyImplementation(Handle<JSObject>(object),
4290 Handle<String>(key));
4291 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004292 // Well, there is one exception: Handle [] on strings.
4293 uint32_t index;
4294 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00004295 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004296 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004297 return Heap::true_value();
4298 }
4299 }
4300 return Heap::false_value();
4301}
4302
4303
4304static Object* Runtime_HasProperty(Arguments args) {
4305 NoHandleAllocation na;
4306 ASSERT(args.length() == 2);
4307
4308 // Only JS objects can have properties.
4309 if (args[0]->IsJSObject()) {
4310 JSObject* object = JSObject::cast(args[0]);
4311 CONVERT_CHECKED(String, key, args[1]);
4312 if (object->HasProperty(key)) return Heap::true_value();
4313 }
4314 return Heap::false_value();
4315}
4316
4317
4318static Object* Runtime_HasElement(Arguments args) {
4319 NoHandleAllocation na;
4320 ASSERT(args.length() == 2);
4321
4322 // Only JS objects can have elements.
4323 if (args[0]->IsJSObject()) {
4324 JSObject* object = JSObject::cast(args[0]);
4325 CONVERT_CHECKED(Smi, index_obj, args[1]);
4326 uint32_t index = index_obj->value();
4327 if (object->HasElement(index)) return Heap::true_value();
4328 }
4329 return Heap::false_value();
4330}
4331
4332
4333static Object* Runtime_IsPropertyEnumerable(Arguments args) {
4334 NoHandleAllocation ha;
4335 ASSERT(args.length() == 2);
4336
4337 CONVERT_CHECKED(JSObject, object, args[0]);
4338 CONVERT_CHECKED(String, key, args[1]);
4339
4340 uint32_t index;
4341 if (key->AsArrayIndex(&index)) {
4342 return Heap::ToBoolean(object->HasElement(index));
4343 }
4344
ager@chromium.org870a0b62008-11-04 11:43:05 +00004345 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
4346 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004347}
4348
4349
4350static Object* Runtime_GetPropertyNames(Arguments args) {
4351 HandleScope scope;
4352 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004353 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004354 return *GetKeysFor(object);
4355}
4356
4357
4358// Returns either a FixedArray as Runtime_GetPropertyNames,
4359// or, if the given object has an enum cache that contains
4360// all enumerable properties of the object and its prototypes
4361// have none, the map of the object. This is used to speed up
4362// the check for deletions during a for-in.
4363static Object* Runtime_GetPropertyNamesFast(Arguments args) {
4364 ASSERT(args.length() == 1);
4365
4366 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4367
4368 if (raw_object->IsSimpleEnum()) return raw_object->map();
4369
4370 HandleScope scope;
4371 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004372 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
4373 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004374
4375 // Test again, since cache may have been built by preceding call.
4376 if (object->IsSimpleEnum()) return object->map();
4377
4378 return *content;
4379}
4380
4381
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004382// Find the length of the prototype chain that is to to handled as one. If a
4383// prototype object is hidden it is to be viewed as part of the the object it
4384// is prototype for.
4385static int LocalPrototypeChainLength(JSObject* obj) {
4386 int count = 1;
4387 Object* proto = obj->GetPrototype();
4388 while (proto->IsJSObject() &&
4389 JSObject::cast(proto)->map()->is_hidden_prototype()) {
4390 count++;
4391 proto = JSObject::cast(proto)->GetPrototype();
4392 }
4393 return count;
4394}
4395
4396
4397// Return the names of the local named properties.
4398// args[0]: object
4399static Object* Runtime_GetLocalPropertyNames(Arguments args) {
4400 HandleScope scope;
4401 ASSERT(args.length() == 1);
4402 if (!args[0]->IsJSObject()) {
4403 return Heap::undefined_value();
4404 }
4405 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4406
4407 // Skip the global proxy as it has no properties and always delegates to the
4408 // real global object.
4409 if (obj->IsJSGlobalProxy()) {
4410 // Only collect names if access is permitted.
4411 if (obj->IsAccessCheckNeeded() &&
4412 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
4413 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
4414 return *Factory::NewJSArray(0);
4415 }
4416 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
4417 }
4418
4419 // Find the number of objects making up this.
4420 int length = LocalPrototypeChainLength(*obj);
4421
4422 // Find the number of local properties for each of the objects.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004423 ScopedVector<int> local_property_count(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004424 int total_property_count = 0;
4425 Handle<JSObject> jsproto = obj;
4426 for (int i = 0; i < length; i++) {
4427 // Only collect names if access is permitted.
4428 if (jsproto->IsAccessCheckNeeded() &&
4429 !Top::MayNamedAccess(*jsproto,
4430 Heap::undefined_value(),
4431 v8::ACCESS_KEYS)) {
4432 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
4433 return *Factory::NewJSArray(0);
4434 }
4435 int n;
4436 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4437 local_property_count[i] = n;
4438 total_property_count += n;
4439 if (i < length - 1) {
4440 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4441 }
4442 }
4443
4444 // Allocate an array with storage for all the property names.
4445 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
4446
4447 // Get the property names.
4448 jsproto = obj;
4449 int proto_with_hidden_properties = 0;
4450 for (int i = 0; i < length; i++) {
4451 jsproto->GetLocalPropertyNames(*names,
4452 i == 0 ? 0 : local_property_count[i - 1]);
4453 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
4454 proto_with_hidden_properties++;
4455 }
4456 if (i < length - 1) {
4457 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4458 }
4459 }
4460
4461 // Filter out name of hidden propeties object.
4462 if (proto_with_hidden_properties > 0) {
4463 Handle<FixedArray> old_names = names;
4464 names = Factory::NewFixedArray(
4465 names->length() - proto_with_hidden_properties);
4466 int dest_pos = 0;
4467 for (int i = 0; i < total_property_count; i++) {
4468 Object* name = old_names->get(i);
4469 if (name == Heap::hidden_symbol()) {
4470 continue;
4471 }
4472 names->set(dest_pos++, name);
4473 }
4474 }
4475
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004476 return *Factory::NewJSArrayWithElements(names);
4477}
4478
4479
4480// Return the names of the local indexed properties.
4481// args[0]: object
4482static Object* Runtime_GetLocalElementNames(Arguments args) {
4483 HandleScope scope;
4484 ASSERT(args.length() == 1);
4485 if (!args[0]->IsJSObject()) {
4486 return Heap::undefined_value();
4487 }
4488 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4489
4490 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4491 Handle<FixedArray> names = Factory::NewFixedArray(n);
4492 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4493 return *Factory::NewJSArrayWithElements(names);
4494}
4495
4496
4497// Return information on whether an object has a named or indexed interceptor.
4498// args[0]: object
4499static Object* Runtime_GetInterceptorInfo(Arguments args) {
4500 HandleScope scope;
4501 ASSERT(args.length() == 1);
4502 if (!args[0]->IsJSObject()) {
4503 return Smi::FromInt(0);
4504 }
4505 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4506
4507 int result = 0;
4508 if (obj->HasNamedInterceptor()) result |= 2;
4509 if (obj->HasIndexedInterceptor()) result |= 1;
4510
4511 return Smi::FromInt(result);
4512}
4513
4514
4515// Return property names from named interceptor.
4516// args[0]: object
4517static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
4518 HandleScope scope;
4519 ASSERT(args.length() == 1);
4520 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4521
4522 if (obj->HasNamedInterceptor()) {
4523 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4524 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4525 }
4526 return Heap::undefined_value();
4527}
4528
4529
4530// Return element names from indexed interceptor.
4531// args[0]: object
4532static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
4533 HandleScope scope;
4534 ASSERT(args.length() == 1);
4535 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4536
4537 if (obj->HasIndexedInterceptor()) {
4538 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4539 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4540 }
4541 return Heap::undefined_value();
4542}
4543
4544
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004545static Object* Runtime_LocalKeys(Arguments args) {
4546 ASSERT_EQ(args.length(), 1);
4547 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4548 HandleScope scope;
4549 Handle<JSObject> object(raw_object);
4550 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
4551 LOCAL_ONLY);
4552 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
4553 // property array and since the result is mutable we have to create
4554 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004555 int length = contents->length();
4556 Handle<FixedArray> copy = Factory::NewFixedArray(length);
4557 for (int i = 0; i < length; i++) {
4558 Object* entry = contents->get(i);
4559 if (entry->IsString()) {
4560 copy->set(i, entry);
4561 } else {
4562 ASSERT(entry->IsNumber());
4563 HandleScope scope;
4564 Handle<Object> entry_handle(entry);
4565 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
4566 copy->set(i, *entry_str);
4567 }
4568 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004569 return *Factory::NewJSArrayWithElements(copy);
4570}
4571
4572
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004573static Object* Runtime_GetArgumentsProperty(Arguments args) {
4574 NoHandleAllocation ha;
4575 ASSERT(args.length() == 1);
4576
4577 // Compute the frame holding the arguments.
4578 JavaScriptFrameIterator it;
4579 it.AdvanceToArgumentsFrame();
4580 JavaScriptFrame* frame = it.frame();
4581
4582 // Get the actual number of provided arguments.
4583 const uint32_t n = frame->GetProvidedParametersCount();
4584
4585 // Try to convert the key to an index. If successful and within
4586 // index return the the argument from the frame.
4587 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004588 if (args[0]->ToArrayIndex(&index) && index < n) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004589 return frame->GetParameter(index);
4590 }
4591
4592 // Convert the key to a string.
4593 HandleScope scope;
4594 bool exception = false;
4595 Handle<Object> converted =
4596 Execution::ToString(args.at<Object>(0), &exception);
4597 if (exception) return Failure::Exception();
4598 Handle<String> key = Handle<String>::cast(converted);
4599
4600 // Try to convert the string key into an array index.
4601 if (key->AsArrayIndex(&index)) {
4602 if (index < n) {
4603 return frame->GetParameter(index);
4604 } else {
4605 return Top::initial_object_prototype()->GetElement(index);
4606 }
4607 }
4608
4609 // Handle special arguments properties.
4610 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
4611 if (key->Equals(Heap::callee_symbol())) return frame->function();
4612
4613 // Lookup in the initial Object.prototype object.
4614 return Top::initial_object_prototype()->GetProperty(*key);
4615}
4616
4617
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004618static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004619 HandleScope scope;
4620
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004621 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004622 Handle<Object> object = args.at<Object>(0);
4623 if (object->IsJSObject()) {
4624 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00004625 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
4626 js_object->TransformToFastProperties(0);
4627 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004628 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004629 return *object;
4630}
4631
4632
4633static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004634 HandleScope scope;
4635
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004636 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004637 Handle<Object> object = args.at<Object>(0);
4638 if (object->IsJSObject()) {
4639 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004640 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004641 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004642 return *object;
4643}
4644
4645
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004646static Object* Runtime_ToBool(Arguments args) {
4647 NoHandleAllocation ha;
4648 ASSERT(args.length() == 1);
4649
4650 return args[0]->ToBoolean();
4651}
4652
4653
4654// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
4655// Possible optimizations: put the type string into the oddballs.
4656static Object* Runtime_Typeof(Arguments args) {
4657 NoHandleAllocation ha;
4658
4659 Object* obj = args[0];
4660 if (obj->IsNumber()) return Heap::number_symbol();
4661 HeapObject* heap_obj = HeapObject::cast(obj);
4662
4663 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004664 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004665
4666 InstanceType instance_type = heap_obj->map()->instance_type();
4667 if (instance_type < FIRST_NONSTRING_TYPE) {
4668 return Heap::string_symbol();
4669 }
4670
4671 switch (instance_type) {
4672 case ODDBALL_TYPE:
4673 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
4674 return Heap::boolean_symbol();
4675 }
4676 if (heap_obj->IsNull()) {
4677 return Heap::object_symbol();
4678 }
4679 ASSERT(heap_obj->IsUndefined());
4680 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004681 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004682 return Heap::function_symbol();
4683 default:
4684 // For any kind of object not handled above, the spec rule for
4685 // host objects gives that it is okay to return "object"
4686 return Heap::object_symbol();
4687 }
4688}
4689
4690
lrn@chromium.org25156de2010-04-06 13:10:27 +00004691static bool AreDigits(const char*s, int from, int to) {
4692 for (int i = from; i < to; i++) {
4693 if (s[i] < '0' || s[i] > '9') return false;
4694 }
4695
4696 return true;
4697}
4698
4699
4700static int ParseDecimalInteger(const char*s, int from, int to) {
4701 ASSERT(to - from < 10); // Overflow is not possible.
4702 ASSERT(from < to);
4703 int d = s[from] - '0';
4704
4705 for (int i = from + 1; i < to; i++) {
4706 d = 10 * d + (s[i] - '0');
4707 }
4708
4709 return d;
4710}
4711
4712
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004713static Object* Runtime_StringToNumber(Arguments args) {
4714 NoHandleAllocation ha;
4715 ASSERT(args.length() == 1);
4716 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004717 subject->TryFlatten();
lrn@chromium.org25156de2010-04-06 13:10:27 +00004718
4719 // Fast case: short integer or some sorts of junk values.
4720 int len = subject->length();
4721 if (subject->IsSeqAsciiString()) {
4722 if (len == 0) return Smi::FromInt(0);
4723
4724 char const* data = SeqAsciiString::cast(subject)->GetChars();
4725 bool minus = (data[0] == '-');
4726 int start_pos = (minus ? 1 : 0);
4727
4728 if (start_pos == len) {
4729 return Heap::nan_value();
4730 } else if (data[start_pos] > '9') {
4731 // Fast check for a junk value. A valid string may start from a
4732 // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit or
4733 // the 'I' character ('Infinity'). All of that have codes not greater than
4734 // '9' except 'I'.
4735 if (data[start_pos] != 'I') {
4736 return Heap::nan_value();
4737 }
4738 } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
4739 // The maximal/minimal smi has 10 digits. If the string has less digits we
4740 // know it will fit into the smi-data type.
4741 int d = ParseDecimalInteger(data, start_pos, len);
4742 if (minus) {
4743 if (d == 0) return Heap::minus_zero_value();
4744 d = -d;
4745 }
4746 return Smi::FromInt(d);
4747 }
4748 }
4749
4750 // Slower case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004751 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
4752}
4753
4754
4755static Object* Runtime_StringFromCharCodeArray(Arguments args) {
4756 NoHandleAllocation ha;
4757 ASSERT(args.length() == 1);
4758
4759 CONVERT_CHECKED(JSArray, codes, args[0]);
4760 int length = Smi::cast(codes->length())->value();
4761
4762 // Check if the string can be ASCII.
4763 int i;
4764 for (i = 0; i < length; i++) {
4765 Object* element = codes->GetElement(i);
4766 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
4767 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
4768 break;
4769 }
4770
4771 Object* object = NULL;
4772 if (i == length) { // The string is ASCII.
4773 object = Heap::AllocateRawAsciiString(length);
4774 } else { // The string is not ASCII.
4775 object = Heap::AllocateRawTwoByteString(length);
4776 }
4777
4778 if (object->IsFailure()) return object;
4779 String* result = String::cast(object);
4780 for (int i = 0; i < length; i++) {
4781 Object* element = codes->GetElement(i);
4782 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004783 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004784 }
4785 return result;
4786}
4787
4788
4789// kNotEscaped is generated by the following:
4790//
4791// #!/bin/perl
4792// for (my $i = 0; $i < 256; $i++) {
4793// print "\n" if $i % 16 == 0;
4794// my $c = chr($i);
4795// my $escaped = 1;
4796// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
4797// print $escaped ? "0, " : "1, ";
4798// }
4799
4800
4801static bool IsNotEscaped(uint16_t character) {
4802 // Only for 8 bit characters, the rest are always escaped (in a different way)
4803 ASSERT(character < 256);
4804 static const char kNotEscaped[256] = {
4805 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4806 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4807 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
4808 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
4809 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4810 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
4811 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4812 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
4813 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4814 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4815 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4816 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4817 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4818 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4819 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4820 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4821 };
4822 return kNotEscaped[character] != 0;
4823}
4824
4825
4826static Object* Runtime_URIEscape(Arguments args) {
4827 const char hex_chars[] = "0123456789ABCDEF";
4828 NoHandleAllocation ha;
4829 ASSERT(args.length() == 1);
4830 CONVERT_CHECKED(String, source, args[0]);
4831
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004832 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004833
4834 int escaped_length = 0;
4835 int length = source->length();
4836 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004837 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004838 buffer->Reset(source);
4839 while (buffer->has_more()) {
4840 uint16_t character = buffer->GetNext();
4841 if (character >= 256) {
4842 escaped_length += 6;
4843 } else if (IsNotEscaped(character)) {
4844 escaped_length++;
4845 } else {
4846 escaped_length += 3;
4847 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004848 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004849 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004850 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004851 Top::context()->mark_out_of_memory();
4852 return Failure::OutOfMemoryException();
4853 }
4854 }
4855 }
4856 // No length change implies no change. Return original string if no change.
4857 if (escaped_length == length) {
4858 return source;
4859 }
4860 Object* o = Heap::AllocateRawAsciiString(escaped_length);
4861 if (o->IsFailure()) return o;
4862 String* destination = String::cast(o);
4863 int dest_position = 0;
4864
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004865 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004866 buffer->Rewind();
4867 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00004868 uint16_t chr = buffer->GetNext();
4869 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004870 destination->Set(dest_position, '%');
4871 destination->Set(dest_position+1, 'u');
4872 destination->Set(dest_position+2, hex_chars[chr >> 12]);
4873 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
4874 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
4875 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004876 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00004877 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004878 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004879 dest_position++;
4880 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004881 destination->Set(dest_position, '%');
4882 destination->Set(dest_position+1, hex_chars[chr >> 4]);
4883 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004884 dest_position += 3;
4885 }
4886 }
4887 return destination;
4888}
4889
4890
4891static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
4892 static const signed char kHexValue['g'] = {
4893 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4894 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4895 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4896 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
4897 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4898 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4899 -1, 10, 11, 12, 13, 14, 15 };
4900
4901 if (character1 > 'f') return -1;
4902 int hi = kHexValue[character1];
4903 if (hi == -1) return -1;
4904 if (character2 > 'f') return -1;
4905 int lo = kHexValue[character2];
4906 if (lo == -1) return -1;
4907 return (hi << 4) + lo;
4908}
4909
4910
ager@chromium.org870a0b62008-11-04 11:43:05 +00004911static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004912 int i,
4913 int length,
4914 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004915 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00004916 int32_t hi = 0;
4917 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004918 if (character == '%' &&
4919 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004920 source->Get(i + 1) == 'u' &&
4921 (hi = TwoDigitHex(source->Get(i + 2),
4922 source->Get(i + 3))) != -1 &&
4923 (lo = TwoDigitHex(source->Get(i + 4),
4924 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004925 *step = 6;
4926 return (hi << 8) + lo;
4927 } else if (character == '%' &&
4928 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004929 (lo = TwoDigitHex(source->Get(i + 1),
4930 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004931 *step = 3;
4932 return lo;
4933 } else {
4934 *step = 1;
4935 return character;
4936 }
4937}
4938
4939
4940static Object* Runtime_URIUnescape(Arguments args) {
4941 NoHandleAllocation ha;
4942 ASSERT(args.length() == 1);
4943 CONVERT_CHECKED(String, source, args[0]);
4944
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004945 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004946
4947 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004948 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004949
4950 int unescaped_length = 0;
4951 for (int i = 0; i < length; unescaped_length++) {
4952 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004953 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004954 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004955 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004956 i += step;
4957 }
4958
4959 // No length change implies no change. Return original string if no change.
4960 if (unescaped_length == length)
4961 return source;
4962
4963 Object* o = ascii ?
4964 Heap::AllocateRawAsciiString(unescaped_length) :
4965 Heap::AllocateRawTwoByteString(unescaped_length);
4966 if (o->IsFailure()) return o;
4967 String* destination = String::cast(o);
4968
4969 int dest_position = 0;
4970 for (int i = 0; i < length; dest_position++) {
4971 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004972 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004973 i += step;
4974 }
4975 return destination;
4976}
4977
4978
4979static Object* Runtime_StringParseInt(Arguments args) {
4980 NoHandleAllocation ha;
4981
4982 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004983 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004984
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004985 s->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004986
lrn@chromium.org25156de2010-04-06 13:10:27 +00004987 RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
4988 double value = StringToInt(s, radix);
4989 return Heap::NumberFromDouble(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004990 return Heap::nan_value();
4991}
4992
4993
4994static Object* Runtime_StringParseFloat(Arguments args) {
4995 NoHandleAllocation ha;
4996 CONVERT_CHECKED(String, str, args[0]);
4997
4998 // ECMA-262 section 15.1.2.3, empty string is NaN
4999 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
5000
5001 // Create a number object from the value.
5002 return Heap::NumberFromDouble(value);
5003}
5004
5005
5006static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
5007static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
5008
5009
5010template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005011static Object* ConvertCaseHelper(String* s,
5012 int length,
5013 int input_string_length,
5014 unibrow::Mapping<Converter, 128>* mapping) {
5015 // We try this twice, once with the assumption that the result is no longer
5016 // than the input and, if that assumption breaks, again with the exact
5017 // length. This may not be pretty, but it is nicer than what was here before
5018 // and I hereby claim my vaffel-is.
5019 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005020 // Allocate the resulting string.
5021 //
5022 // NOTE: This assumes that the upper/lower case of an ascii
5023 // character is also ascii. This is currently the case, but it
5024 // might break in the future if we implement more context and locale
5025 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00005026 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005027 ? Heap::AllocateRawAsciiString(length)
5028 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005029 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005030 String* result = String::cast(o);
5031 bool has_changed_character = false;
5032
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005033 // Convert all characters to upper case, assuming that they will fit
5034 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005035 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005036 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005037 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005038 // We can assume that the string is not empty
5039 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005040 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00005041 bool has_next = buffer->has_more();
5042 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005043 int char_length = mapping->get(current, next, chars);
5044 if (char_length == 0) {
5045 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005046 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005047 i++;
5048 } else if (char_length == 1) {
5049 // Common case: converting the letter resulted in one character.
5050 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005051 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005052 has_changed_character = true;
5053 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005054 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005055 // We've assumed that the result would be as long as the
5056 // input but here is a character that converts to several
5057 // characters. No matter, we calculate the exact length
5058 // of the result and try the whole thing again.
5059 //
5060 // Note that this leaves room for optimization. We could just
5061 // memcpy what we already have to the result string. Also,
5062 // the result string is the last object allocated we could
5063 // "realloc" it and probably, in the vast majority of cases,
5064 // extend the existing string to be able to hold the full
5065 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00005066 int next_length = 0;
5067 if (has_next) {
5068 next_length = mapping->get(next, 0, chars);
5069 if (next_length == 0) next_length = 1;
5070 }
5071 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005072 while (buffer->has_more()) {
5073 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00005074 // NOTE: we use 0 as the next character here because, while
5075 // the next character may affect what a character converts to,
5076 // it does not in any case affect the length of what it convert
5077 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005078 int char_length = mapping->get(current, 0, chars);
5079 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00005080 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005081 if (current_length > Smi::kMaxValue) {
5082 Top::context()->mark_out_of_memory();
5083 return Failure::OutOfMemoryException();
5084 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005085 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005086 // Try again with the real length.
5087 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005088 } else {
5089 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005090 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005091 i++;
5092 }
5093 has_changed_character = true;
5094 }
5095 current = next;
5096 }
5097 if (has_changed_character) {
5098 return result;
5099 } else {
5100 // If we didn't actually change anything in doing the conversion
5101 // we simple return the result and let the converted string
5102 // become garbage; there is no reason to keep two identical strings
5103 // alive.
5104 return s;
5105 }
5106}
5107
5108
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005109namespace {
5110
5111struct ToLowerTraits {
5112 typedef unibrow::ToLowercase UnibrowConverter;
5113
5114 static bool ConvertAscii(char* dst, char* src, int length) {
5115 bool changed = false;
5116 for (int i = 0; i < length; ++i) {
5117 char c = src[i];
5118 if ('A' <= c && c <= 'Z') {
5119 c += ('a' - 'A');
5120 changed = true;
5121 }
5122 dst[i] = c;
5123 }
5124 return changed;
5125 }
5126};
5127
5128
5129struct ToUpperTraits {
5130 typedef unibrow::ToUppercase UnibrowConverter;
5131
5132 static bool ConvertAscii(char* dst, char* src, int length) {
5133 bool changed = false;
5134 for (int i = 0; i < length; ++i) {
5135 char c = src[i];
5136 if ('a' <= c && c <= 'z') {
5137 c -= ('a' - 'A');
5138 changed = true;
5139 }
5140 dst[i] = c;
5141 }
5142 return changed;
5143 }
5144};
5145
5146} // namespace
5147
5148
5149template <typename ConvertTraits>
5150static Object* ConvertCase(
5151 Arguments args,
5152 unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005153 NoHandleAllocation ha;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005154 CONVERT_CHECKED(String, s, args[0]);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005155 s = s->TryFlattenGetString();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005156
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005157 const int length = s->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005158 // Assume that the string is not empty; we need this assumption later
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005159 if (length == 0) return s;
5160
5161 // Simpler handling of ascii strings.
5162 //
5163 // NOTE: This assumes that the upper/lower case of an ascii
5164 // character is also ascii. This is currently the case, but it
5165 // might break in the future if we implement more context and locale
5166 // dependent upper/lower conversions.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005167 if (s->IsSeqAsciiString()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005168 Object* o = Heap::AllocateRawAsciiString(length);
5169 if (o->IsFailure()) return o;
5170 SeqAsciiString* result = SeqAsciiString::cast(o);
5171 bool has_changed_character = ConvertTraits::ConvertAscii(
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005172 result->GetChars(), SeqAsciiString::cast(s)->GetChars(), length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005173 return has_changed_character ? result : s;
5174 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005175
5176 Object* answer = ConvertCaseHelper(s, length, length, mapping);
5177 if (answer->IsSmi()) {
5178 // Retry with correct length.
5179 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
5180 }
5181 return answer; // This may be a failure.
5182}
5183
5184
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005185static Object* Runtime_StringToLowerCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005186 return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005187}
5188
5189
5190static Object* Runtime_StringToUpperCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005191 return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005192}
5193
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005194
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005195static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
5196 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
5197}
5198
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005199
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005200static Object* Runtime_StringTrim(Arguments args) {
5201 NoHandleAllocation ha;
5202 ASSERT(args.length() == 3);
5203
5204 CONVERT_CHECKED(String, s, args[0]);
5205 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
5206 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
5207
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005208 s->TryFlatten();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005209 int length = s->length();
5210
5211 int left = 0;
5212 if (trimLeft) {
5213 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
5214 left++;
5215 }
5216 }
5217
5218 int right = length;
5219 if (trimRight) {
5220 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
5221 right--;
5222 }
5223 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005224 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005225}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005226
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005227
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005228template <typename schar, typename pchar>
5229void FindStringIndices(Vector<const schar> subject,
5230 Vector<const pchar> pattern,
5231 ZoneList<int>* indices,
5232 unsigned int limit) {
5233 ASSERT(limit > 0);
5234 // Collect indices of pattern in subject, and the end-of-string index.
5235 // Stop after finding at most limit values.
5236 StringSearchStrategy strategy =
5237 InitializeStringSearch(pattern, sizeof(schar) == 1);
5238 switch (strategy) {
5239 case SEARCH_FAIL: return;
5240 case SEARCH_SHORT: {
5241 int pattern_length = pattern.length();
5242 int index = 0;
5243 while (limit > 0) {
5244 index = SimpleIndexOf(subject, pattern, index);
5245 if (index < 0) return;
5246 indices->Add(index);
5247 index += pattern_length;
5248 limit--;
5249 }
5250 return;
5251 }
5252 case SEARCH_LONG: {
5253 int pattern_length = pattern.length();
5254 int index = 0;
5255 while (limit > 0) {
5256 index = ComplexIndexOf(subject, pattern, index);
5257 if (index < 0) return;
5258 indices->Add(index);
5259 index += pattern_length;
5260 limit--;
5261 }
5262 return;
5263 }
5264 default:
5265 UNREACHABLE();
5266 return;
5267 }
5268}
5269
5270template <typename schar>
5271inline void FindCharIndices(Vector<const schar> subject,
5272 const schar pattern_char,
5273 ZoneList<int>* indices,
5274 unsigned int limit) {
5275 // Collect indices of pattern_char in subject, and the end-of-string index.
5276 // Stop after finding at most limit values.
5277 int index = 0;
5278 while (limit > 0) {
5279 index = SingleCharIndexOf(subject, pattern_char, index);
5280 if (index < 0) return;
5281 indices->Add(index);
5282 index++;
5283 limit--;
5284 }
5285}
5286
5287
5288static Object* Runtime_StringSplit(Arguments args) {
5289 ASSERT(args.length() == 3);
5290 HandleScope handle_scope;
5291 CONVERT_ARG_CHECKED(String, subject, 0);
5292 CONVERT_ARG_CHECKED(String, pattern, 1);
5293 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
5294
5295 int subject_length = subject->length();
5296 int pattern_length = pattern->length();
5297 RUNTIME_ASSERT(pattern_length > 0);
5298
5299 // The limit can be very large (0xffffffffu), but since the pattern
5300 // isn't empty, we can never create more parts than ~half the length
5301 // of the subject.
5302
5303 if (!subject->IsFlat()) FlattenString(subject);
5304
5305 static const int kMaxInitialListCapacity = 16;
5306
5307 ZoneScope scope(DELETE_ON_EXIT);
5308
5309 // Find (up to limit) indices of separator and end-of-string in subject
5310 int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
5311 ZoneList<int> indices(initial_capacity);
5312 if (pattern_length == 1) {
5313 // Special case, go directly to fast single-character split.
5314 AssertNoAllocation nogc;
5315 uc16 pattern_char = pattern->Get(0);
5316 if (subject->IsTwoByteRepresentation()) {
5317 FindCharIndices(subject->ToUC16Vector(), pattern_char,
5318 &indices,
5319 limit);
5320 } else if (pattern_char <= String::kMaxAsciiCharCode) {
5321 FindCharIndices(subject->ToAsciiVector(),
5322 static_cast<char>(pattern_char),
5323 &indices,
5324 limit);
5325 }
5326 } else {
5327 if (!pattern->IsFlat()) FlattenString(pattern);
5328 AssertNoAllocation nogc;
5329 if (subject->IsAsciiRepresentation()) {
5330 Vector<const char> subject_vector = subject->ToAsciiVector();
5331 if (pattern->IsAsciiRepresentation()) {
5332 FindStringIndices(subject_vector,
5333 pattern->ToAsciiVector(),
5334 &indices,
5335 limit);
5336 } else {
5337 FindStringIndices(subject_vector,
5338 pattern->ToUC16Vector(),
5339 &indices,
5340 limit);
5341 }
5342 } else {
5343 Vector<const uc16> subject_vector = subject->ToUC16Vector();
5344 if (pattern->IsAsciiRepresentation()) {
5345 FindStringIndices(subject_vector,
5346 pattern->ToAsciiVector(),
5347 &indices,
5348 limit);
5349 } else {
5350 FindStringIndices(subject_vector,
5351 pattern->ToUC16Vector(),
5352 &indices,
5353 limit);
5354 }
5355 }
5356 }
5357 if (static_cast<uint32_t>(indices.length()) < limit) {
5358 indices.Add(subject_length);
5359 }
5360 // The list indices now contains the end of each part to create.
5361
5362
5363 // Create JSArray of substrings separated by separator.
5364 int part_count = indices.length();
5365
5366 Handle<JSArray> result = Factory::NewJSArray(part_count);
5367 result->set_length(Smi::FromInt(part_count));
5368
5369 ASSERT(result->HasFastElements());
5370
5371 if (part_count == 1 && indices.at(0) == subject_length) {
5372 FixedArray::cast(result->elements())->set(0, *subject);
5373 return *result;
5374 }
5375
5376 Handle<FixedArray> elements(FixedArray::cast(result->elements()));
5377 int part_start = 0;
5378 for (int i = 0; i < part_count; i++) {
5379 HandleScope local_loop_handle;
5380 int part_end = indices.at(i);
5381 Handle<String> substring =
5382 Factory::NewSubString(subject, part_start, part_end);
5383 elements->set(i, *substring);
5384 part_start = part_end + pattern_length;
5385 }
5386
5387 return *result;
5388}
5389
5390
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005391// Copies ascii characters to the given fixed array looking up
5392// one-char strings in the cache. Gives up on the first char that is
5393// not in the cache and fills the remainder with smi zeros. Returns
5394// the length of the successfully copied prefix.
5395static int CopyCachedAsciiCharsToArray(const char* chars,
5396 FixedArray* elements,
5397 int length) {
5398 AssertNoAllocation nogc;
5399 FixedArray* ascii_cache = Heap::single_character_string_cache();
5400 Object* undefined = Heap::undefined_value();
5401 int i;
5402 for (i = 0; i < length; ++i) {
5403 Object* value = ascii_cache->get(chars[i]);
5404 if (value == undefined) break;
5405 ASSERT(!Heap::InNewSpace(value));
5406 elements->set(i, value, SKIP_WRITE_BARRIER);
5407 }
5408 if (i < length) {
5409 ASSERT(Smi::FromInt(0) == 0);
5410 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
5411 }
5412#ifdef DEBUG
5413 for (int j = 0; j < length; ++j) {
5414 Object* element = elements->get(j);
5415 ASSERT(element == Smi::FromInt(0) ||
5416 (element->IsString() && String::cast(element)->LooksValid()));
5417 }
5418#endif
5419 return i;
5420}
5421
5422
5423// Converts a String to JSArray.
5424// For example, "foo" => ["f", "o", "o"].
5425static Object* Runtime_StringToArray(Arguments args) {
5426 HandleScope scope;
5427 ASSERT(args.length() == 1);
5428 CONVERT_ARG_CHECKED(String, s, 0);
5429
5430 s->TryFlatten();
5431 const int length = s->length();
5432
5433 Handle<FixedArray> elements;
5434 if (s->IsFlat() && s->IsAsciiRepresentation()) {
5435 Object* obj = Heap::AllocateUninitializedFixedArray(length);
5436 if (obj->IsFailure()) return obj;
5437 elements = Handle<FixedArray>(FixedArray::cast(obj));
5438
5439 Vector<const char> chars = s->ToAsciiVector();
5440 // Note, this will initialize all elements (not only the prefix)
5441 // to prevent GC from seeing partially initialized array.
5442 int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
5443 *elements,
5444 length);
5445
5446 for (int i = num_copied_from_cache; i < length; ++i) {
5447 elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
5448 }
5449 } else {
5450 elements = Factory::NewFixedArray(length);
5451 for (int i = 0; i < length; ++i) {
5452 elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
5453 }
5454 }
5455
5456#ifdef DEBUG
5457 for (int i = 0; i < length; ++i) {
5458 ASSERT(String::cast(elements->get(i))->length() == 1);
5459 }
5460#endif
5461
5462 return *Factory::NewJSArrayWithElements(elements);
5463}
5464
5465
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00005466bool Runtime::IsUpperCaseChar(uint16_t ch) {
5467 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
5468 int char_length = to_upper_mapping.get(ch, 0, chars);
5469 return char_length == 0;
5470}
5471
5472
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005473static Object* Runtime_NumberToString(Arguments args) {
5474 NoHandleAllocation ha;
5475 ASSERT(args.length() == 1);
5476
5477 Object* number = args[0];
5478 RUNTIME_ASSERT(number->IsNumber());
5479
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005480 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005481}
5482
5483
ager@chromium.org357bf652010-04-12 11:30:10 +00005484static Object* Runtime_NumberToStringSkipCache(Arguments args) {
5485 NoHandleAllocation ha;
5486 ASSERT(args.length() == 1);
5487
5488 Object* number = args[0];
5489 RUNTIME_ASSERT(number->IsNumber());
5490
5491 return Heap::NumberToString(number, false);
5492}
5493
5494
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005495static Object* Runtime_NumberToInteger(Arguments args) {
5496 NoHandleAllocation ha;
5497 ASSERT(args.length() == 1);
5498
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005499 CONVERT_DOUBLE_CHECKED(number, args[0]);
5500
5501 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5502 if (number > 0 && number <= Smi::kMaxValue) {
5503 return Smi::FromInt(static_cast<int>(number));
5504 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005505 return Heap::NumberFromDouble(DoubleToInteger(number));
5506}
5507
5508
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005509static Object* Runtime_NumberToIntegerMapMinusZero(Arguments args) {
5510 NoHandleAllocation ha;
5511 ASSERT(args.length() == 1);
5512
5513 CONVERT_DOUBLE_CHECKED(number, args[0]);
5514
5515 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5516 if (number > 0 && number <= Smi::kMaxValue) {
5517 return Smi::FromInt(static_cast<int>(number));
5518 }
5519
5520 double double_value = DoubleToInteger(number);
5521 // Map both -0 and +0 to +0.
5522 if (double_value == 0) double_value = 0;
5523
5524 return Heap::NumberFromDouble(double_value);
5525}
5526
5527
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005528static Object* Runtime_NumberToJSUint32(Arguments args) {
5529 NoHandleAllocation ha;
5530 ASSERT(args.length() == 1);
5531
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005532 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005533 return Heap::NumberFromUint32(number);
5534}
5535
5536
5537static Object* Runtime_NumberToJSInt32(Arguments args) {
5538 NoHandleAllocation ha;
5539 ASSERT(args.length() == 1);
5540
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005541 CONVERT_DOUBLE_CHECKED(number, args[0]);
5542
5543 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5544 if (number > 0 && number <= Smi::kMaxValue) {
5545 return Smi::FromInt(static_cast<int>(number));
5546 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005547 return Heap::NumberFromInt32(DoubleToInt32(number));
5548}
5549
5550
ager@chromium.org870a0b62008-11-04 11:43:05 +00005551// Converts a Number to a Smi, if possible. Returns NaN if the number is not
5552// a small integer.
5553static Object* Runtime_NumberToSmi(Arguments args) {
5554 NoHandleAllocation ha;
5555 ASSERT(args.length() == 1);
5556
5557 Object* obj = args[0];
5558 if (obj->IsSmi()) {
5559 return obj;
5560 }
5561 if (obj->IsHeapNumber()) {
5562 double value = HeapNumber::cast(obj)->value();
5563 int int_value = FastD2I(value);
5564 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
5565 return Smi::FromInt(int_value);
5566 }
5567 }
5568 return Heap::nan_value();
5569}
5570
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005571
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005572static Object* Runtime_NumberAdd(Arguments args) {
5573 NoHandleAllocation ha;
5574 ASSERT(args.length() == 2);
5575
5576 CONVERT_DOUBLE_CHECKED(x, args[0]);
5577 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005578 return Heap::NumberFromDouble(x + y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005579}
5580
5581
5582static Object* Runtime_NumberSub(Arguments args) {
5583 NoHandleAllocation ha;
5584 ASSERT(args.length() == 2);
5585
5586 CONVERT_DOUBLE_CHECKED(x, args[0]);
5587 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005588 return Heap::NumberFromDouble(x - y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005589}
5590
5591
5592static Object* Runtime_NumberMul(Arguments args) {
5593 NoHandleAllocation ha;
5594 ASSERT(args.length() == 2);
5595
5596 CONVERT_DOUBLE_CHECKED(x, args[0]);
5597 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005598 return Heap::NumberFromDouble(x * y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005599}
5600
5601
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005602static Object* Runtime_NumberUnaryMinus(Arguments args) {
5603 NoHandleAllocation ha;
5604 ASSERT(args.length() == 1);
5605
5606 CONVERT_DOUBLE_CHECKED(x, args[0]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005607 return Heap::NumberFromDouble(-x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005608}
5609
5610
5611static Object* Runtime_NumberDiv(Arguments args) {
5612 NoHandleAllocation ha;
5613 ASSERT(args.length() == 2);
5614
5615 CONVERT_DOUBLE_CHECKED(x, args[0]);
5616 CONVERT_DOUBLE_CHECKED(y, args[1]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005617 return Heap::NumberFromDouble(x / y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005618}
5619
5620
5621static Object* Runtime_NumberMod(Arguments args) {
5622 NoHandleAllocation ha;
5623 ASSERT(args.length() == 2);
5624
5625 CONVERT_DOUBLE_CHECKED(x, args[0]);
5626 CONVERT_DOUBLE_CHECKED(y, args[1]);
5627
ager@chromium.org3811b432009-10-28 14:53:37 +00005628 x = modulo(x, y);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005629 // NumberFromDouble may return a Smi instead of a Number object
5630 return Heap::NumberFromDouble(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005631}
5632
5633
5634static Object* Runtime_StringAdd(Arguments args) {
5635 NoHandleAllocation ha;
5636 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005637 CONVERT_CHECKED(String, str1, args[0]);
5638 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005639 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00005640 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005641}
5642
5643
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005644template <typename sinkchar>
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005645static inline void StringBuilderConcatHelper(String* special,
5646 sinkchar* sink,
5647 FixedArray* fixed_array,
5648 int array_length) {
5649 int position = 0;
5650 for (int i = 0; i < array_length; i++) {
5651 Object* element = fixed_array->get(i);
5652 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005653 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005654 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005655 int pos;
5656 int len;
5657 if (encoded_slice > 0) {
5658 // Position and length encoded in one smi.
5659 pos = StringBuilderSubstringPosition::decode(encoded_slice);
5660 len = StringBuilderSubstringLength::decode(encoded_slice);
5661 } else {
5662 // Position and length encoded in two smis.
5663 Object* obj = fixed_array->get(++i);
5664 ASSERT(obj->IsSmi());
5665 pos = Smi::cast(obj)->value();
5666 len = -encoded_slice;
5667 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00005668 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00005669 sink + position,
5670 pos,
5671 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005672 position += len;
5673 } else {
5674 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005675 int element_length = string->length();
5676 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005677 position += element_length;
5678 }
5679 }
5680}
5681
5682
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005683static Object* Runtime_StringBuilderConcat(Arguments args) {
5684 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005685 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005686 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005687 if (!args[1]->IsSmi()) {
5688 Top::context()->mark_out_of_memory();
5689 return Failure::OutOfMemoryException();
5690 }
5691 int array_length = Smi::cast(args[1])->value();
5692 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005693
5694 // This assumption is used by the slice encoding in one or two smis.
5695 ASSERT(Smi::kMaxValue >= String::kMaxLength);
5696
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005697 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005698 if (!array->HasFastElements()) {
5699 return Top::Throw(Heap::illegal_argument_symbol());
5700 }
5701 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005702 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005703 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005704 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005705
5706 if (array_length == 0) {
5707 return Heap::empty_string();
5708 } else if (array_length == 1) {
5709 Object* first = fixed_array->get(0);
5710 if (first->IsString()) return first;
5711 }
5712
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005713 bool ascii = special->HasOnlyAsciiChars();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005714 int position = 0;
5715 for (int i = 0; i < array_length; i++) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005716 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005717 Object* elt = fixed_array->get(i);
5718 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005719 // Smi encoding of position and length.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005720 int smi_value = Smi::cast(elt)->value();
5721 int pos;
5722 int len;
5723 if (smi_value > 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005724 // Position and length encoded in one smi.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005725 pos = StringBuilderSubstringPosition::decode(smi_value);
5726 len = StringBuilderSubstringLength::decode(smi_value);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005727 } else {
5728 // Position and length encoded in two smis.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005729 len = -smi_value;
5730 // Get the position and check that it is a positive smi.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005731 i++;
5732 if (i >= array_length) {
5733 return Top::Throw(Heap::illegal_argument_symbol());
5734 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005735 Object* next_smi = fixed_array->get(i);
5736 if (!next_smi->IsSmi()) {
5737 return Top::Throw(Heap::illegal_argument_symbol());
5738 }
5739 pos = Smi::cast(next_smi)->value();
5740 if (pos < 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005741 return Top::Throw(Heap::illegal_argument_symbol());
5742 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005743 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005744 ASSERT(pos >= 0);
5745 ASSERT(len >= 0);
5746 if (pos > special_length || len > special_length - pos) {
5747 return Top::Throw(Heap::illegal_argument_symbol());
5748 }
5749 increment = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005750 } else if (elt->IsString()) {
5751 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005752 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005753 increment = element_length;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005754 if (ascii && !element->HasOnlyAsciiChars()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005755 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005756 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005757 } else {
5758 return Top::Throw(Heap::illegal_argument_symbol());
5759 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005760 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005761 Top::context()->mark_out_of_memory();
5762 return Failure::OutOfMemoryException();
5763 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005764 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005765 }
5766
5767 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005768 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005769
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005770 if (ascii) {
5771 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005772 if (object->IsFailure()) return object;
5773 SeqAsciiString* answer = SeqAsciiString::cast(object);
5774 StringBuilderConcatHelper(special,
5775 answer->GetChars(),
5776 fixed_array,
5777 array_length);
5778 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005779 } else {
5780 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005781 if (object->IsFailure()) return object;
5782 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
5783 StringBuilderConcatHelper(special,
5784 answer->GetChars(),
5785 fixed_array,
5786 array_length);
5787 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005788 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005789}
5790
5791
5792static Object* Runtime_NumberOr(Arguments args) {
5793 NoHandleAllocation ha;
5794 ASSERT(args.length() == 2);
5795
5796 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5797 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5798 return Heap::NumberFromInt32(x | y);
5799}
5800
5801
5802static Object* Runtime_NumberAnd(Arguments args) {
5803 NoHandleAllocation ha;
5804 ASSERT(args.length() == 2);
5805
5806 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5807 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5808 return Heap::NumberFromInt32(x & y);
5809}
5810
5811
5812static Object* Runtime_NumberXor(Arguments args) {
5813 NoHandleAllocation ha;
5814 ASSERT(args.length() == 2);
5815
5816 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5817 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5818 return Heap::NumberFromInt32(x ^ y);
5819}
5820
5821
5822static Object* Runtime_NumberNot(Arguments args) {
5823 NoHandleAllocation ha;
5824 ASSERT(args.length() == 1);
5825
5826 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5827 return Heap::NumberFromInt32(~x);
5828}
5829
5830
5831static Object* Runtime_NumberShl(Arguments args) {
5832 NoHandleAllocation ha;
5833 ASSERT(args.length() == 2);
5834
5835 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5836 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5837 return Heap::NumberFromInt32(x << (y & 0x1f));
5838}
5839
5840
5841static Object* Runtime_NumberShr(Arguments args) {
5842 NoHandleAllocation ha;
5843 ASSERT(args.length() == 2);
5844
5845 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
5846 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5847 return Heap::NumberFromUint32(x >> (y & 0x1f));
5848}
5849
5850
5851static Object* Runtime_NumberSar(Arguments args) {
5852 NoHandleAllocation ha;
5853 ASSERT(args.length() == 2);
5854
5855 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5856 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5857 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
5858}
5859
5860
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005861static Object* Runtime_NumberEquals(Arguments args) {
5862 NoHandleAllocation ha;
5863 ASSERT(args.length() == 2);
5864
5865 CONVERT_DOUBLE_CHECKED(x, args[0]);
5866 CONVERT_DOUBLE_CHECKED(y, args[1]);
5867 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
5868 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
5869 if (x == y) return Smi::FromInt(EQUAL);
5870 Object* result;
5871 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
5872 result = Smi::FromInt(EQUAL);
5873 } else {
5874 result = Smi::FromInt(NOT_EQUAL);
5875 }
5876 return result;
5877}
5878
5879
5880static Object* Runtime_StringEquals(Arguments args) {
5881 NoHandleAllocation ha;
5882 ASSERT(args.length() == 2);
5883
5884 CONVERT_CHECKED(String, x, args[0]);
5885 CONVERT_CHECKED(String, y, args[1]);
5886
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005887 bool not_equal = !x->Equals(y);
5888 // This is slightly convoluted because the value that signifies
5889 // equality is 0 and inequality is 1 so we have to negate the result
5890 // from String::Equals.
5891 ASSERT(not_equal == 0 || not_equal == 1);
5892 STATIC_CHECK(EQUAL == 0);
5893 STATIC_CHECK(NOT_EQUAL == 1);
5894 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005895}
5896
5897
5898static Object* Runtime_NumberCompare(Arguments args) {
5899 NoHandleAllocation ha;
5900 ASSERT(args.length() == 3);
5901
5902 CONVERT_DOUBLE_CHECKED(x, args[0]);
5903 CONVERT_DOUBLE_CHECKED(y, args[1]);
5904 if (isnan(x) || isnan(y)) return args[2];
5905 if (x == y) return Smi::FromInt(EQUAL);
5906 if (isless(x, y)) return Smi::FromInt(LESS);
5907 return Smi::FromInt(GREATER);
5908}
5909
5910
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005911// Compare two Smis as if they were converted to strings and then
5912// compared lexicographically.
5913static Object* Runtime_SmiLexicographicCompare(Arguments args) {
5914 NoHandleAllocation ha;
5915 ASSERT(args.length() == 2);
5916
5917 // Arrays for the individual characters of the two Smis. Smis are
5918 // 31 bit integers and 10 decimal digits are therefore enough.
5919 static int x_elms[10];
5920 static int y_elms[10];
5921
5922 // Extract the integer values from the Smis.
5923 CONVERT_CHECKED(Smi, x, args[0]);
5924 CONVERT_CHECKED(Smi, y, args[1]);
5925 int x_value = x->value();
5926 int y_value = y->value();
5927
5928 // If the integers are equal so are the string representations.
5929 if (x_value == y_value) return Smi::FromInt(EQUAL);
5930
5931 // If one of the integers are zero the normal integer order is the
5932 // same as the lexicographic order of the string representations.
5933 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
5934
ager@chromium.org32912102009-01-16 10:38:43 +00005935 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005936 // smallest because the char code of '-' is less than the char code
5937 // of any digit. Otherwise, we make both values positive.
5938 if (x_value < 0 || y_value < 0) {
5939 if (y_value >= 0) return Smi::FromInt(LESS);
5940 if (x_value >= 0) return Smi::FromInt(GREATER);
5941 x_value = -x_value;
5942 y_value = -y_value;
5943 }
5944
5945 // Convert the integers to arrays of their decimal digits.
5946 int x_index = 0;
5947 int y_index = 0;
5948 while (x_value > 0) {
5949 x_elms[x_index++] = x_value % 10;
5950 x_value /= 10;
5951 }
5952 while (y_value > 0) {
5953 y_elms[y_index++] = y_value % 10;
5954 y_value /= 10;
5955 }
5956
5957 // Loop through the arrays of decimal digits finding the first place
5958 // where they differ.
5959 while (--x_index >= 0 && --y_index >= 0) {
5960 int diff = x_elms[x_index] - y_elms[y_index];
5961 if (diff != 0) return Smi::FromInt(diff);
5962 }
5963
5964 // If one array is a suffix of the other array, the longest array is
5965 // the representation of the largest of the Smis in the
5966 // lexicographic ordering.
5967 return Smi::FromInt(x_index - y_index);
5968}
5969
5970
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005971static Object* StringInputBufferCompare(String* x, String* y) {
5972 static StringInputBuffer bufx;
5973 static StringInputBuffer bufy;
5974 bufx.Reset(x);
5975 bufy.Reset(y);
5976 while (bufx.has_more() && bufy.has_more()) {
5977 int d = bufx.GetNext() - bufy.GetNext();
5978 if (d < 0) return Smi::FromInt(LESS);
5979 else if (d > 0) return Smi::FromInt(GREATER);
5980 }
5981
5982 // x is (non-trivial) prefix of y:
5983 if (bufy.has_more()) return Smi::FromInt(LESS);
5984 // y is prefix of x:
5985 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
5986}
5987
5988
5989static Object* FlatStringCompare(String* x, String* y) {
5990 ASSERT(x->IsFlat());
5991 ASSERT(y->IsFlat());
5992 Object* equal_prefix_result = Smi::FromInt(EQUAL);
5993 int prefix_length = x->length();
5994 if (y->length() < prefix_length) {
5995 prefix_length = y->length();
5996 equal_prefix_result = Smi::FromInt(GREATER);
5997 } else if (y->length() > prefix_length) {
5998 equal_prefix_result = Smi::FromInt(LESS);
5999 }
6000 int r;
6001 if (x->IsAsciiRepresentation()) {
6002 Vector<const char> x_chars = x->ToAsciiVector();
6003 if (y->IsAsciiRepresentation()) {
6004 Vector<const char> y_chars = y->ToAsciiVector();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006005 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006006 } else {
6007 Vector<const uc16> y_chars = y->ToUC16Vector();
6008 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
6009 }
6010 } else {
6011 Vector<const uc16> x_chars = x->ToUC16Vector();
6012 if (y->IsAsciiRepresentation()) {
6013 Vector<const char> y_chars = y->ToAsciiVector();
6014 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
6015 } else {
6016 Vector<const uc16> y_chars = y->ToUC16Vector();
6017 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
6018 }
6019 }
6020 Object* result;
6021 if (r == 0) {
6022 result = equal_prefix_result;
6023 } else {
6024 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
6025 }
6026 ASSERT(result == StringInputBufferCompare(x, y));
6027 return result;
6028}
6029
6030
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006031static Object* Runtime_StringCompare(Arguments args) {
6032 NoHandleAllocation ha;
6033 ASSERT(args.length() == 2);
6034
6035 CONVERT_CHECKED(String, x, args[0]);
6036 CONVERT_CHECKED(String, y, args[1]);
6037
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006038 Counters::string_compare_runtime.Increment();
6039
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006040 // A few fast case tests before we flatten.
6041 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006042 if (y->length() == 0) {
6043 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006044 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006045 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006046 return Smi::FromInt(LESS);
6047 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006048
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006049 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006050 if (d < 0) return Smi::FromInt(LESS);
6051 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006052
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006053 Object* obj = Heap::PrepareForCompare(x);
6054 if (obj->IsFailure()) return obj;
6055 obj = Heap::PrepareForCompare(y);
6056 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006057
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006058 return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
6059 : StringInputBufferCompare(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006060}
6061
6062
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006063static Object* Runtime_Math_acos(Arguments args) {
6064 NoHandleAllocation ha;
6065 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006066 Counters::math_acos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006067
6068 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006069 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006070}
6071
6072
6073static Object* Runtime_Math_asin(Arguments args) {
6074 NoHandleAllocation ha;
6075 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006076 Counters::math_asin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006077
6078 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006079 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006080}
6081
6082
6083static Object* Runtime_Math_atan(Arguments args) {
6084 NoHandleAllocation ha;
6085 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006086 Counters::math_atan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006087
6088 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006089 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006090}
6091
6092
6093static Object* Runtime_Math_atan2(Arguments args) {
6094 NoHandleAllocation ha;
6095 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006096 Counters::math_atan2.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006097
6098 CONVERT_DOUBLE_CHECKED(x, args[0]);
6099 CONVERT_DOUBLE_CHECKED(y, args[1]);
6100 double result;
6101 if (isinf(x) && isinf(y)) {
6102 // Make sure that the result in case of two infinite arguments
6103 // is a multiple of Pi / 4. The sign of the result is determined
6104 // by the first argument (x) and the sign of the second argument
6105 // determines the multiplier: one or three.
6106 static double kPiDividedBy4 = 0.78539816339744830962;
6107 int multiplier = (x < 0) ? -1 : 1;
6108 if (y < 0) multiplier *= 3;
6109 result = multiplier * kPiDividedBy4;
6110 } else {
6111 result = atan2(x, y);
6112 }
6113 return Heap::AllocateHeapNumber(result);
6114}
6115
6116
6117static Object* Runtime_Math_ceil(Arguments args) {
6118 NoHandleAllocation ha;
6119 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006120 Counters::math_ceil.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006121
6122 CONVERT_DOUBLE_CHECKED(x, args[0]);
6123 return Heap::NumberFromDouble(ceiling(x));
6124}
6125
6126
6127static Object* Runtime_Math_cos(Arguments args) {
6128 NoHandleAllocation ha;
6129 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006130 Counters::math_cos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006131
6132 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006133 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006134}
6135
6136
6137static Object* Runtime_Math_exp(Arguments args) {
6138 NoHandleAllocation ha;
6139 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006140 Counters::math_exp.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006141
6142 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006143 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006144}
6145
6146
6147static Object* Runtime_Math_floor(Arguments args) {
6148 NoHandleAllocation ha;
6149 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006150 Counters::math_floor.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006151
6152 CONVERT_DOUBLE_CHECKED(x, args[0]);
6153 return Heap::NumberFromDouble(floor(x));
6154}
6155
6156
6157static Object* Runtime_Math_log(Arguments args) {
6158 NoHandleAllocation ha;
6159 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006160 Counters::math_log.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006161
6162 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006163 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006164}
6165
6166
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006167// Helper function to compute x^y, where y is known to be an
6168// integer. Uses binary decomposition to limit the number of
6169// multiplications; see the discussion in "Hacker's Delight" by Henry
6170// S. Warren, Jr., figure 11-6, page 213.
6171static double powi(double x, int y) {
6172 ASSERT(y != kMinInt);
6173 unsigned n = (y < 0) ? -y : y;
6174 double m = x;
6175 double p = 1;
6176 while (true) {
6177 if ((n & 1) != 0) p *= m;
6178 n >>= 1;
6179 if (n == 0) {
6180 if (y < 0) {
6181 // Unfortunately, we have to be careful when p has reached
6182 // infinity in the computation, because sometimes the higher
6183 // internal precision in the pow() implementation would have
6184 // given us a finite p. This happens very rarely.
6185 double result = 1.0 / p;
6186 return (result == 0 && isinf(p))
6187 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
6188 : result;
6189 } else {
6190 return p;
6191 }
6192 }
6193 m *= m;
6194 }
6195}
6196
6197
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006198static Object* Runtime_Math_pow(Arguments args) {
6199 NoHandleAllocation ha;
6200 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006201 Counters::math_pow.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006202
6203 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006204
6205 // If the second argument is a smi, it is much faster to call the
6206 // custom powi() function than the generic pow().
6207 if (args[1]->IsSmi()) {
6208 int y = Smi::cast(args[1])->value();
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00006209 return Heap::NumberFromDouble(powi(x, y));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006210 }
6211
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006212 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00006213
6214 if (!isinf(x)) {
6215 if (y == 0.5) {
6216 // It's not uncommon to use Math.pow(x, 0.5) to compute the
6217 // square root of a number. To speed up such computations, we
6218 // explictly check for this case and use the sqrt() function
6219 // which is faster than pow().
6220 return Heap::AllocateHeapNumber(sqrt(x));
6221 } else if (y == -0.5) {
6222 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
6223 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
6224 }
6225 }
6226
6227 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006228 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006229 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6230 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006231 } else {
6232 return Heap::AllocateHeapNumber(pow(x, y));
6233 }
6234}
6235
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006236// Fast version of Math.pow if we know that y is not an integer and
6237// y is not -0.5 or 0.5. Used as slowcase from codegen.
6238static Object* Runtime_Math_pow_cfunction(Arguments args) {
6239 NoHandleAllocation ha;
6240 ASSERT(args.length() == 2);
6241 CONVERT_DOUBLE_CHECKED(x, args[0]);
6242 CONVERT_DOUBLE_CHECKED(y, args[1]);
6243 if (y == 0) {
6244 return Smi::FromInt(1);
6245 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6246 return Heap::nan_value();
6247 } else {
6248 return Heap::AllocateHeapNumber(pow(x, y));
6249 }
6250}
6251
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006252
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006253static Object* Runtime_RoundNumber(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006254 NoHandleAllocation ha;
6255 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006256 Counters::math_round.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006257
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006258 if (!args[0]->IsHeapNumber()) {
6259 // Must be smi. Return the argument unchanged for all the other types
6260 // to make fuzz-natives test happy.
6261 return args[0];
6262 }
6263
6264 HeapNumber* number = reinterpret_cast<HeapNumber*>(args[0]);
6265
6266 double value = number->value();
6267 int exponent = number->get_exponent();
6268 int sign = number->get_sign();
6269
6270 // We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
6271 // should be rounded to 2^30, which is not smi.
6272 if (!sign && exponent <= kSmiValueSize - 3) {
6273 return Smi::FromInt(static_cast<int>(value + 0.5));
6274 }
6275
6276 // If the magnitude is big enough, there's no place for fraction part. If we
6277 // try to add 0.5 to this number, 1.0 will be added instead.
6278 if (exponent >= 52) {
6279 return number;
6280 }
6281
6282 if (sign && value >= -0.5) return Heap::minus_zero_value();
6283
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006284 // Do not call NumberFromDouble() to avoid extra checks.
6285 return Heap::AllocateHeapNumber(floor(value + 0.5));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006286}
6287
6288
6289static Object* Runtime_Math_sin(Arguments args) {
6290 NoHandleAllocation ha;
6291 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006292 Counters::math_sin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006293
6294 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006295 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006296}
6297
6298
6299static Object* Runtime_Math_sqrt(Arguments args) {
6300 NoHandleAllocation ha;
6301 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006302 Counters::math_sqrt.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006303
6304 CONVERT_DOUBLE_CHECKED(x, args[0]);
6305 return Heap::AllocateHeapNumber(sqrt(x));
6306}
6307
6308
6309static Object* Runtime_Math_tan(Arguments args) {
6310 NoHandleAllocation ha;
6311 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006312 Counters::math_tan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006313
6314 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006315 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006316}
6317
6318
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006319static int MakeDay(int year, int month, int day) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006320 static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
6321 181, 212, 243, 273, 304, 334};
6322 static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
6323 182, 213, 244, 274, 305, 335};
6324
6325 year += month / 12;
6326 month %= 12;
6327 if (month < 0) {
6328 year--;
6329 month += 12;
6330 }
6331
6332 ASSERT(month >= 0);
6333 ASSERT(month < 12);
6334
6335 // year_delta is an arbitrary number such that:
6336 // a) year_delta = -1 (mod 400)
6337 // b) year + year_delta > 0 for years in the range defined by
6338 // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
6339 // Jan 1 1970. This is required so that we don't run into integer
6340 // division of negative numbers.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006341 // c) there shouldn't be an overflow for 32-bit integers in the following
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006342 // operations.
6343 static const int year_delta = 399999;
6344 static const int base_day = 365 * (1970 + year_delta) +
6345 (1970 + year_delta) / 4 -
6346 (1970 + year_delta) / 100 +
6347 (1970 + year_delta) / 400;
6348
6349 int year1 = year + year_delta;
6350 int day_from_year = 365 * year1 +
6351 year1 / 4 -
6352 year1 / 100 +
6353 year1 / 400 -
6354 base_day;
6355
6356 if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006357 return day_from_year + day_from_month[month] + day - 1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006358 }
6359
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006360 return day_from_year + day_from_month_leap[month] + day - 1;
6361}
6362
6363
6364static Object* Runtime_DateMakeDay(Arguments args) {
6365 NoHandleAllocation ha;
6366 ASSERT(args.length() == 3);
6367
6368 CONVERT_SMI_CHECKED(year, args[0]);
6369 CONVERT_SMI_CHECKED(month, args[1]);
6370 CONVERT_SMI_CHECKED(date, args[2]);
6371
6372 return Smi::FromInt(MakeDay(year, month, date));
6373}
6374
6375
6376static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
6377static const int kDaysIn4Years = 4 * 365 + 1;
6378static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
6379static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
6380static const int kDays1970to2000 = 30 * 365 + 7;
6381static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
6382 kDays1970to2000;
6383static const int kYearsOffset = 400000;
6384
6385static const char kDayInYear[] = {
6386 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6387 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6388 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6389 22, 23, 24, 25, 26, 27, 28,
6390 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6391 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6392 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6393 22, 23, 24, 25, 26, 27, 28, 29, 30,
6394 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6395 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6396 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6397 22, 23, 24, 25, 26, 27, 28, 29, 30,
6398 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6399 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6400 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6401 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6402 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6403 22, 23, 24, 25, 26, 27, 28, 29, 30,
6404 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6405 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6406 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6407 22, 23, 24, 25, 26, 27, 28, 29, 30,
6408 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6409 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6410
6411 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6412 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6413 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6414 22, 23, 24, 25, 26, 27, 28,
6415 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6416 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6417 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6418 22, 23, 24, 25, 26, 27, 28, 29, 30,
6419 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6420 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6421 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6422 22, 23, 24, 25, 26, 27, 28, 29, 30,
6423 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6424 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6425 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6426 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6427 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6428 22, 23, 24, 25, 26, 27, 28, 29, 30,
6429 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6430 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6431 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6432 22, 23, 24, 25, 26, 27, 28, 29, 30,
6433 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6434 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6435
6436 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6437 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6438 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6439 22, 23, 24, 25, 26, 27, 28, 29,
6440 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6441 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6442 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6443 22, 23, 24, 25, 26, 27, 28, 29, 30,
6444 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6445 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6446 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6447 22, 23, 24, 25, 26, 27, 28, 29, 30,
6448 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6449 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6450 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6451 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6452 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6453 22, 23, 24, 25, 26, 27, 28, 29, 30,
6454 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6455 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6456 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6457 22, 23, 24, 25, 26, 27, 28, 29, 30,
6458 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6459 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6460
6461 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6462 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6463 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6464 22, 23, 24, 25, 26, 27, 28,
6465 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6466 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6467 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6468 22, 23, 24, 25, 26, 27, 28, 29, 30,
6469 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6470 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6471 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6472 22, 23, 24, 25, 26, 27, 28, 29, 30,
6473 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6474 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6475 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6476 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6477 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6478 22, 23, 24, 25, 26, 27, 28, 29, 30,
6479 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6480 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6481 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6482 22, 23, 24, 25, 26, 27, 28, 29, 30,
6483 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6484 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
6485
6486static const char kMonthInYear[] = {
6487 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,
6488 0, 0, 0, 0, 0, 0,
6489 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,
6490 1, 1, 1,
6491 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,
6492 2, 2, 2, 2, 2, 2,
6493 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,
6494 3, 3, 3, 3, 3,
6495 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,
6496 4, 4, 4, 4, 4, 4,
6497 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,
6498 5, 5, 5, 5, 5,
6499 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,
6500 6, 6, 6, 6, 6, 6,
6501 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,
6502 7, 7, 7, 7, 7, 7,
6503 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,
6504 8, 8, 8, 8, 8,
6505 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,
6506 9, 9, 9, 9, 9, 9,
6507 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6508 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6509 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6510 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6511
6512 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,
6513 0, 0, 0, 0, 0, 0,
6514 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,
6515 1, 1, 1,
6516 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,
6517 2, 2, 2, 2, 2, 2,
6518 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,
6519 3, 3, 3, 3, 3,
6520 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,
6521 4, 4, 4, 4, 4, 4,
6522 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,
6523 5, 5, 5, 5, 5,
6524 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,
6525 6, 6, 6, 6, 6, 6,
6526 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,
6527 7, 7, 7, 7, 7, 7,
6528 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,
6529 8, 8, 8, 8, 8,
6530 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,
6531 9, 9, 9, 9, 9, 9,
6532 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6533 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6534 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6535 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6536
6537 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,
6538 0, 0, 0, 0, 0, 0,
6539 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,
6540 1, 1, 1, 1,
6541 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,
6542 2, 2, 2, 2, 2, 2,
6543 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,
6544 3, 3, 3, 3, 3,
6545 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,
6546 4, 4, 4, 4, 4, 4,
6547 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,
6548 5, 5, 5, 5, 5,
6549 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,
6550 6, 6, 6, 6, 6, 6,
6551 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,
6552 7, 7, 7, 7, 7, 7,
6553 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,
6554 8, 8, 8, 8, 8,
6555 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,
6556 9, 9, 9, 9, 9, 9,
6557 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6558 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6559 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6560 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6561
6562 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,
6563 0, 0, 0, 0, 0, 0,
6564 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,
6565 1, 1, 1,
6566 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,
6567 2, 2, 2, 2, 2, 2,
6568 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,
6569 3, 3, 3, 3, 3,
6570 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,
6571 4, 4, 4, 4, 4, 4,
6572 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,
6573 5, 5, 5, 5, 5,
6574 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,
6575 6, 6, 6, 6, 6, 6,
6576 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,
6577 7, 7, 7, 7, 7, 7,
6578 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,
6579 8, 8, 8, 8, 8,
6580 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,
6581 9, 9, 9, 9, 9, 9,
6582 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6583 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6584 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6585 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
6586
6587
6588// This function works for dates from 1970 to 2099.
6589static inline void DateYMDFromTimeAfter1970(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006590 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006591#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006592 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006593#endif
6594
6595 year = 1970 + (4 * date + 2) / kDaysIn4Years;
6596 date %= kDaysIn4Years;
6597
6598 month = kMonthInYear[date];
6599 day = kDayInYear[date];
6600
6601 ASSERT(MakeDay(year, month, day) == save_date);
6602}
6603
6604
6605static inline void DateYMDFromTimeSlow(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006606 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006607#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006608 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006609#endif
6610
6611 date += kDaysOffset;
6612 year = 400 * (date / kDaysIn400Years) - kYearsOffset;
6613 date %= kDaysIn400Years;
6614
6615 ASSERT(MakeDay(year, 0, 1) + date == save_date);
6616
6617 date--;
6618 int yd1 = date / kDaysIn100Years;
6619 date %= kDaysIn100Years;
6620 year += 100 * yd1;
6621
6622 date++;
6623 int yd2 = date / kDaysIn4Years;
6624 date %= kDaysIn4Years;
6625 year += 4 * yd2;
6626
6627 date--;
6628 int yd3 = date / 365;
6629 date %= 365;
6630 year += yd3;
6631
6632 bool is_leap = (!yd1 || yd2) && !yd3;
6633
6634 ASSERT(date >= -1);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006635 ASSERT(is_leap || (date >= 0));
6636 ASSERT((date < 365) || (is_leap && (date < 366)));
6637 ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0))));
6638 ASSERT(is_leap || ((MakeDay(year, 0, 1) + date) == save_date));
6639 ASSERT(!is_leap || ((MakeDay(year, 0, 1) + date + 1) == save_date));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006640
6641 if (is_leap) {
6642 day = kDayInYear[2*365 + 1 + date];
6643 month = kMonthInYear[2*365 + 1 + date];
6644 } else {
6645 day = kDayInYear[date];
6646 month = kMonthInYear[date];
6647 }
6648
6649 ASSERT(MakeDay(year, month, day) == save_date);
6650}
6651
6652
6653static inline void DateYMDFromTime(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006654 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006655 if (date >= 0 && date < 32 * kDaysIn4Years) {
6656 DateYMDFromTimeAfter1970(date, year, month, day);
6657 } else {
6658 DateYMDFromTimeSlow(date, year, month, day);
6659 }
6660}
6661
6662
6663static Object* Runtime_DateYMDFromTime(Arguments args) {
6664 NoHandleAllocation ha;
6665 ASSERT(args.length() == 2);
6666
6667 CONVERT_DOUBLE_CHECKED(t, args[0]);
6668 CONVERT_CHECKED(JSArray, res_array, args[1]);
6669
6670 int year, month, day;
6671 DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
6672
6673 res_array->SetElement(0, Smi::FromInt(year));
6674 res_array->SetElement(1, Smi::FromInt(month));
6675 res_array->SetElement(2, Smi::FromInt(day));
6676
6677 return Heap::undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006678}
6679
6680
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006681static Object* Runtime_NewArgumentsFast(Arguments args) {
6682 NoHandleAllocation ha;
6683 ASSERT(args.length() == 3);
6684
6685 JSFunction* callee = JSFunction::cast(args[0]);
6686 Object** parameters = reinterpret_cast<Object**>(args[1]);
6687 const int length = Smi::cast(args[2])->value();
6688
6689 Object* result = Heap::AllocateArgumentsObject(callee, length);
6690 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006691 // Allocate the elements if needed.
6692 if (length > 0) {
6693 // Allocate the fixed array.
6694 Object* obj = Heap::AllocateRawFixedArray(length);
6695 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006696
6697 AssertNoAllocation no_gc;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006698 FixedArray* array = reinterpret_cast<FixedArray*>(obj);
6699 array->set_map(Heap::fixed_array_map());
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006700 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006701
6702 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006703 for (int i = 0; i < length; i++) {
6704 array->set(i, *--parameters, mode);
6705 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006706 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006707 }
6708 return result;
6709}
6710
6711
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006712static Object* Runtime_NewClosure(Arguments args) {
6713 HandleScope scope;
6714 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00006715 CONVERT_ARG_CHECKED(Context, context, 0);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006716 CONVERT_ARG_CHECKED(SharedFunctionInfo, shared, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006717
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00006718 PretenureFlag pretenure = (context->global_context() == *context)
6719 ? TENURED // Allocate global closures in old space.
6720 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006721 Handle<JSFunction> result =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006722 Factory::NewFunctionFromSharedFunctionInfo(shared, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006723 return *result;
6724}
6725
6726
ager@chromium.org5c838252010-02-19 08:53:10 +00006727static Code* ComputeConstructStub(Handle<JSFunction> function) {
6728 Handle<Object> prototype = Factory::null_value();
6729 if (function->has_instance_prototype()) {
6730 prototype = Handle<Object>(function->instance_prototype());
6731 }
6732 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006733 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00006734 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006735 if (code->IsFailure()) {
6736 return Builtins::builtin(Builtins::JSConstructStubGeneric);
6737 }
6738 return Code::cast(code);
6739 }
6740
ager@chromium.org5c838252010-02-19 08:53:10 +00006741 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006742}
6743
6744
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006745static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006746 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006747 ASSERT(args.length() == 1);
6748
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006749 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006750
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006751 // If the constructor isn't a proper function we throw a type error.
6752 if (!constructor->IsJSFunction()) {
6753 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6754 Handle<Object> type_error =
6755 Factory::NewTypeError("not_constructor", arguments);
6756 return Top::Throw(*type_error);
6757 }
6758
6759 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006760
6761 // If function should not have prototype, construction is not allowed. In this
6762 // case generated code bailouts here, since function has no initial_map.
6763 if (!function->should_have_prototype()) {
6764 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6765 Handle<Object> type_error =
6766 Factory::NewTypeError("not_constructor", arguments);
6767 return Top::Throw(*type_error);
6768 }
6769
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006770#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006771 // Handle stepping into constructors if step into is active.
6772 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006773 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006774 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006775#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006776
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006777 if (function->has_initial_map()) {
6778 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006779 // The 'Function' function ignores the receiver object when
6780 // called using 'new' and creates a new JSFunction object that
6781 // is returned. The receiver object is only used for error
6782 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006783 // JSFunction. Factory::NewJSObject() should not be used to
6784 // allocate JSFunctions since it does not properly initialize
6785 // the shared part of the function. Since the receiver is
6786 // ignored anyway, we use the global object as the receiver
6787 // instead of a new JSFunction object. This way, errors are
6788 // reported the same way whether or not 'Function' is called
6789 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006790 return Top::context()->global();
6791 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006792 }
6793
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006794 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006795 Handle<SharedFunctionInfo> shared(function->shared());
6796 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006797
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006798 bool first_allocation = !function->has_initial_map();
6799 Handle<JSObject> result = Factory::NewJSObject(function);
6800 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006801 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00006802 ComputeConstructStub(Handle<JSFunction>(function)));
6803 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006804 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006805
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006806 Counters::constructed_objects.Increment();
6807 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006808
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006809 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006810}
6811
6812
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006813static Object* Runtime_LazyCompile(Arguments args) {
6814 HandleScope scope;
6815 ASSERT(args.length() == 1);
6816
6817 Handle<JSFunction> function = args.at<JSFunction>(0);
6818#ifdef DEBUG
6819 if (FLAG_trace_lazy) {
6820 PrintF("[lazy: ");
6821 function->shared()->name()->Print();
6822 PrintF("]\n");
6823 }
6824#endif
6825
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006826 // Compile the target function. Here we compile using CompileLazyInLoop in
6827 // order to get the optimized version. This helps code like delta-blue
6828 // that calls performance-critical routines through constructors. A
6829 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
6830 // direct call. Since the in-loop tracking takes place through CallICs
6831 // this means that things called through constructors are never known to
6832 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006833 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006834 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006835 return Failure::Exception();
6836 }
6837
6838 return function->code();
6839}
6840
6841
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006842static Object* Runtime_GetFunctionDelegate(Arguments args) {
6843 HandleScope scope;
6844 ASSERT(args.length() == 1);
6845 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6846 return *Execution::GetFunctionDelegate(args.at<Object>(0));
6847}
6848
6849
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00006850static Object* Runtime_GetConstructorDelegate(Arguments args) {
6851 HandleScope scope;
6852 ASSERT(args.length() == 1);
6853 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6854 return *Execution::GetConstructorDelegate(args.at<Object>(0));
6855}
6856
6857
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006858static Object* Runtime_NewContext(Arguments args) {
6859 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00006860 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006861
kasper.lund7276f142008-07-30 08:49:36 +00006862 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006863 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
6864 Object* result = Heap::AllocateFunctionContext(length, function);
6865 if (result->IsFailure()) return result;
6866
6867 Top::set_context(Context::cast(result));
6868
kasper.lund7276f142008-07-30 08:49:36 +00006869 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006870}
6871
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006872static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006873 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006874 Object* js_object = object;
6875 if (!js_object->IsJSObject()) {
6876 js_object = js_object->ToObject();
6877 if (js_object->IsFailure()) {
6878 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006879 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006880 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006881 Handle<Object> result =
6882 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
6883 return Top::Throw(*result);
6884 }
6885 }
6886
6887 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006888 Heap::AllocateWithContext(Top::context(),
6889 JSObject::cast(js_object),
6890 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006891 if (result->IsFailure()) return result;
6892
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006893 Context* context = Context::cast(result);
6894 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006895
kasper.lund7276f142008-07-30 08:49:36 +00006896 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006897}
6898
6899
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006900static Object* Runtime_PushContext(Arguments args) {
6901 NoHandleAllocation ha;
6902 ASSERT(args.length() == 1);
6903 return PushContextHelper(args[0], false);
6904}
6905
6906
6907static Object* Runtime_PushCatchContext(Arguments args) {
6908 NoHandleAllocation ha;
6909 ASSERT(args.length() == 1);
6910 return PushContextHelper(args[0], true);
6911}
6912
6913
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006914static Object* Runtime_LookupContext(Arguments args) {
6915 HandleScope scope;
6916 ASSERT(args.length() == 2);
6917
6918 CONVERT_ARG_CHECKED(Context, context, 0);
6919 CONVERT_ARG_CHECKED(String, name, 1);
6920
6921 int index;
6922 PropertyAttributes attributes;
6923 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006924 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006925 context->Lookup(name, flags, &index, &attributes);
6926
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006927 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006928 ASSERT(holder->IsJSObject());
6929 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006930 }
6931
6932 // No intermediate context found. Use global object by default.
6933 return Top::context()->global();
6934}
6935
6936
ager@chromium.orga1645e22009-09-09 19:27:10 +00006937// A mechanism to return a pair of Object pointers in registers (if possible).
6938// How this is achieved is calling convention-dependent.
6939// All currently supported x86 compiles uses calling conventions that are cdecl
6940// variants where a 64-bit value is returned in two 32-bit registers
6941// (edx:eax on ia32, r1:r0 on ARM).
6942// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
6943// In Win64 calling convention, a struct of two pointers is returned in memory,
6944// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006945#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006946struct ObjectPair {
6947 Object* x;
6948 Object* y;
6949};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006950
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006951static inline ObjectPair MakePair(Object* x, Object* y) {
6952 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006953 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
6954 // In Win64 they are assigned to a hidden first argument.
6955 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006956}
6957#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006958typedef uint64_t ObjectPair;
6959static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006960 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006961 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006962}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006963#endif
6964
6965
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006966static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006967 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
6968 USE(attributes);
6969 return x->IsTheHole() ? Heap::undefined_value() : x;
6970}
6971
6972
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006973static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
6974 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006975 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006976 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006977 JSFunction* context_extension_function =
6978 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006979 // If the holder isn't a context extension object, we just return it
6980 // as the receiver. This allows arguments objects to be used as
6981 // receivers, but only if they are put in the context scope chain
6982 // explicitly via a with-statement.
6983 Object* constructor = holder->map()->constructor();
6984 if (constructor != context_extension_function) return holder;
6985 // Fall back to using the global object as the receiver if the
6986 // property turns out to be a local variable allocated in a context
6987 // extension object - introduced via eval.
6988 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006989}
6990
6991
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006992static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006993 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00006994 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006995
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006996 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00006997 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006998 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006999 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007000 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007001
7002 int index;
7003 PropertyAttributes attributes;
7004 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007005 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007006 context->Lookup(name, flags, &index, &attributes);
7007
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007008 // If the index is non-negative, the slot has been found in a local
7009 // variable or a parameter. Read it from the context object or the
7010 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007011 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007012 // If the "property" we were looking for is a local variable or an
7013 // argument in a context, the receiver is the global object; see
7014 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
7015 JSObject* receiver = Top::context()->global()->global_receiver();
7016 Object* value = (holder->IsContext())
7017 ? Context::cast(*holder)->get(index)
7018 : JSObject::cast(*holder)->GetElement(index);
7019 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007020 }
7021
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007022 // If the holder is found, we read the property from it.
7023 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007024 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007025 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007026 JSObject* receiver;
7027 if (object->IsGlobalObject()) {
7028 receiver = GlobalObject::cast(object)->global_receiver();
7029 } else if (context->is_exception_holder(*holder)) {
7030 receiver = Top::context()->global()->global_receiver();
7031 } else {
7032 receiver = ComputeReceiverForNonGlobal(object);
7033 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007034 // No need to unhole the value here. This is taken care of by the
7035 // GetProperty function.
7036 Object* value = object->GetProperty(*name);
7037 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007038 }
7039
7040 if (throw_error) {
7041 // The property doesn't exist - throw exception.
7042 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007043 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007044 return MakePair(Top::Throw(*reference_error), NULL);
7045 } else {
7046 // The property doesn't exist - return undefined
7047 return MakePair(Heap::undefined_value(), Heap::undefined_value());
7048 }
7049}
7050
7051
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007052static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007053 return LoadContextSlotHelper(args, true);
7054}
7055
7056
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007057static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007058 return LoadContextSlotHelper(args, false);
7059}
7060
7061
7062static Object* Runtime_StoreContextSlot(Arguments args) {
7063 HandleScope scope;
7064 ASSERT(args.length() == 3);
7065
7066 Handle<Object> value(args[0]);
7067 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007068 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007069
7070 int index;
7071 PropertyAttributes attributes;
7072 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007073 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007074 context->Lookup(name, flags, &index, &attributes);
7075
7076 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007077 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007078 // Ignore if read_only variable.
7079 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007080 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007081 }
7082 } else {
7083 ASSERT((attributes & READ_ONLY) == 0);
7084 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007085 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007086 USE(result);
7087 ASSERT(!result->IsFailure());
7088 }
7089 return *value;
7090 }
7091
7092 // Slow case: The property is not in a FixedArray context.
7093 // It is either in an JSObject extension context or it was not found.
7094 Handle<JSObject> context_ext;
7095
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007096 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007097 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007098 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007099 } else {
7100 // The property was not found. It needs to be stored in the global context.
7101 ASSERT(attributes == ABSENT);
7102 attributes = NONE;
7103 context_ext = Handle<JSObject>(Top::context()->global());
7104 }
7105
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007106 // Set the property, but ignore if read_only variable on the context
7107 // extension object itself.
7108 if ((attributes & READ_ONLY) == 0 ||
7109 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007110 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
7111 if (set.is_null()) {
7112 // Failure::Exception is converted to a null handle in the
7113 // handle-based methods such as SetProperty. We therefore need
7114 // to convert null handles back to exceptions.
7115 ASSERT(Top::has_pending_exception());
7116 return Failure::Exception();
7117 }
7118 }
7119 return *value;
7120}
7121
7122
7123static Object* Runtime_Throw(Arguments args) {
7124 HandleScope scope;
7125 ASSERT(args.length() == 1);
7126
7127 return Top::Throw(args[0]);
7128}
7129
7130
7131static Object* Runtime_ReThrow(Arguments args) {
7132 HandleScope scope;
7133 ASSERT(args.length() == 1);
7134
7135 return Top::ReThrow(args[0]);
7136}
7137
7138
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007139static Object* Runtime_PromoteScheduledException(Arguments args) {
7140 ASSERT_EQ(0, args.length());
7141 return Top::PromoteScheduledException();
7142}
7143
7144
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007145static Object* Runtime_ThrowReferenceError(Arguments args) {
7146 HandleScope scope;
7147 ASSERT(args.length() == 1);
7148
7149 Handle<Object> name(args[0]);
7150 Handle<Object> reference_error =
7151 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
7152 return Top::Throw(*reference_error);
7153}
7154
7155
7156static Object* Runtime_StackOverflow(Arguments args) {
7157 NoHandleAllocation na;
7158 return Top::StackOverflow();
7159}
7160
7161
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007162static Object* Runtime_StackGuard(Arguments args) {
7163 ASSERT(args.length() == 1);
7164
7165 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007166 if (StackGuard::IsStackOverflow()) {
7167 return Runtime_StackOverflow(args);
7168 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007169
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007170 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007171}
7172
7173
7174// NOTE: These PrintXXX functions are defined for all builds (not just
7175// DEBUG builds) because we may want to be able to trace function
7176// calls in all modes.
7177static void PrintString(String* str) {
7178 // not uncommon to have empty strings
7179 if (str->length() > 0) {
7180 SmartPointer<char> s =
7181 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
7182 PrintF("%s", *s);
7183 }
7184}
7185
7186
7187static void PrintObject(Object* obj) {
7188 if (obj->IsSmi()) {
7189 PrintF("%d", Smi::cast(obj)->value());
7190 } else if (obj->IsString() || obj->IsSymbol()) {
7191 PrintString(String::cast(obj));
7192 } else if (obj->IsNumber()) {
7193 PrintF("%g", obj->Number());
7194 } else if (obj->IsFailure()) {
7195 PrintF("<failure>");
7196 } else if (obj->IsUndefined()) {
7197 PrintF("<undefined>");
7198 } else if (obj->IsNull()) {
7199 PrintF("<null>");
7200 } else if (obj->IsTrue()) {
7201 PrintF("<true>");
7202 } else if (obj->IsFalse()) {
7203 PrintF("<false>");
7204 } else {
7205 PrintF("%p", obj);
7206 }
7207}
7208
7209
7210static int StackSize() {
7211 int n = 0;
7212 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
7213 return n;
7214}
7215
7216
7217static void PrintTransition(Object* result) {
7218 // indentation
7219 { const int nmax = 80;
7220 int n = StackSize();
7221 if (n <= nmax)
7222 PrintF("%4d:%*s", n, n, "");
7223 else
7224 PrintF("%4d:%*s", n, nmax, "...");
7225 }
7226
7227 if (result == NULL) {
7228 // constructor calls
7229 JavaScriptFrameIterator it;
7230 JavaScriptFrame* frame = it.frame();
7231 if (frame->IsConstructor()) PrintF("new ");
7232 // function name
7233 Object* fun = frame->function();
7234 if (fun->IsJSFunction()) {
7235 PrintObject(JSFunction::cast(fun)->shared()->name());
7236 } else {
7237 PrintObject(fun);
7238 }
7239 // function arguments
7240 // (we are intentionally only printing the actually
7241 // supplied parameters, not all parameters required)
7242 PrintF("(this=");
7243 PrintObject(frame->receiver());
7244 const int length = frame->GetProvidedParametersCount();
7245 for (int i = 0; i < length; i++) {
7246 PrintF(", ");
7247 PrintObject(frame->GetParameter(i));
7248 }
7249 PrintF(") {\n");
7250
7251 } else {
7252 // function result
7253 PrintF("} -> ");
7254 PrintObject(result);
7255 PrintF("\n");
7256 }
7257}
7258
7259
7260static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007261 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007262 NoHandleAllocation ha;
7263 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007264 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007265}
7266
7267
7268static Object* Runtime_TraceExit(Arguments args) {
7269 NoHandleAllocation ha;
7270 PrintTransition(args[0]);
7271 return args[0]; // return TOS
7272}
7273
7274
7275static Object* Runtime_DebugPrint(Arguments args) {
7276 NoHandleAllocation ha;
7277 ASSERT(args.length() == 1);
7278
7279#ifdef DEBUG
7280 if (args[0]->IsString()) {
7281 // If we have a string, assume it's a code "marker"
7282 // and print some interesting cpu debugging info.
7283 JavaScriptFrameIterator it;
7284 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007285 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
7286 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007287 } else {
7288 PrintF("DebugPrint: ");
7289 }
7290 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007291 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007292 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007293 HeapObject::cast(args[0])->map()->Print();
7294 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007295#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007296 // ShortPrint is available in release mode. Print is not.
7297 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007298#endif
7299 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00007300 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007301
7302 return args[0]; // return TOS
7303}
7304
7305
7306static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007307 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007308 NoHandleAllocation ha;
7309 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007310 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007311}
7312
7313
mads.s.ager31e71382008-08-13 09:32:07 +00007314static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007315 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007316 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007317
7318 // According to ECMA-262, section 15.9.1, page 117, the precision of
7319 // the number in a Date object representing a particular instant in
7320 // time is milliseconds. Therefore, we floor the result of getting
7321 // the OS time.
7322 double millis = floor(OS::TimeCurrentMillis());
7323 return Heap::NumberFromDouble(millis);
7324}
7325
7326
7327static Object* Runtime_DateParseString(Arguments args) {
7328 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007329 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007330
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007331 CONVERT_ARG_CHECKED(String, str, 0);
7332 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007333
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007334 CONVERT_ARG_CHECKED(JSArray, output, 1);
7335 RUNTIME_ASSERT(output->HasFastElements());
7336
7337 AssertNoAllocation no_allocation;
7338
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007339 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007340 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
7341 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007342 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007343 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007344 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007345 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007346 result = DateParser::Parse(str->ToUC16Vector(), output_array);
7347 }
7348
7349 if (result) {
7350 return *output;
7351 } else {
7352 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007353 }
7354}
7355
7356
7357static Object* Runtime_DateLocalTimezone(Arguments args) {
7358 NoHandleAllocation ha;
7359 ASSERT(args.length() == 1);
7360
7361 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007362 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007363 return Heap::AllocateStringFromUtf8(CStrVector(zone));
7364}
7365
7366
7367static Object* Runtime_DateLocalTimeOffset(Arguments args) {
7368 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007369 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007370
7371 return Heap::NumberFromDouble(OS::LocalTimeOffset());
7372}
7373
7374
7375static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
7376 NoHandleAllocation ha;
7377 ASSERT(args.length() == 1);
7378
7379 CONVERT_DOUBLE_CHECKED(x, args[0]);
7380 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
7381}
7382
7383
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007384static Object* Runtime_GlobalReceiver(Arguments args) {
7385 ASSERT(args.length() == 1);
7386 Object* global = args[0];
7387 if (!global->IsJSGlobalObject()) return Heap::null_value();
7388 return JSGlobalObject::cast(global)->global_receiver();
7389}
7390
7391
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007392static Object* Runtime_CompileString(Arguments args) {
7393 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007394 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007395 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007396 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007397
ager@chromium.org381abbb2009-02-25 13:23:22 +00007398 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007399 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007400 Compiler::ValidationState validate = (is_json->IsTrue())
7401 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007402 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
7403 context,
7404 true,
7405 validate);
7406 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007407 Handle<JSFunction> fun =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007408 Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007409 return *fun;
7410}
7411
7412
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007413static ObjectPair CompileGlobalEval(Handle<String> source,
7414 Handle<Object> receiver) {
7415 // Deal with a normal eval call with a string argument. Compile it
7416 // and return the compiled function bound in the local context.
7417 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
7418 source,
7419 Handle<Context>(Top::context()),
7420 Top::context()->IsGlobalContext(),
7421 Compiler::DONT_VALIDATE_JSON);
7422 if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
7423 Handle<JSFunction> compiled = Factory::NewFunctionFromSharedFunctionInfo(
7424 shared,
7425 Handle<Context>(Top::context()),
7426 NOT_TENURED);
7427 return MakePair(*compiled, *receiver);
7428}
7429
7430
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007431static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
7432 ASSERT(args.length() == 3);
7433 if (!args[0]->IsJSFunction()) {
7434 return MakePair(Top::ThrowIllegalOperation(), NULL);
7435 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007436
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007437 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007438 Handle<JSFunction> callee = args.at<JSFunction>(0);
7439 Handle<Object> receiver; // Will be overwritten.
7440
7441 // Compute the calling context.
7442 Handle<Context> context = Handle<Context>(Top::context());
7443#ifdef DEBUG
7444 // Make sure Top::context() agrees with the old code that traversed
7445 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007446 StackFrameLocator locator;
7447 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007448 ASSERT(Context::cast(frame->context()) == *context);
7449#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007450
7451 // Find where the 'eval' symbol is bound. It is unaliased only if
7452 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007453 int index = -1;
7454 PropertyAttributes attributes = ABSENT;
7455 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007456 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
7457 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007458 // Stop search when eval is found or when the global context is
7459 // reached.
7460 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007461 if (context->is_function_context()) {
7462 context = Handle<Context>(Context::cast(context->closure()->context()));
7463 } else {
7464 context = Handle<Context>(context->previous());
7465 }
7466 }
7467
iposva@chromium.org245aa852009-02-10 00:49:54 +00007468 // If eval could not be resolved, it has been deleted and we need to
7469 // throw a reference error.
7470 if (attributes == ABSENT) {
7471 Handle<Object> name = Factory::eval_symbol();
7472 Handle<Object> reference_error =
7473 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007474 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007475 }
7476
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007477 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007478 // 'eval' is not bound in the global context. Just call the function
7479 // with the given arguments. This is not necessarily the global eval.
7480 if (receiver->IsContext()) {
7481 context = Handle<Context>::cast(receiver);
7482 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007483 } else if (receiver->IsJSContextExtensionObject()) {
7484 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007485 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007486 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007487 }
7488
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007489 // 'eval' is bound in the global context, but it may have been overwritten.
7490 // Compare it to the builtin 'GlobalEval' function to make sure.
7491 if (*callee != Top::global_context()->global_eval_fun() ||
7492 !args[1]->IsString()) {
7493 return MakePair(*callee, Top::context()->global()->global_receiver());
7494 }
7495
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007496 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
7497}
7498
7499
7500static ObjectPair Runtime_ResolvePossiblyDirectEvalNoLookup(Arguments args) {
7501 ASSERT(args.length() == 3);
7502 if (!args[0]->IsJSFunction()) {
7503 return MakePair(Top::ThrowIllegalOperation(), NULL);
7504 }
7505
7506 HandleScope scope;
7507 Handle<JSFunction> callee = args.at<JSFunction>(0);
7508
7509 // 'eval' is bound in the global context, but it may have been overwritten.
7510 // Compare it to the builtin 'GlobalEval' function to make sure.
7511 if (*callee != Top::global_context()->global_eval_fun() ||
7512 !args[1]->IsString()) {
7513 return MakePair(*callee, Top::context()->global()->global_receiver());
7514 }
7515
7516 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007517}
7518
7519
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007520static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
7521 // This utility adjusts the property attributes for newly created Function
7522 // object ("new Function(...)") by changing the map.
7523 // All it does is changing the prototype property to enumerable
7524 // as specified in ECMA262, 15.3.5.2.
7525 HandleScope scope;
7526 ASSERT(args.length() == 1);
7527 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7528 ASSERT(func->map()->instance_type() ==
7529 Top::function_instance_map()->instance_type());
7530 ASSERT(func->map()->instance_size() ==
7531 Top::function_instance_map()->instance_size());
7532 func->set_map(*Top::function_instance_map());
7533 return *func;
7534}
7535
7536
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007537// Push an array unto an array of arrays if it is not already in the
7538// array. Returns true if the element was pushed on the stack and
7539// false otherwise.
7540static Object* Runtime_PushIfAbsent(Arguments args) {
7541 ASSERT(args.length() == 2);
7542 CONVERT_CHECKED(JSArray, array, args[0]);
7543 CONVERT_CHECKED(JSArray, element, args[1]);
7544 RUNTIME_ASSERT(array->HasFastElements());
7545 int length = Smi::cast(array->length())->value();
7546 FixedArray* elements = FixedArray::cast(array->elements());
7547 for (int i = 0; i < length; i++) {
7548 if (elements->get(i) == element) return Heap::false_value();
7549 }
7550 Object* obj = array->SetFastElement(length, element);
7551 if (obj->IsFailure()) return obj;
7552 return Heap::true_value();
7553}
7554
7555
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007556/**
7557 * A simple visitor visits every element of Array's.
7558 * The backend storage can be a fixed array for fast elements case,
7559 * or a dictionary for sparse array. Since Dictionary is a subtype
7560 * of FixedArray, the class can be used by both fast and slow cases.
7561 * The second parameter of the constructor, fast_elements, specifies
7562 * whether the storage is a FixedArray or Dictionary.
7563 *
7564 * An index limit is used to deal with the situation that a result array
7565 * length overflows 32-bit non-negative integer.
7566 */
7567class ArrayConcatVisitor {
7568 public:
7569 ArrayConcatVisitor(Handle<FixedArray> storage,
7570 uint32_t index_limit,
7571 bool fast_elements) :
7572 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007573 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007574
7575 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007576 if (i >= index_limit_ - index_offset_) return;
7577 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007578
7579 if (fast_elements_) {
7580 ASSERT(index < static_cast<uint32_t>(storage_->length()));
7581 storage_->set(index, *elm);
7582
7583 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007584 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
7585 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007586 Factory::DictionaryAtNumberPut(dict, index, elm);
7587 if (!result.is_identical_to(dict))
7588 storage_ = result;
7589 }
7590 }
7591
7592 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007593 if (index_limit_ - index_offset_ < delta) {
7594 index_offset_ = index_limit_;
7595 } else {
7596 index_offset_ += delta;
7597 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007598 }
7599
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007600 Handle<FixedArray> storage() { return storage_; }
7601
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007602 private:
7603 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007604 // Limit on the accepted indices. Elements with indices larger than the
7605 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007606 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007607 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007608 uint32_t index_offset_;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007609 const bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007610};
7611
7612
ager@chromium.org3811b432009-10-28 14:53:37 +00007613template<class ExternalArrayClass, class ElementType>
7614static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
7615 bool elements_are_ints,
7616 bool elements_are_guaranteed_smis,
7617 uint32_t range,
7618 ArrayConcatVisitor* visitor) {
7619 Handle<ExternalArrayClass> array(
7620 ExternalArrayClass::cast(receiver->elements()));
7621 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
7622
7623 if (visitor != NULL) {
7624 if (elements_are_ints) {
7625 if (elements_are_guaranteed_smis) {
7626 for (uint32_t j = 0; j < len; j++) {
7627 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
7628 visitor->visit(j, e);
7629 }
7630 } else {
7631 for (uint32_t j = 0; j < len; j++) {
7632 int64_t val = static_cast<int64_t>(array->get(j));
7633 if (Smi::IsValid(static_cast<intptr_t>(val))) {
7634 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
7635 visitor->visit(j, e);
7636 } else {
7637 Handle<Object> e(
7638 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
7639 visitor->visit(j, e);
7640 }
7641 }
7642 }
7643 } else {
7644 for (uint32_t j = 0; j < len; j++) {
7645 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
7646 visitor->visit(j, e);
7647 }
7648 }
7649 }
7650
7651 return len;
7652}
7653
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007654/**
7655 * A helper function that visits elements of a JSObject. Only elements
7656 * whose index between 0 and range (exclusive) are visited.
7657 *
7658 * If the third parameter, visitor, is not NULL, the visitor is called
7659 * with parameters, 'visitor_index_offset + element index' and the element.
7660 *
7661 * It returns the number of visisted elements.
7662 */
7663static uint32_t IterateElements(Handle<JSObject> receiver,
7664 uint32_t range,
7665 ArrayConcatVisitor* visitor) {
7666 uint32_t num_of_elements = 0;
7667
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007668 switch (receiver->GetElementsKind()) {
7669 case JSObject::FAST_ELEMENTS: {
7670 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
7671 uint32_t len = elements->length();
7672 if (range < len) {
7673 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007674 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007675
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007676 for (uint32_t j = 0; j < len; j++) {
7677 Handle<Object> e(elements->get(j));
7678 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007679 num_of_elements++;
7680 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007681 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007682 }
7683 }
7684 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007685 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007686 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007687 case JSObject::PIXEL_ELEMENTS: {
7688 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
7689 uint32_t len = pixels->length();
7690 if (range < len) {
7691 len = range;
7692 }
7693
7694 for (uint32_t j = 0; j < len; j++) {
7695 num_of_elements++;
7696 if (visitor != NULL) {
7697 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
7698 visitor->visit(j, e);
7699 }
7700 }
7701 break;
7702 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007703 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
7704 num_of_elements =
7705 IterateExternalArrayElements<ExternalByteArray, int8_t>(
7706 receiver, true, true, range, visitor);
7707 break;
7708 }
7709 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7710 num_of_elements =
7711 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
7712 receiver, true, true, range, visitor);
7713 break;
7714 }
7715 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
7716 num_of_elements =
7717 IterateExternalArrayElements<ExternalShortArray, int16_t>(
7718 receiver, true, true, range, visitor);
7719 break;
7720 }
7721 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7722 num_of_elements =
7723 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
7724 receiver, true, true, range, visitor);
7725 break;
7726 }
7727 case JSObject::EXTERNAL_INT_ELEMENTS: {
7728 num_of_elements =
7729 IterateExternalArrayElements<ExternalIntArray, int32_t>(
7730 receiver, true, false, range, visitor);
7731 break;
7732 }
7733 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7734 num_of_elements =
7735 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
7736 receiver, true, false, range, visitor);
7737 break;
7738 }
7739 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
7740 num_of_elements =
7741 IterateExternalArrayElements<ExternalFloatArray, float>(
7742 receiver, false, false, range, visitor);
7743 break;
7744 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007745 case JSObject::DICTIONARY_ELEMENTS: {
7746 Handle<NumberDictionary> dict(receiver->element_dictionary());
7747 uint32_t capacity = dict->Capacity();
7748 for (uint32_t j = 0; j < capacity; j++) {
7749 Handle<Object> k(dict->KeyAt(j));
7750 if (dict->IsKey(*k)) {
7751 ASSERT(k->IsNumber());
7752 uint32_t index = static_cast<uint32_t>(k->Number());
7753 if (index < range) {
7754 num_of_elements++;
7755 if (visitor) {
7756 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
7757 }
7758 }
7759 }
7760 }
7761 break;
7762 }
7763 default:
7764 UNREACHABLE();
7765 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007766 }
7767
7768 return num_of_elements;
7769}
7770
7771
7772/**
7773 * A helper function that visits elements of an Array object, and elements
7774 * on its prototypes.
7775 *
7776 * Elements on prototypes are visited first, and only elements whose indices
7777 * less than Array length are visited.
7778 *
7779 * If a ArrayConcatVisitor object is given, the visitor is called with
7780 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007781 *
7782 * The returned number of elements is an upper bound on the actual number
7783 * of elements added. If the same element occurs in more than one object
7784 * in the array's prototype chain, it will be counted more than once, but
7785 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007786 */
7787static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
7788 ArrayConcatVisitor* visitor) {
7789 uint32_t range = static_cast<uint32_t>(array->length()->Number());
7790 Handle<Object> obj = array;
7791
7792 static const int kEstimatedPrototypes = 3;
7793 List< Handle<JSObject> > objects(kEstimatedPrototypes);
7794
7795 // Visit prototype first. If an element on the prototype is shadowed by
7796 // the inheritor using the same index, the ArrayConcatVisitor visits
7797 // the prototype element before the shadowing element.
7798 // The visitor can simply overwrite the old value by new value using
7799 // the same index. This follows Array::concat semantics.
7800 while (!obj->IsNull()) {
7801 objects.Add(Handle<JSObject>::cast(obj));
7802 obj = Handle<Object>(obj->GetPrototype());
7803 }
7804
7805 uint32_t nof_elements = 0;
7806 for (int i = objects.length() - 1; i >= 0; i--) {
7807 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007808 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007809 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007810
7811 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
7812 nof_elements = JSObject::kMaxElementCount;
7813 } else {
7814 nof_elements += encountered_elements;
7815 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007816 }
7817
7818 return nof_elements;
7819}
7820
7821
7822/**
7823 * A helper function of Runtime_ArrayConcat.
7824 *
7825 * The first argument is an Array of arrays and objects. It is the
7826 * same as the arguments array of Array::concat JS function.
7827 *
7828 * If an argument is an Array object, the function visits array
7829 * elements. If an argument is not an Array object, the function
7830 * visits the object as if it is an one-element array.
7831 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007832 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007833 * non-negative number is used as new length. For example, if one
7834 * array length is 2^32 - 1, second array length is 1, the
7835 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007836 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
7837 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007838 */
7839static uint32_t IterateArguments(Handle<JSArray> arguments,
7840 ArrayConcatVisitor* visitor) {
7841 uint32_t visited_elements = 0;
7842 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7843
7844 for (uint32_t i = 0; i < num_of_args; i++) {
7845 Handle<Object> obj(arguments->GetElement(i));
7846 if (obj->IsJSArray()) {
7847 Handle<JSArray> array = Handle<JSArray>::cast(obj);
7848 uint32_t len = static_cast<uint32_t>(array->length()->Number());
7849 uint32_t nof_elements =
7850 IterateArrayAndPrototypeElements(array, visitor);
7851 // Total elements of array and its prototype chain can be more than
7852 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007853 // the array length number of elements. We use the length as an estimate
7854 // for the actual number of elements added.
7855 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
7856 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
7857 visited_elements = JSArray::kMaxElementCount;
7858 } else {
7859 visited_elements += added_elements;
7860 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007861 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007862 } else {
7863 if (visitor) {
7864 visitor->visit(0, obj);
7865 visitor->increase_index_offset(1);
7866 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007867 if (visited_elements < JSArray::kMaxElementCount) {
7868 visited_elements++;
7869 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007870 }
7871 }
7872 return visited_elements;
7873}
7874
7875
7876/**
7877 * Array::concat implementation.
7878 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007879 * TODO(lrn): Fix non-compliance for very large concatenations and update to
7880 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007881 */
7882static Object* Runtime_ArrayConcat(Arguments args) {
7883 ASSERT(args.length() == 1);
7884 HandleScope handle_scope;
7885
7886 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
7887 Handle<JSArray> arguments(arg_arrays);
7888
7889 // Pass 1: estimate the number of elements of the result
7890 // (it could be more than real numbers if prototype has elements).
7891 uint32_t result_length = 0;
7892 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7893
7894 { AssertNoAllocation nogc;
7895 for (uint32_t i = 0; i < num_of_args; i++) {
7896 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007897 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007898 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007899 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007900 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
7901 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007902 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007903 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007904 if (JSObject::kMaxElementCount - result_length < length_estimate) {
7905 result_length = JSObject::kMaxElementCount;
7906 break;
7907 }
7908 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007909 }
7910 }
7911
7912 // Allocate an empty array, will set length and content later.
7913 Handle<JSArray> result = Factory::NewJSArray(0);
7914
7915 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
7916 // If estimated number of elements is more than half of length, a
7917 // fixed array (fast case) is more time and space-efficient than a
7918 // dictionary.
7919 bool fast_case = (estimate_nof_elements * 2) >= result_length;
7920
7921 Handle<FixedArray> storage;
7922 if (fast_case) {
7923 // The backing storage array must have non-existing elements to
7924 // preserve holes across concat operations.
7925 storage = Factory::NewFixedArrayWithHoles(result_length);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007926 result->set_map(*Factory::GetFastElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007927 } else {
7928 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
7929 uint32_t at_least_space_for = estimate_nof_elements +
7930 (estimate_nof_elements >> 2);
7931 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007932 Factory::NewNumberDictionary(at_least_space_for));
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007933 result->set_map(*Factory::GetSlowElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007934 }
7935
7936 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
7937
7938 ArrayConcatVisitor visitor(storage, result_length, fast_case);
7939
7940 IterateArguments(arguments, &visitor);
7941
7942 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007943 // Please note the storage might have changed in the visitor.
7944 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007945
7946 return *result;
7947}
7948
7949
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007950// This will not allocate (flatten the string), but it may run
7951// very slowly for very deeply nested ConsStrings. For debugging use only.
7952static Object* Runtime_GlobalPrint(Arguments args) {
7953 NoHandleAllocation ha;
7954 ASSERT(args.length() == 1);
7955
7956 CONVERT_CHECKED(String, string, args[0]);
7957 StringInputBuffer buffer(string);
7958 while (buffer.has_more()) {
7959 uint16_t character = buffer.GetNext();
7960 PrintF("%c", character);
7961 }
7962 return string;
7963}
7964
ager@chromium.org5ec48922009-05-05 07:25:34 +00007965// Moves all own elements of an object, that are below a limit, to positions
7966// starting at zero. All undefined values are placed after non-undefined values,
7967// and are followed by non-existing element. Does not change the length
7968// property.
7969// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007970static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007971 ASSERT(args.length() == 2);
7972 CONVERT_CHECKED(JSObject, object, args[0]);
7973 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
7974 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007975}
7976
7977
7978// Move contents of argument 0 (an array) to argument 1 (an array)
7979static Object* Runtime_MoveArrayContents(Arguments args) {
7980 ASSERT(args.length() == 2);
7981 CONVERT_CHECKED(JSArray, from, args[0]);
7982 CONVERT_CHECKED(JSArray, to, args[1]);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007983 HeapObject* new_elements = from->elements();
7984 Object* new_map;
7985 if (new_elements->map() == Heap::fixed_array_map()) {
7986 new_map = to->map()->GetFastElementsMap();
7987 } else {
7988 new_map = to->map()->GetSlowElementsMap();
7989 }
7990 if (new_map->IsFailure()) return new_map;
7991 to->set_map(Map::cast(new_map));
7992 to->set_elements(new_elements);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007993 to->set_length(from->length());
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007994 Object* obj = from->ResetElements();
7995 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007996 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007997 return to;
7998}
7999
8000
8001// How many elements does this array have?
8002static Object* Runtime_EstimateNumberOfElements(Arguments args) {
8003 ASSERT(args.length() == 1);
8004 CONVERT_CHECKED(JSArray, array, args[0]);
8005 HeapObject* elements = array->elements();
8006 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008007 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008008 } else {
8009 return array->length();
8010 }
8011}
8012
8013
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008014static Object* Runtime_SwapElements(Arguments args) {
8015 HandleScope handle_scope;
8016
8017 ASSERT_EQ(3, args.length());
8018
ager@chromium.orgac091b72010-05-05 07:34:42 +00008019 CONVERT_ARG_CHECKED(JSObject, object, 0);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008020 Handle<Object> key1 = args.at<Object>(1);
8021 Handle<Object> key2 = args.at<Object>(2);
8022
8023 uint32_t index1, index2;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008024 if (!key1->ToArrayIndex(&index1)
8025 || !key2->ToArrayIndex(&index2)) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00008026 return Top::ThrowIllegalOperation();
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008027 }
8028
ager@chromium.orgac091b72010-05-05 07:34:42 +00008029 Handle<JSObject> jsobject = Handle<JSObject>::cast(object);
8030 Handle<Object> tmp1 = GetElement(jsobject, index1);
8031 Handle<Object> tmp2 = GetElement(jsobject, index2);
8032
8033 SetElement(jsobject, index1, tmp2);
8034 SetElement(jsobject, index2, tmp1);
8035
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008036 return Heap::undefined_value();
8037}
8038
8039
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008040// Returns an array that tells you where in the [0, length) interval an array
8041// might have elements. Can either return keys or intervals. Keys can have
8042// gaps in (undefined). Intervals can also span over some undefined keys.
8043static Object* Runtime_GetArrayKeys(Arguments args) {
8044 ASSERT(args.length() == 2);
8045 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00008046 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008047 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008048 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008049 // Create an array and get all the keys into it, then remove all the
8050 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008051 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008052 int keys_length = keys->length();
8053 for (int i = 0; i < keys_length; i++) {
8054 Object* key = keys->get(i);
8055 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008056 if (!key->ToArrayIndex(&index) || index >= length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008057 // Zap invalid keys.
8058 keys->set_undefined(i);
8059 }
8060 }
8061 return *Factory::NewJSArrayWithElements(keys);
8062 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008063 ASSERT(array->HasFastElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008064 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
8065 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008066 single_interval->set(0, Smi::FromInt(-1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008067 uint32_t actual_length =
8068 static_cast<uint32_t>(FixedArray::cast(array->elements())->length());
ager@chromium.org5ec48922009-05-05 07:25:34 +00008069 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008070 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00008071 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008072 single_interval->set(1, *length_object);
8073 return *Factory::NewJSArrayWithElements(single_interval);
8074 }
8075}
8076
8077
8078// DefineAccessor takes an optional final argument which is the
8079// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
8080// to the way accessors are implemented, it is set for both the getter
8081// and setter on the first call to DefineAccessor and ignored on
8082// subsequent calls.
8083static Object* Runtime_DefineAccessor(Arguments args) {
8084 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
8085 // Compute attributes.
8086 PropertyAttributes attributes = NONE;
8087 if (args.length() == 5) {
8088 CONVERT_CHECKED(Smi, attrs, args[4]);
8089 int value = attrs->value();
8090 // Only attribute bits should be set.
8091 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
8092 attributes = static_cast<PropertyAttributes>(value);
8093 }
8094
8095 CONVERT_CHECKED(JSObject, obj, args[0]);
8096 CONVERT_CHECKED(String, name, args[1]);
8097 CONVERT_CHECKED(Smi, flag, args[2]);
8098 CONVERT_CHECKED(JSFunction, fun, args[3]);
8099 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
8100}
8101
8102
8103static Object* Runtime_LookupAccessor(Arguments args) {
8104 ASSERT(args.length() == 3);
8105 CONVERT_CHECKED(JSObject, obj, args[0]);
8106 CONVERT_CHECKED(String, name, args[1]);
8107 CONVERT_CHECKED(Smi, flag, args[2]);
8108 return obj->LookupAccessor(name, flag->value() == 0);
8109}
8110
8111
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008112#ifdef ENABLE_DEBUGGER_SUPPORT
8113static Object* Runtime_DebugBreak(Arguments args) {
8114 ASSERT(args.length() == 0);
8115 return Execution::DebugBreakHelper();
8116}
8117
8118
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008119// Helper functions for wrapping and unwrapping stack frame ids.
8120static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008121 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008122 return Smi::FromInt(id >> 2);
8123}
8124
8125
8126static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
8127 return static_cast<StackFrame::Id>(wrapped->value() << 2);
8128}
8129
8130
8131// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00008132// args[0]: debug event listener function to set or null or undefined for
8133// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008134// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00008135static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008136 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00008137 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
8138 args[0]->IsUndefined() ||
8139 args[0]->IsNull());
8140 Handle<Object> callback = args.at<Object>(0);
8141 Handle<Object> data = args.at<Object>(1);
8142 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008143
8144 return Heap::undefined_value();
8145}
8146
8147
8148static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00008149 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008150 StackGuard::DebugBreak();
8151 return Heap::undefined_value();
8152}
8153
8154
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008155static Object* DebugLookupResultValue(Object* receiver, String* name,
8156 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00008157 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008158 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008159 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008160 case NORMAL:
8161 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008162 if (value->IsTheHole()) {
8163 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008164 }
8165 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008166 case FIELD:
8167 value =
8168 JSObject::cast(
8169 result->holder())->FastPropertyAt(result->GetFieldIndex());
8170 if (value->IsTheHole()) {
8171 return Heap::undefined_value();
8172 }
8173 return value;
8174 case CONSTANT_FUNCTION:
8175 return result->GetConstantFunction();
8176 case CALLBACKS: {
8177 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008178 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008179 value = receiver->GetPropertyWithCallback(
8180 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00008181 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008182 value = Top::pending_exception();
8183 Top::clear_pending_exception();
8184 if (caught_exception != NULL) {
8185 *caught_exception = true;
8186 }
8187 }
8188 return value;
8189 } else {
8190 return Heap::undefined_value();
8191 }
8192 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008193 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008194 case MAP_TRANSITION:
8195 case CONSTANT_TRANSITION:
8196 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008197 return Heap::undefined_value();
8198 default:
8199 UNREACHABLE();
8200 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008201 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008202 return Heap::undefined_value();
8203}
8204
8205
ager@chromium.org32912102009-01-16 10:38:43 +00008206// Get debugger related details for an object property.
8207// args[0]: object holding property
8208// args[1]: name of the property
8209//
8210// The array returned contains the following information:
8211// 0: Property value
8212// 1: Property details
8213// 2: Property value is exception
8214// 3: Getter function if defined
8215// 4: Setter function if defined
8216// Items 2-4 are only filled if the property has either a getter or a setter
8217// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00008218static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008219 HandleScope scope;
8220
8221 ASSERT(args.length() == 2);
8222
8223 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8224 CONVERT_ARG_CHECKED(String, name, 1);
8225
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008226 // Make sure to set the current context to the context before the debugger was
8227 // entered (if the debugger is entered). The reason for switching context here
8228 // is that for some property lookups (accessors and interceptors) callbacks
8229 // into the embedding application can occour, and the embedding application
8230 // could have the assumption that its own global context is the current
8231 // context and not some internal debugger context.
8232 SaveContext save;
8233 if (Debug::InDebugger()) {
8234 Top::set_context(*Debug::debugger_entry()->GetContext());
8235 }
8236
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008237 // Skip the global proxy as it has no properties and always delegates to the
8238 // real global object.
8239 if (obj->IsJSGlobalProxy()) {
8240 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
8241 }
8242
8243
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008244 // Check if the name is trivially convertible to an index and get the element
8245 // if so.
8246 uint32_t index;
8247 if (name->AsArrayIndex(&index)) {
8248 Handle<FixedArray> details = Factory::NewFixedArray(2);
8249 details->set(0, Runtime::GetElementOrCharAt(obj, index));
8250 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
8251 return *Factory::NewJSArrayWithElements(details);
8252 }
8253
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008254 // Find the number of objects making up this.
8255 int length = LocalPrototypeChainLength(*obj);
8256
8257 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008258 Handle<JSObject> jsproto = obj;
8259 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008260 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008261 jsproto->LocalLookup(*name, &result);
8262 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008263 // LookupResult is not GC safe as it holds raw object pointers.
8264 // GC can happen later in this code so put the required fields into
8265 // local variables using handles when required for later use.
8266 PropertyType result_type = result.type();
8267 Handle<Object> result_callback_obj;
8268 if (result_type == CALLBACKS) {
8269 result_callback_obj = Handle<Object>(result.GetCallbackObject());
8270 }
8271 Smi* property_details = result.GetPropertyDetails().AsSmi();
8272 // DebugLookupResultValue can cause GC so details from LookupResult needs
8273 // to be copied to handles before this.
8274 bool caught_exception = false;
8275 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
8276 &caught_exception);
8277 if (raw_value->IsFailure()) return raw_value;
8278 Handle<Object> value(raw_value);
8279
8280 // If the callback object is a fixed array then it contains JavaScript
8281 // getter and/or setter.
8282 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
8283 result_callback_obj->IsFixedArray();
8284 Handle<FixedArray> details =
8285 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
8286 details->set(0, *value);
8287 details->set(1, property_details);
8288 if (hasJavaScriptAccessors) {
8289 details->set(2,
8290 caught_exception ? Heap::true_value()
8291 : Heap::false_value());
8292 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
8293 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
8294 }
8295
8296 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008297 }
8298 if (i < length - 1) {
8299 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
8300 }
8301 }
8302
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008303 return Heap::undefined_value();
8304}
8305
8306
8307static Object* Runtime_DebugGetProperty(Arguments args) {
8308 HandleScope scope;
8309
8310 ASSERT(args.length() == 2);
8311
8312 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8313 CONVERT_ARG_CHECKED(String, name, 1);
8314
8315 LookupResult result;
8316 obj->Lookup(*name, &result);
8317 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008318 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008319 }
8320 return Heap::undefined_value();
8321}
8322
8323
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008324// Return the property type calculated from the property details.
8325// args[0]: smi with property details.
8326static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
8327 ASSERT(args.length() == 1);
8328 CONVERT_CHECKED(Smi, details, args[0]);
8329 PropertyType type = PropertyDetails(details).type();
8330 return Smi::FromInt(static_cast<int>(type));
8331}
8332
8333
8334// Return the property attribute calculated from the property details.
8335// args[0]: smi with property details.
8336static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
8337 ASSERT(args.length() == 1);
8338 CONVERT_CHECKED(Smi, details, args[0]);
8339 PropertyAttributes attributes = PropertyDetails(details).attributes();
8340 return Smi::FromInt(static_cast<int>(attributes));
8341}
8342
8343
8344// Return the property insertion index calculated from the property details.
8345// args[0]: smi with property details.
8346static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
8347 ASSERT(args.length() == 1);
8348 CONVERT_CHECKED(Smi, details, args[0]);
8349 int index = PropertyDetails(details).index();
8350 return Smi::FromInt(index);
8351}
8352
8353
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008354// Return property value from named interceptor.
8355// args[0]: object
8356// args[1]: property name
8357static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
8358 HandleScope scope;
8359 ASSERT(args.length() == 2);
8360 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8361 RUNTIME_ASSERT(obj->HasNamedInterceptor());
8362 CONVERT_ARG_CHECKED(String, name, 1);
8363
8364 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008365 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008366}
8367
8368
8369// Return element value from indexed interceptor.
8370// args[0]: object
8371// args[1]: index
8372static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
8373 HandleScope scope;
8374 ASSERT(args.length() == 2);
8375 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8376 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
8377 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
8378
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008379 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008380}
8381
8382
8383static Object* Runtime_CheckExecutionState(Arguments args) {
8384 ASSERT(args.length() >= 1);
8385 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00008386 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008387 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008388 return Top::Throw(Heap::illegal_execution_state_symbol());
8389 }
8390
8391 return Heap::true_value();
8392}
8393
8394
8395static Object* Runtime_GetFrameCount(Arguments args) {
8396 HandleScope scope;
8397 ASSERT(args.length() == 1);
8398
8399 // Check arguments.
8400 Object* result = Runtime_CheckExecutionState(args);
8401 if (result->IsFailure()) return result;
8402
8403 // Count all frames which are relevant to debugging stack trace.
8404 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008405 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008406 if (id == StackFrame::NO_ID) {
8407 // If there is no JavaScript stack frame count is 0.
8408 return Smi::FromInt(0);
8409 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008410 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
8411 return Smi::FromInt(n);
8412}
8413
8414
8415static const int kFrameDetailsFrameIdIndex = 0;
8416static const int kFrameDetailsReceiverIndex = 1;
8417static const int kFrameDetailsFunctionIndex = 2;
8418static const int kFrameDetailsArgumentCountIndex = 3;
8419static const int kFrameDetailsLocalCountIndex = 4;
8420static const int kFrameDetailsSourcePositionIndex = 5;
8421static const int kFrameDetailsConstructCallIndex = 6;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008422static const int kFrameDetailsAtReturnIndex = 7;
8423static const int kFrameDetailsDebuggerFrameIndex = 8;
8424static const int kFrameDetailsFirstDynamicIndex = 9;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008425
8426// Return an array with frame details
8427// args[0]: number: break id
8428// args[1]: number: frame index
8429//
8430// The array returned contains the following information:
8431// 0: Frame id
8432// 1: Receiver
8433// 2: Function
8434// 3: Argument count
8435// 4: Local count
8436// 5: Source position
8437// 6: Constructor call
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008438// 7: Is at return
8439// 8: Debugger frame
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008440// Arguments name, value
8441// Locals name, value
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008442// Return value if any
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008443static Object* Runtime_GetFrameDetails(Arguments args) {
8444 HandleScope scope;
8445 ASSERT(args.length() == 2);
8446
8447 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008448 Object* check = Runtime_CheckExecutionState(args);
8449 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008450 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8451
8452 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008453 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008454 if (id == StackFrame::NO_ID) {
8455 // If there are no JavaScript stack frames return undefined.
8456 return Heap::undefined_value();
8457 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008458 int count = 0;
8459 JavaScriptFrameIterator it(id);
8460 for (; !it.done(); it.Advance()) {
8461 if (count == index) break;
8462 count++;
8463 }
8464 if (it.done()) return Heap::undefined_value();
8465
8466 // Traverse the saved contexts chain to find the active context for the
8467 // selected frame.
8468 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008469 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008470 save = save->prev();
8471 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008472 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008473
8474 // Get the frame id.
8475 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
8476
8477 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008478 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008479
8480 // Check for constructor frame.
8481 bool constructor = it.frame()->IsConstructor();
8482
8483 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008484 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008485 ScopeInfo<> info(*code);
8486
8487 // Get the context.
8488 Handle<Context> context(Context::cast(it.frame()->context()));
8489
8490 // Get the locals names and values into a temporary array.
8491 //
8492 // TODO(1240907): Hide compiler-introduced stack variables
8493 // (e.g. .result)? For users of the debugger, they will probably be
8494 // confusing.
8495 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
8496 for (int i = 0; i < info.NumberOfLocals(); i++) {
8497 // Name of the local.
8498 locals->set(i * 2, *info.LocalName(i));
8499
8500 // Fetch the value of the local - either from the stack or from a
8501 // heap-allocated context.
8502 if (i < info.number_of_stack_slots()) {
8503 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
8504 } else {
8505 Handle<String> name = info.LocalName(i);
8506 // Traverse the context chain to the function context as all local
8507 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008508 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008509 context = Handle<Context>(context->previous());
8510 }
8511 ASSERT(context->is_function_context());
8512 locals->set(i * 2 + 1,
8513 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
8514 NULL)));
8515 }
8516 }
8517
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008518 // Check whether this frame is positioned at return.
8519 int at_return = (index == 0) ? Debug::IsBreakAtReturn(it.frame()) : false;
8520
8521 // If positioned just before return find the value to be returned and add it
8522 // to the frame information.
8523 Handle<Object> return_value = Factory::undefined_value();
8524 if (at_return) {
8525 StackFrameIterator it2;
8526 Address internal_frame_sp = NULL;
8527 while (!it2.done()) {
8528 if (it2.frame()->is_internal()) {
8529 internal_frame_sp = it2.frame()->sp();
8530 } else {
8531 if (it2.frame()->is_java_script()) {
8532 if (it2.frame()->id() == it.frame()->id()) {
8533 // The internal frame just before the JavaScript frame contains the
8534 // value to return on top. A debug break at return will create an
8535 // internal frame to store the return value (eax/rax/r0) before
8536 // entering the debug break exit frame.
8537 if (internal_frame_sp != NULL) {
8538 return_value =
8539 Handle<Object>(Memory::Object_at(internal_frame_sp));
8540 break;
8541 }
8542 }
8543 }
8544
8545 // Indicate that the previous frame was not an internal frame.
8546 internal_frame_sp = NULL;
8547 }
8548 it2.Advance();
8549 }
8550 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008551
8552 // Now advance to the arguments adapter frame (if any). It contains all
8553 // the provided parameters whereas the function frame always have the number
8554 // of arguments matching the functions parameters. The rest of the
8555 // information (except for what is collected above) is the same.
8556 it.AdvanceToArgumentsFrame();
8557
8558 // Find the number of arguments to fill. At least fill the number of
8559 // parameters for the function and fill more if more parameters are provided.
8560 int argument_count = info.number_of_parameters();
8561 if (argument_count < it.frame()->GetProvidedParametersCount()) {
8562 argument_count = it.frame()->GetProvidedParametersCount();
8563 }
8564
8565 // Calculate the size of the result.
8566 int details_size = kFrameDetailsFirstDynamicIndex +
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008567 2 * (argument_count + info.NumberOfLocals()) +
8568 (at_return ? 1 : 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008569 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8570
8571 // Add the frame id.
8572 details->set(kFrameDetailsFrameIdIndex, *frame_id);
8573
8574 // Add the function (same as in function frame).
8575 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
8576
8577 // Add the arguments count.
8578 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
8579
8580 // Add the locals count
8581 details->set(kFrameDetailsLocalCountIndex,
8582 Smi::FromInt(info.NumberOfLocals()));
8583
8584 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00008585 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008586 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
8587 } else {
8588 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
8589 }
8590
8591 // Add the constructor information.
8592 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
8593
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008594 // Add the at return information.
8595 details->set(kFrameDetailsAtReturnIndex, Heap::ToBoolean(at_return));
8596
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008597 // Add information on whether this frame is invoked in the debugger context.
8598 details->set(kFrameDetailsDebuggerFrameIndex,
8599 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
8600
8601 // Fill the dynamic part.
8602 int details_index = kFrameDetailsFirstDynamicIndex;
8603
8604 // Add arguments name and value.
8605 for (int i = 0; i < argument_count; i++) {
8606 // Name of the argument.
8607 if (i < info.number_of_parameters()) {
8608 details->set(details_index++, *info.parameter_name(i));
8609 } else {
8610 details->set(details_index++, Heap::undefined_value());
8611 }
8612
8613 // Parameter value.
8614 if (i < it.frame()->GetProvidedParametersCount()) {
8615 details->set(details_index++, it.frame()->GetParameter(i));
8616 } else {
8617 details->set(details_index++, Heap::undefined_value());
8618 }
8619 }
8620
8621 // Add locals name and value from the temporary copy from the function frame.
8622 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
8623 details->set(details_index++, locals->get(i));
8624 }
8625
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008626 // Add the value being returned.
8627 if (at_return) {
8628 details->set(details_index++, *return_value);
8629 }
8630
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008631 // Add the receiver (same as in function frame).
8632 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
8633 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
8634 Handle<Object> receiver(it.frame()->receiver());
8635 if (!receiver->IsJSObject()) {
8636 // If the receiver is NOT a JSObject we have hit an optimization
8637 // where a value object is not converted into a wrapped JS objects.
8638 // To hide this optimization from the debugger, we wrap the receiver
8639 // by creating correct wrapper object based on the calling frame's
8640 // global context.
8641 it.Advance();
8642 Handle<Context> calling_frames_global_context(
8643 Context::cast(Context::cast(it.frame()->context())->global_context()));
8644 receiver = Factory::ToObject(receiver, calling_frames_global_context);
8645 }
8646 details->set(kFrameDetailsReceiverIndex, *receiver);
8647
8648 ASSERT_EQ(details_size, details_index);
8649 return *Factory::NewJSArrayWithElements(details);
8650}
8651
8652
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008653// Copy all the context locals into an object used to materialize a scope.
8654static void CopyContextLocalsToScopeObject(Handle<Code> code,
8655 ScopeInfo<>& scope_info,
8656 Handle<Context> context,
8657 Handle<JSObject> scope_object) {
8658 // Fill all context locals to the context extension.
8659 for (int i = Context::MIN_CONTEXT_SLOTS;
8660 i < scope_info.number_of_context_slots();
8661 i++) {
8662 int context_index =
8663 ScopeInfo<>::ContextSlotIndex(*code,
8664 *scope_info.context_slot_name(i),
8665 NULL);
8666
8667 // Don't include the arguments shadow (.arguments) context variable.
8668 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
8669 SetProperty(scope_object,
8670 scope_info.context_slot_name(i),
8671 Handle<Object>(context->get(context_index)), NONE);
8672 }
8673 }
8674}
8675
8676
8677// Create a plain JSObject which materializes the local scope for the specified
8678// frame.
8679static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
8680 Handle<JSFunction> function(JSFunction::cast(frame->function()));
8681 Handle<Code> code(function->code());
8682 ScopeInfo<> scope_info(*code);
8683
8684 // Allocate and initialize a JSObject with all the arguments, stack locals
8685 // heap locals and extension properties of the debugged function.
8686 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
8687
8688 // First fill all parameters.
8689 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8690 SetProperty(local_scope,
8691 scope_info.parameter_name(i),
8692 Handle<Object>(frame->GetParameter(i)), NONE);
8693 }
8694
8695 // Second fill all stack locals.
8696 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
8697 SetProperty(local_scope,
8698 scope_info.stack_slot_name(i),
8699 Handle<Object>(frame->GetExpression(i)), NONE);
8700 }
8701
8702 // Third fill all context locals.
8703 Handle<Context> frame_context(Context::cast(frame->context()));
8704 Handle<Context> function_context(frame_context->fcontext());
8705 CopyContextLocalsToScopeObject(code, scope_info,
8706 function_context, local_scope);
8707
8708 // Finally copy any properties from the function context extension. This will
8709 // be variables introduced by eval.
8710 if (function_context->closure() == *function) {
8711 if (function_context->has_extension() &&
8712 !function_context->IsGlobalContext()) {
8713 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008714 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008715 for (int i = 0; i < keys->length(); i++) {
8716 // Names of variables introduced by eval are strings.
8717 ASSERT(keys->get(i)->IsString());
8718 Handle<String> key(String::cast(keys->get(i)));
8719 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
8720 }
8721 }
8722 }
8723 return local_scope;
8724}
8725
8726
8727// Create a plain JSObject which materializes the closure content for the
8728// context.
8729static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
8730 ASSERT(context->is_function_context());
8731
8732 Handle<Code> code(context->closure()->code());
8733 ScopeInfo<> scope_info(*code);
8734
8735 // Allocate and initialize a JSObject with all the content of theis function
8736 // closure.
8737 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
8738
8739 // Check whether the arguments shadow object exists.
8740 int arguments_shadow_index =
8741 ScopeInfo<>::ContextSlotIndex(*code,
8742 Heap::arguments_shadow_symbol(),
8743 NULL);
8744 if (arguments_shadow_index >= 0) {
8745 // In this case all the arguments are available in the arguments shadow
8746 // object.
8747 Handle<JSObject> arguments_shadow(
8748 JSObject::cast(context->get(arguments_shadow_index)));
8749 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8750 SetProperty(closure_scope,
8751 scope_info.parameter_name(i),
8752 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
8753 }
8754 }
8755
8756 // Fill all context locals to the context extension.
8757 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
8758
8759 // Finally copy any properties from the function context extension. This will
8760 // be variables introduced by eval.
8761 if (context->has_extension()) {
8762 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008763 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008764 for (int i = 0; i < keys->length(); i++) {
8765 // Names of variables introduced by eval are strings.
8766 ASSERT(keys->get(i)->IsString());
8767 Handle<String> key(String::cast(keys->get(i)));
8768 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
8769 }
8770 }
8771
8772 return closure_scope;
8773}
8774
8775
8776// Iterate over the actual scopes visible from a stack frame. All scopes are
8777// backed by an actual context except the local scope, which is inserted
8778// "artifically" in the context chain.
8779class ScopeIterator {
8780 public:
8781 enum ScopeType {
8782 ScopeTypeGlobal = 0,
8783 ScopeTypeLocal,
8784 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00008785 ScopeTypeClosure,
8786 // Every catch block contains an implicit with block (its parameter is
8787 // a JSContextExtensionObject) that extends current scope with a variable
8788 // holding exception object. Such with blocks are treated as scopes of their
8789 // own type.
8790 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008791 };
8792
8793 explicit ScopeIterator(JavaScriptFrame* frame)
8794 : frame_(frame),
8795 function_(JSFunction::cast(frame->function())),
8796 context_(Context::cast(frame->context())),
8797 local_done_(false),
8798 at_local_(false) {
8799
8800 // Check whether the first scope is actually a local scope.
8801 if (context_->IsGlobalContext()) {
8802 // If there is a stack slot for .result then this local scope has been
8803 // created for evaluating top level code and it is not a real local scope.
8804 // Checking for the existence of .result seems fragile, but the scope info
8805 // saved with the code object does not otherwise have that information.
8806 Handle<Code> code(function_->code());
8807 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
8808 at_local_ = index < 0;
8809 } else if (context_->is_function_context()) {
8810 at_local_ = true;
8811 }
8812 }
8813
8814 // More scopes?
8815 bool Done() { return context_.is_null(); }
8816
8817 // Move to the next scope.
8818 void Next() {
8819 // If at a local scope mark the local scope as passed.
8820 if (at_local_) {
8821 at_local_ = false;
8822 local_done_ = true;
8823
8824 // If the current context is not associated with the local scope the
8825 // current context is the next real scope, so don't move to the next
8826 // context in this case.
8827 if (context_->closure() != *function_) {
8828 return;
8829 }
8830 }
8831
8832 // The global scope is always the last in the chain.
8833 if (context_->IsGlobalContext()) {
8834 context_ = Handle<Context>();
8835 return;
8836 }
8837
8838 // Move to the next context.
8839 if (context_->is_function_context()) {
8840 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
8841 } else {
8842 context_ = Handle<Context>(context_->previous());
8843 }
8844
8845 // If passing the local scope indicate that the current scope is now the
8846 // local scope.
8847 if (!local_done_ &&
8848 (context_->IsGlobalContext() || (context_->is_function_context()))) {
8849 at_local_ = true;
8850 }
8851 }
8852
8853 // Return the type of the current scope.
8854 int Type() {
8855 if (at_local_) {
8856 return ScopeTypeLocal;
8857 }
8858 if (context_->IsGlobalContext()) {
8859 ASSERT(context_->global()->IsGlobalObject());
8860 return ScopeTypeGlobal;
8861 }
8862 if (context_->is_function_context()) {
8863 return ScopeTypeClosure;
8864 }
8865 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00008866 // Current scope is either an explicit with statement or a with statement
8867 // implicitely generated for a catch block.
8868 // If the extension object here is a JSContextExtensionObject then
8869 // current with statement is one frome a catch block otherwise it's a
8870 // regular with statement.
8871 if (context_->extension()->IsJSContextExtensionObject()) {
8872 return ScopeTypeCatch;
8873 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008874 return ScopeTypeWith;
8875 }
8876
8877 // Return the JavaScript object with the content of the current scope.
8878 Handle<JSObject> ScopeObject() {
8879 switch (Type()) {
8880 case ScopeIterator::ScopeTypeGlobal:
8881 return Handle<JSObject>(CurrentContext()->global());
8882 break;
8883 case ScopeIterator::ScopeTypeLocal:
8884 // Materialize the content of the local scope into a JSObject.
8885 return MaterializeLocalScope(frame_);
8886 break;
8887 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00008888 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008889 // Return the with object.
8890 return Handle<JSObject>(CurrentContext()->extension());
8891 break;
8892 case ScopeIterator::ScopeTypeClosure:
8893 // Materialize the content of the closure scope into a JSObject.
8894 return MaterializeClosure(CurrentContext());
8895 break;
8896 }
8897 UNREACHABLE();
8898 return Handle<JSObject>();
8899 }
8900
8901 // Return the context for this scope. For the local context there might not
8902 // be an actual context.
8903 Handle<Context> CurrentContext() {
8904 if (at_local_ && context_->closure() != *function_) {
8905 return Handle<Context>();
8906 }
8907 return context_;
8908 }
8909
8910#ifdef DEBUG
8911 // Debug print of the content of the current scope.
8912 void DebugPrint() {
8913 switch (Type()) {
8914 case ScopeIterator::ScopeTypeGlobal:
8915 PrintF("Global:\n");
8916 CurrentContext()->Print();
8917 break;
8918
8919 case ScopeIterator::ScopeTypeLocal: {
8920 PrintF("Local:\n");
8921 Handle<Code> code(function_->code());
8922 ScopeInfo<> scope_info(*code);
8923 scope_info.Print();
8924 if (!CurrentContext().is_null()) {
8925 CurrentContext()->Print();
8926 if (CurrentContext()->has_extension()) {
8927 Handle<JSObject> extension =
8928 Handle<JSObject>(CurrentContext()->extension());
8929 if (extension->IsJSContextExtensionObject()) {
8930 extension->Print();
8931 }
8932 }
8933 }
8934 break;
8935 }
8936
8937 case ScopeIterator::ScopeTypeWith: {
8938 PrintF("With:\n");
8939 Handle<JSObject> extension =
8940 Handle<JSObject>(CurrentContext()->extension());
8941 extension->Print();
8942 break;
8943 }
8944
ager@chromium.orga1645e22009-09-09 19:27:10 +00008945 case ScopeIterator::ScopeTypeCatch: {
8946 PrintF("Catch:\n");
8947 Handle<JSObject> extension =
8948 Handle<JSObject>(CurrentContext()->extension());
8949 extension->Print();
8950 break;
8951 }
8952
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008953 case ScopeIterator::ScopeTypeClosure: {
8954 PrintF("Closure:\n");
8955 CurrentContext()->Print();
8956 if (CurrentContext()->has_extension()) {
8957 Handle<JSObject> extension =
8958 Handle<JSObject>(CurrentContext()->extension());
8959 if (extension->IsJSContextExtensionObject()) {
8960 extension->Print();
8961 }
8962 }
8963 break;
8964 }
8965
8966 default:
8967 UNREACHABLE();
8968 }
8969 PrintF("\n");
8970 }
8971#endif
8972
8973 private:
8974 JavaScriptFrame* frame_;
8975 Handle<JSFunction> function_;
8976 Handle<Context> context_;
8977 bool local_done_;
8978 bool at_local_;
8979
8980 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
8981};
8982
8983
8984static Object* Runtime_GetScopeCount(Arguments args) {
8985 HandleScope scope;
8986 ASSERT(args.length() == 2);
8987
8988 // Check arguments.
8989 Object* check = Runtime_CheckExecutionState(args);
8990 if (check->IsFailure()) return check;
8991 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8992
8993 // Get the frame where the debugging is performed.
8994 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8995 JavaScriptFrameIterator it(id);
8996 JavaScriptFrame* frame = it.frame();
8997
8998 // Count the visible scopes.
8999 int n = 0;
9000 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
9001 n++;
9002 }
9003
9004 return Smi::FromInt(n);
9005}
9006
9007
9008static const int kScopeDetailsTypeIndex = 0;
9009static const int kScopeDetailsObjectIndex = 1;
9010static const int kScopeDetailsSize = 2;
9011
9012// Return an array with scope details
9013// args[0]: number: break id
9014// args[1]: number: frame index
9015// args[2]: number: scope index
9016//
9017// The array returned contains the following information:
9018// 0: Scope type
9019// 1: Scope object
9020static Object* Runtime_GetScopeDetails(Arguments args) {
9021 HandleScope scope;
9022 ASSERT(args.length() == 3);
9023
9024 // Check arguments.
9025 Object* check = Runtime_CheckExecutionState(args);
9026 if (check->IsFailure()) return check;
9027 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9028 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
9029
9030 // Get the frame where the debugging is performed.
9031 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9032 JavaScriptFrameIterator frame_it(id);
9033 JavaScriptFrame* frame = frame_it.frame();
9034
9035 // Find the requested scope.
9036 int n = 0;
9037 ScopeIterator it(frame);
9038 for (; !it.Done() && n < index; it.Next()) {
9039 n++;
9040 }
9041 if (it.Done()) {
9042 return Heap::undefined_value();
9043 }
9044
9045 // Calculate the size of the result.
9046 int details_size = kScopeDetailsSize;
9047 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
9048
9049 // Fill in scope details.
9050 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
9051 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
9052
9053 return *Factory::NewJSArrayWithElements(details);
9054}
9055
9056
9057static Object* Runtime_DebugPrintScopes(Arguments args) {
9058 HandleScope scope;
9059 ASSERT(args.length() == 0);
9060
9061#ifdef DEBUG
9062 // Print the scopes for the top frame.
9063 StackFrameLocator locator;
9064 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
9065 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
9066 it.DebugPrint();
9067 }
9068#endif
9069 return Heap::undefined_value();
9070}
9071
9072
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009073static Object* Runtime_GetCFrames(Arguments args) {
9074 HandleScope scope;
9075 ASSERT(args.length() == 1);
9076 Object* result = Runtime_CheckExecutionState(args);
9077 if (result->IsFailure()) return result;
9078
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00009079#if V8_HOST_ARCH_64_BIT
9080 UNIMPLEMENTED();
9081 return Heap::undefined_value();
9082#else
9083
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009084 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009085 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
9086 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009087 if (frames_count == OS::kStackWalkError) {
9088 return Heap::undefined_value();
9089 }
9090
9091 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
9092 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
9093 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
9094 for (int i = 0; i < frames_count; i++) {
9095 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
9096 frame_value->SetProperty(
9097 *address_str,
9098 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
9099 NONE);
9100
9101 // Get the stack walk text for this frame.
9102 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009103 int frame_text_length = StrLength(frames[i].text);
9104 if (frame_text_length > 0) {
9105 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009106 frame_text = Factory::NewStringFromAscii(str);
9107 }
9108
9109 if (!frame_text.is_null()) {
9110 frame_value->SetProperty(*text_str, *frame_text, NONE);
9111 }
9112
9113 frames_array->set(i, *frame_value);
9114 }
9115 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00009116#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009117}
9118
9119
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00009120static Object* Runtime_GetThreadCount(Arguments args) {
9121 HandleScope scope;
9122 ASSERT(args.length() == 1);
9123
9124 // Check arguments.
9125 Object* result = Runtime_CheckExecutionState(args);
9126 if (result->IsFailure()) return result;
9127
9128 // Count all archived V8 threads.
9129 int n = 0;
9130 for (ThreadState* thread = ThreadState::FirstInUse();
9131 thread != NULL;
9132 thread = thread->Next()) {
9133 n++;
9134 }
9135
9136 // Total number of threads is current thread and archived threads.
9137 return Smi::FromInt(n + 1);
9138}
9139
9140
9141static const int kThreadDetailsCurrentThreadIndex = 0;
9142static const int kThreadDetailsThreadIdIndex = 1;
9143static const int kThreadDetailsSize = 2;
9144
9145// Return an array with thread details
9146// args[0]: number: break id
9147// args[1]: number: thread index
9148//
9149// The array returned contains the following information:
9150// 0: Is current thread?
9151// 1: Thread id
9152static Object* Runtime_GetThreadDetails(Arguments args) {
9153 HandleScope scope;
9154 ASSERT(args.length() == 2);
9155
9156 // Check arguments.
9157 Object* check = Runtime_CheckExecutionState(args);
9158 if (check->IsFailure()) return check;
9159 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
9160
9161 // Allocate array for result.
9162 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
9163
9164 // Thread index 0 is current thread.
9165 if (index == 0) {
9166 // Fill the details.
9167 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
9168 details->set(kThreadDetailsThreadIdIndex,
9169 Smi::FromInt(ThreadManager::CurrentId()));
9170 } else {
9171 // Find the thread with the requested index.
9172 int n = 1;
9173 ThreadState* thread = ThreadState::FirstInUse();
9174 while (index != n && thread != NULL) {
9175 thread = thread->Next();
9176 n++;
9177 }
9178 if (thread == NULL) {
9179 return Heap::undefined_value();
9180 }
9181
9182 // Fill the details.
9183 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
9184 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
9185 }
9186
9187 // Convert to JS array and return.
9188 return *Factory::NewJSArrayWithElements(details);
9189}
9190
9191
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009192static Object* Runtime_GetBreakLocations(Arguments args) {
9193 HandleScope scope;
9194 ASSERT(args.length() == 1);
9195
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009196 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9197 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009198 // Find the number of break points
9199 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
9200 if (break_locations->IsUndefined()) return Heap::undefined_value();
9201 // Return array as JS array
9202 return *Factory::NewJSArrayWithElements(
9203 Handle<FixedArray>::cast(break_locations));
9204}
9205
9206
9207// Set a break point in a function
9208// args[0]: function
9209// args[1]: number: break source position (within the function source)
9210// args[2]: number: break point object
9211static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
9212 HandleScope scope;
9213 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009214 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9215 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009216 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9217 RUNTIME_ASSERT(source_position >= 0);
9218 Handle<Object> break_point_object_arg = args.at<Object>(2);
9219
9220 // Set break point.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009221 Debug::SetBreakPoint(shared, break_point_object_arg, &source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009222
lrn@chromium.org32d961d2010-06-30 09:09:34 +00009223 return Smi::FromInt(source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009224}
9225
9226
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009227Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
9228 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009229 // Iterate the heap looking for SharedFunctionInfo generated from the
9230 // script. The inner most SharedFunctionInfo containing the source position
9231 // for the requested break point is found.
9232 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
9233 // which is found is not compiled it is compiled and the heap is iterated
9234 // again as the compilation might create inner functions from the newly
9235 // compiled function and the actual requested break point might be in one of
9236 // these functions.
9237 bool done = false;
9238 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00009239 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009240 Handle<SharedFunctionInfo> target;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009241 while (!done) {
9242 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009243 for (HeapObject* obj = iterator.next();
9244 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009245 if (obj->IsSharedFunctionInfo()) {
9246 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
9247 if (shared->script() == *script) {
9248 // If the SharedFunctionInfo found has the requested script data and
9249 // contains the source position it is a candidate.
9250 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00009251 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009252 start_position = shared->start_position();
9253 }
9254 if (start_position <= position &&
9255 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00009256 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009257 // candidate this is the new candidate.
9258 if (target.is_null()) {
9259 target_start_position = start_position;
9260 target = shared;
9261 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00009262 if (target_start_position == start_position &&
9263 shared->end_position() == target->end_position()) {
9264 // If a top-level function contain only one function
9265 // declartion the source for the top-level and the function is
9266 // the same. In that case prefer the non top-level function.
9267 if (!shared->is_toplevel()) {
9268 target_start_position = start_position;
9269 target = shared;
9270 }
9271 } else if (target_start_position <= start_position &&
9272 shared->end_position() <= target->end_position()) {
9273 // This containment check includes equality as a function inside
9274 // a top-level function can share either start or end position
9275 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009276 target_start_position = start_position;
9277 target = shared;
9278 }
9279 }
9280 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009281 }
9282 }
9283 }
9284
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009285 if (target.is_null()) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009286 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009287 }
9288
9289 // If the candidate found is compiled we are done. NOTE: when lazy
9290 // compilation of inner functions is introduced some additional checking
9291 // needs to be done here to compile inner functions.
9292 done = target->is_compiled();
9293 if (!done) {
9294 // If the candidate is not compiled compile it to reveal any inner
9295 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009296 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009297 }
9298 }
9299
9300 return *target;
9301}
9302
9303
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009304// Changes the state of a break point in a script and returns source position
9305// where break point was set. NOTE: Regarding performance see the NOTE for
9306// GetScriptFromScriptData.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009307// args[0]: script to set break point in
9308// args[1]: number: break source position (within the script source)
9309// args[2]: number: break point object
9310static Object* Runtime_SetScriptBreakPoint(Arguments args) {
9311 HandleScope scope;
9312 ASSERT(args.length() == 3);
9313 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
9314 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9315 RUNTIME_ASSERT(source_position >= 0);
9316 Handle<Object> break_point_object_arg = args.at<Object>(2);
9317
9318 // Get the script from the script wrapper.
9319 RUNTIME_ASSERT(wrapper->value()->IsScript());
9320 Handle<Script> script(Script::cast(wrapper->value()));
9321
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009322 Object* result = Runtime::FindSharedFunctionInfoInScript(
9323 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009324 if (!result->IsUndefined()) {
9325 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
9326 // Find position within function. The script position might be before the
9327 // source position of the first function.
9328 int position;
9329 if (shared->start_position() > source_position) {
9330 position = 0;
9331 } else {
9332 position = source_position - shared->start_position();
9333 }
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009334 Debug::SetBreakPoint(shared, break_point_object_arg, &position);
9335 position += shared->start_position();
9336 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009337 }
9338 return Heap::undefined_value();
9339}
9340
9341
9342// Clear a break point
9343// args[0]: number: break point object
9344static Object* Runtime_ClearBreakPoint(Arguments args) {
9345 HandleScope scope;
9346 ASSERT(args.length() == 1);
9347 Handle<Object> break_point_object_arg = args.at<Object>(0);
9348
9349 // Clear break point.
9350 Debug::ClearBreakPoint(break_point_object_arg);
9351
9352 return Heap::undefined_value();
9353}
9354
9355
9356// Change the state of break on exceptions
9357// args[0]: boolean indicating uncaught exceptions
9358// args[1]: boolean indicating on/off
9359static Object* Runtime_ChangeBreakOnException(Arguments args) {
9360 HandleScope scope;
9361 ASSERT(args.length() == 2);
9362 ASSERT(args[0]->IsNumber());
9363 ASSERT(args[1]->IsBoolean());
9364
9365 // Update break point state
9366 ExceptionBreakType type =
9367 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
9368 bool enable = args[1]->ToBoolean()->IsTrue();
9369 Debug::ChangeBreakOnException(type, enable);
9370 return Heap::undefined_value();
9371}
9372
9373
9374// Prepare for stepping
9375// args[0]: break id for checking execution state
9376// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00009377// args[2]: number of times to perform the step, for step out it is the number
9378// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009379static Object* Runtime_PrepareStep(Arguments args) {
9380 HandleScope scope;
9381 ASSERT(args.length() == 3);
9382 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009383 Object* check = Runtime_CheckExecutionState(args);
9384 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009385 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
9386 return Top::Throw(Heap::illegal_argument_symbol());
9387 }
9388
9389 // Get the step action and check validity.
9390 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
9391 if (step_action != StepIn &&
9392 step_action != StepNext &&
9393 step_action != StepOut &&
9394 step_action != StepInMin &&
9395 step_action != StepMin) {
9396 return Top::Throw(Heap::illegal_argument_symbol());
9397 }
9398
9399 // Get the number of steps.
9400 int step_count = NumberToInt32(args[2]);
9401 if (step_count < 1) {
9402 return Top::Throw(Heap::illegal_argument_symbol());
9403 }
9404
ager@chromium.orga1645e22009-09-09 19:27:10 +00009405 // Clear all current stepping setup.
9406 Debug::ClearStepping();
9407
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009408 // Prepare step.
9409 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
9410 return Heap::undefined_value();
9411}
9412
9413
9414// Clear all stepping set by PrepareStep.
9415static Object* Runtime_ClearStepping(Arguments args) {
9416 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009417 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009418 Debug::ClearStepping();
9419 return Heap::undefined_value();
9420}
9421
9422
9423// Creates a copy of the with context chain. The copy of the context chain is
9424// is linked to the function context supplied.
9425static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
9426 Handle<Context> function_context) {
9427 // At the bottom of the chain. Return the function context to link to.
9428 if (context_chain->is_function_context()) {
9429 return function_context;
9430 }
9431
9432 // Recursively copy the with contexts.
9433 Handle<Context> previous(context_chain->previous());
9434 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
9435 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009436 CopyWithContextChain(function_context, previous),
9437 extension,
9438 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009439}
9440
9441
9442// Helper function to find or create the arguments object for
9443// Runtime_DebugEvaluate.
9444static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
9445 Handle<JSFunction> function,
9446 Handle<Code> code,
9447 const ScopeInfo<>* sinfo,
9448 Handle<Context> function_context) {
9449 // Try to find the value of 'arguments' to pass as parameter. If it is not
9450 // found (that is the debugged function does not reference 'arguments' and
9451 // does not support eval) then create an 'arguments' object.
9452 int index;
9453 if (sinfo->number_of_stack_slots() > 0) {
9454 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
9455 if (index != -1) {
9456 return Handle<Object>(frame->GetExpression(index));
9457 }
9458 }
9459
9460 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
9461 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
9462 NULL);
9463 if (index != -1) {
9464 return Handle<Object>(function_context->get(index));
9465 }
9466 }
9467
9468 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009469 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
9470 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009471
9472 AssertNoAllocation no_gc;
9473 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009474 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009475 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009476 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009477 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009478 return arguments;
9479}
9480
9481
9482// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00009483// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009484// extension part has all the parameters and locals of the function on the
9485// stack frame. A function which calls eval with the code to evaluate is then
9486// compiled in this context and called in this context. As this context
9487// replaces the context of the function on the stack frame a new (empty)
9488// function is created as well to be used as the closure for the context.
9489// This function and the context acts as replacements for the function on the
9490// stack frame presenting the same view of the values of parameters and
9491// local variables as if the piece of JavaScript was evaluated at the point
9492// where the function on the stack frame is currently stopped.
9493static Object* Runtime_DebugEvaluate(Arguments args) {
9494 HandleScope scope;
9495
9496 // Check the execution state and decode arguments frame and source to be
9497 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009498 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009499 Object* check_result = Runtime_CheckExecutionState(args);
9500 if (check_result->IsFailure()) return check_result;
9501 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9502 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009503 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
9504
9505 // Handle the processing of break.
9506 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009507
9508 // Get the frame where the debugging is performed.
9509 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9510 JavaScriptFrameIterator it(id);
9511 JavaScriptFrame* frame = it.frame();
9512 Handle<JSFunction> function(JSFunction::cast(frame->function()));
9513 Handle<Code> code(function->code());
9514 ScopeInfo<> sinfo(*code);
9515
9516 // Traverse the saved contexts chain to find the active context for the
9517 // selected frame.
9518 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009519 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009520 save = save->prev();
9521 }
9522 ASSERT(save != NULL);
9523 SaveContext savex;
9524 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009525
9526 // Create the (empty) function replacing the function on the stack frame for
9527 // the purpose of evaluating in the context created below. It is important
9528 // that this function does not describe any parameters and local variables
9529 // in the context. If it does then this will cause problems with the lookup
9530 // in Context::Lookup, where context slots for parameters and local variables
9531 // are looked at before the extension object.
9532 Handle<JSFunction> go_between =
9533 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
9534 go_between->set_context(function->context());
9535#ifdef DEBUG
9536 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
9537 ASSERT(go_between_sinfo.number_of_parameters() == 0);
9538 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
9539#endif
9540
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009541 // Materialize the content of the local scope into a JSObject.
9542 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009543
9544 // Allocate a new context for the debug evaluation and set the extension
9545 // object build.
9546 Handle<Context> context =
9547 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009548 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009549 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009550 Handle<Context> frame_context(Context::cast(frame->context()));
9551 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009552 context = CopyWithContextChain(frame_context, context);
9553
9554 // Wrap the evaluation statement in a new function compiled in the newly
9555 // created context. The function has one parameter which has to be called
9556 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00009557 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009558 // function(arguments,__source__) {return eval(__source__);}
9559 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00009560 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009561 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009562 Handle<String> function_source =
9563 Factory::NewStringFromAscii(Vector<const char>(source_str,
9564 source_str_length));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009565 Handle<SharedFunctionInfo> shared =
ager@chromium.org381abbb2009-02-25 13:23:22 +00009566 Compiler::CompileEval(function_source,
9567 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00009568 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00009569 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009570 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009571 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009572 Factory::NewFunctionFromSharedFunctionInfo(shared, context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009573
9574 // Invoke the result of the compilation to get the evaluation function.
9575 bool has_pending_exception;
9576 Handle<Object> receiver(frame->receiver());
9577 Handle<Object> evaluation_function =
9578 Execution::Call(compiled_function, receiver, 0, NULL,
9579 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009580 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009581
9582 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
9583 function_context);
9584
9585 // Invoke the evaluation function and return the result.
9586 const int argc = 2;
9587 Object** argv[argc] = { arguments.location(),
9588 Handle<Object>::cast(source).location() };
9589 Handle<Object> result =
9590 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
9591 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009592 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009593
9594 // Skip the global proxy as it has no properties and always delegates to the
9595 // real global object.
9596 if (result->IsJSGlobalProxy()) {
9597 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
9598 }
9599
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009600 return *result;
9601}
9602
9603
9604static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
9605 HandleScope scope;
9606
9607 // Check the execution state and decode arguments frame and source to be
9608 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009609 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009610 Object* check_result = Runtime_CheckExecutionState(args);
9611 if (check_result->IsFailure()) return check_result;
9612 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009613 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
9614
9615 // Handle the processing of break.
9616 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009617
9618 // Enter the top context from before the debugger was invoked.
9619 SaveContext save;
9620 SaveContext* top = &save;
9621 while (top != NULL && *top->context() == *Debug::debug_context()) {
9622 top = top->prev();
9623 }
9624 if (top != NULL) {
9625 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009626 }
9627
9628 // Get the global context now set to the top context from before the
9629 // debugger was invoked.
9630 Handle<Context> context = Top::global_context();
9631
9632 // Compile the source to be evaluated.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009633 Handle<SharedFunctionInfo> shared =
9634 Compiler::CompileEval(source,
9635 context,
9636 true,
9637 Compiler::DONT_VALIDATE_JSON);
9638 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009639 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009640 Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
9641 context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009642
9643 // Invoke the result of the compilation to get the evaluation function.
9644 bool has_pending_exception;
9645 Handle<Object> receiver = Top::global();
9646 Handle<Object> result =
9647 Execution::Call(compiled_function, receiver, 0, NULL,
9648 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009649 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009650 return *result;
9651}
9652
9653
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009654static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
9655 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009656 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009657
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009658 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009659 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009660
9661 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009662 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00009663 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
9664 // Get the script wrapper in a local handle before calling GetScriptWrapper,
9665 // because using
9666 // instances->set(i, *GetScriptWrapper(script))
9667 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
9668 // already have deferenced the instances handle.
9669 Handle<JSValue> wrapper = GetScriptWrapper(script);
9670 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009671 }
9672
9673 // Return result as a JS array.
9674 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
9675 Handle<JSArray>::cast(result)->SetContent(*instances);
9676 return *result;
9677}
9678
9679
9680// Helper function used by Runtime_DebugReferencedBy below.
9681static int DebugReferencedBy(JSObject* target,
9682 Object* instance_filter, int max_references,
9683 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009684 JSFunction* arguments_function) {
9685 NoHandleAllocation ha;
9686 AssertNoAllocation no_alloc;
9687
9688 // Iterate the heap.
9689 int count = 0;
9690 JSObject* last = NULL;
9691 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009692 HeapObject* heap_obj = NULL;
9693 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009694 (max_references == 0 || count < max_references)) {
9695 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009696 if (heap_obj->IsJSObject()) {
9697 // Skip context extension objects and argument arrays as these are
9698 // checked in the context of functions using them.
9699 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00009700 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009701 obj->map()->constructor() == arguments_function) {
9702 continue;
9703 }
9704
9705 // Check if the JS object has a reference to the object looked for.
9706 if (obj->ReferencesObject(target)) {
9707 // Check instance filter if supplied. This is normally used to avoid
9708 // references from mirror objects (see Runtime_IsInPrototypeChain).
9709 if (!instance_filter->IsUndefined()) {
9710 Object* V = obj;
9711 while (true) {
9712 Object* prototype = V->GetPrototype();
9713 if (prototype->IsNull()) {
9714 break;
9715 }
9716 if (instance_filter == prototype) {
9717 obj = NULL; // Don't add this object.
9718 break;
9719 }
9720 V = prototype;
9721 }
9722 }
9723
9724 if (obj != NULL) {
9725 // Valid reference found add to instance array if supplied an update
9726 // count.
9727 if (instances != NULL && count < instances_size) {
9728 instances->set(count, obj);
9729 }
9730 last = obj;
9731 count++;
9732 }
9733 }
9734 }
9735 }
9736
9737 // Check for circular reference only. This can happen when the object is only
9738 // referenced from mirrors and has a circular reference in which case the
9739 // object is not really alive and would have been garbage collected if not
9740 // referenced from the mirror.
9741 if (count == 1 && last == target) {
9742 count = 0;
9743 }
9744
9745 // Return the number of referencing objects found.
9746 return count;
9747}
9748
9749
9750// Scan the heap for objects with direct references to an object
9751// args[0]: the object to find references to
9752// args[1]: constructor function for instances to exclude (Mirror)
9753// args[2]: the the maximum number of objects to return
9754static Object* Runtime_DebugReferencedBy(Arguments args) {
9755 ASSERT(args.length() == 3);
9756
9757 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009758 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009759
9760 // Check parameters.
9761 CONVERT_CHECKED(JSObject, target, args[0]);
9762 Object* instance_filter = args[1];
9763 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
9764 instance_filter->IsJSObject());
9765 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
9766 RUNTIME_ASSERT(max_references >= 0);
9767
9768 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009769 JSObject* arguments_boilerplate =
9770 Top::context()->global_context()->arguments_boilerplate();
9771 JSFunction* arguments_function =
9772 JSFunction::cast(arguments_boilerplate->map()->constructor());
9773
9774 // Get the number of referencing objects.
9775 int count;
9776 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009777 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009778
9779 // Allocate an array to hold the result.
9780 Object* object = Heap::AllocateFixedArray(count);
9781 if (object->IsFailure()) return object;
9782 FixedArray* instances = FixedArray::cast(object);
9783
9784 // Fill the referencing objects.
9785 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009786 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009787
9788 // Return result as JS array.
9789 Object* result =
9790 Heap::AllocateJSObject(
9791 Top::context()->global_context()->array_function());
9792 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9793 return result;
9794}
9795
9796
9797// Helper function used by Runtime_DebugConstructedBy below.
9798static int DebugConstructedBy(JSFunction* constructor, int max_references,
9799 FixedArray* instances, int instances_size) {
9800 AssertNoAllocation no_alloc;
9801
9802 // Iterate the heap.
9803 int count = 0;
9804 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009805 HeapObject* heap_obj = NULL;
9806 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009807 (max_references == 0 || count < max_references)) {
9808 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009809 if (heap_obj->IsJSObject()) {
9810 JSObject* obj = JSObject::cast(heap_obj);
9811 if (obj->map()->constructor() == constructor) {
9812 // Valid reference found add to instance array if supplied an update
9813 // count.
9814 if (instances != NULL && count < instances_size) {
9815 instances->set(count, obj);
9816 }
9817 count++;
9818 }
9819 }
9820 }
9821
9822 // Return the number of referencing objects found.
9823 return count;
9824}
9825
9826
9827// Scan the heap for objects constructed by a specific function.
9828// args[0]: the constructor to find instances of
9829// args[1]: the the maximum number of objects to return
9830static Object* Runtime_DebugConstructedBy(Arguments args) {
9831 ASSERT(args.length() == 2);
9832
9833 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009834 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009835
9836 // Check parameters.
9837 CONVERT_CHECKED(JSFunction, constructor, args[0]);
9838 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
9839 RUNTIME_ASSERT(max_references >= 0);
9840
9841 // Get the number of referencing objects.
9842 int count;
9843 count = DebugConstructedBy(constructor, max_references, NULL, 0);
9844
9845 // Allocate an array to hold the result.
9846 Object* object = Heap::AllocateFixedArray(count);
9847 if (object->IsFailure()) return object;
9848 FixedArray* instances = FixedArray::cast(object);
9849
9850 // Fill the referencing objects.
9851 count = DebugConstructedBy(constructor, max_references, instances, count);
9852
9853 // Return result as JS array.
9854 Object* result =
9855 Heap::AllocateJSObject(
9856 Top::context()->global_context()->array_function());
9857 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9858 return result;
9859}
9860
9861
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009862// Find the effective prototype object as returned by __proto__.
9863// args[0]: the object to find the prototype for.
9864static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009865 ASSERT(args.length() == 1);
9866
9867 CONVERT_CHECKED(JSObject, obj, args[0]);
9868
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009869 // Use the __proto__ accessor.
9870 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009871}
9872
9873
9874static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009875 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009876 CPU::DebugBreak();
9877 return Heap::undefined_value();
9878}
9879
9880
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009881static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009882#ifdef DEBUG
9883 HandleScope scope;
9884 ASSERT(args.length() == 1);
9885 // Get the function and make sure it is compiled.
9886 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009887 Handle<SharedFunctionInfo> shared(func->shared());
9888 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009889 return Failure::Exception();
9890 }
9891 func->code()->PrintLn();
9892#endif // DEBUG
9893 return Heap::undefined_value();
9894}
ager@chromium.org9085a012009-05-11 19:22:57 +00009895
9896
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009897static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
9898#ifdef DEBUG
9899 HandleScope scope;
9900 ASSERT(args.length() == 1);
9901 // Get the function and make sure it is compiled.
9902 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009903 Handle<SharedFunctionInfo> shared(func->shared());
9904 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009905 return Failure::Exception();
9906 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009907 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009908#endif // DEBUG
9909 return Heap::undefined_value();
9910}
9911
9912
ager@chromium.org9085a012009-05-11 19:22:57 +00009913static Object* Runtime_FunctionGetInferredName(Arguments args) {
9914 NoHandleAllocation ha;
9915 ASSERT(args.length() == 1);
9916
9917 CONVERT_CHECKED(JSFunction, f, args[0]);
9918 return f->shared()->inferred_name();
9919}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009920
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009921
9922static int FindSharedFunctionInfosForScript(Script* script,
9923 FixedArray* buffer) {
9924 AssertNoAllocation no_allocations;
9925
9926 int counter = 0;
9927 int buffer_size = buffer->length();
9928 HeapIterator iterator;
9929 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
9930 ASSERT(obj != NULL);
9931 if (!obj->IsSharedFunctionInfo()) {
9932 continue;
9933 }
9934 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
9935 if (shared->script() != script) {
9936 continue;
9937 }
9938 if (counter < buffer_size) {
9939 buffer->set(counter, shared);
9940 }
9941 counter++;
9942 }
9943 return counter;
9944}
9945
9946// For a script finds all SharedFunctionInfo's in the heap that points
9947// to this script. Returns JSArray of SharedFunctionInfo wrapped
9948// in OpaqueReferences.
9949static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
9950 Arguments args) {
9951 ASSERT(args.length() == 1);
9952 HandleScope scope;
9953 CONVERT_CHECKED(JSValue, script_value, args[0]);
9954
9955 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
9956
9957 const int kBufferSize = 32;
9958
9959 Handle<FixedArray> array;
9960 array = Factory::NewFixedArray(kBufferSize);
9961 int number = FindSharedFunctionInfosForScript(*script, *array);
9962 if (number > kBufferSize) {
9963 array = Factory::NewFixedArray(number);
9964 FindSharedFunctionInfosForScript(*script, *array);
9965 }
9966
9967 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
9968 result->set_length(Smi::FromInt(number));
9969
9970 LiveEdit::WrapSharedFunctionInfos(result);
9971
9972 return *result;
9973}
9974
9975// For a script calculates compilation information about all its functions.
9976// The script source is explicitly specified by the second argument.
9977// The source of the actual script is not used, however it is important that
9978// all generated code keeps references to this particular instance of script.
9979// Returns a JSArray of compilation infos. The array is ordered so that
9980// each function with all its descendant is always stored in a continues range
9981// with the function itself going first. The root function is a script function.
9982static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
9983 ASSERT(args.length() == 2);
9984 HandleScope scope;
9985 CONVERT_CHECKED(JSValue, script, args[0]);
9986 CONVERT_ARG_CHECKED(String, source, 1);
9987 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
9988
9989 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
9990
9991 if (Top::has_pending_exception()) {
9992 return Failure::Exception();
9993 }
9994
9995 return result;
9996}
9997
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009998// Changes the source of the script to a new_source.
9999// If old_script_name is provided (i.e. is a String), also creates a copy of
10000// the script with its original source and sends notification to debugger.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010001static Object* Runtime_LiveEditReplaceScript(Arguments args) {
10002 ASSERT(args.length() == 3);
10003 HandleScope scope;
10004 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
10005 CONVERT_ARG_CHECKED(String, new_source, 1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010006 Handle<Object> old_script_name(args[2]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010007
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010008 CONVERT_CHECKED(Script, original_script_pointer,
10009 original_script_value->value());
10010 Handle<Script> original_script(original_script_pointer);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010011
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010012 Object* old_script = LiveEdit::ChangeScriptSource(original_script,
10013 new_source,
10014 old_script_name);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010015
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010016 if (old_script->IsScript()) {
10017 Handle<Script> script_handle(Script::cast(old_script));
10018 return *(GetScriptWrapper(script_handle));
10019 } else {
10020 return Heap::null_value();
10021 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010022}
10023
10024// Replaces code of SharedFunctionInfo with a new one.
10025static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
10026 ASSERT(args.length() == 2);
10027 HandleScope scope;
10028 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
10029 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
10030
ager@chromium.orgac091b72010-05-05 07:34:42 +000010031 return LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010032}
10033
10034// Connects SharedFunctionInfo to another script.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010035static Object* Runtime_LiveEditFunctionSetScript(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010036 ASSERT(args.length() == 2);
10037 HandleScope scope;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010038 Handle<Object> function_object(args[0]);
10039 Handle<Object> script_object(args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010040
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010041 if (function_object->IsJSValue()) {
10042 Handle<JSValue> function_wrapper = Handle<JSValue>::cast(function_object);
10043 if (script_object->IsJSValue()) {
10044 CONVERT_CHECKED(Script, script, JSValue::cast(*script_object)->value());
10045 script_object = Handle<Object>(script);
10046 }
10047
10048 LiveEdit::SetFunctionScript(function_wrapper, script_object);
10049 } else {
10050 // Just ignore this. We may not have a SharedFunctionInfo for some functions
10051 // and we check it in this function.
10052 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010053
10054 return Heap::undefined_value();
10055}
10056
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010057
10058// In a code of a parent function replaces original function as embedded object
10059// with a substitution one.
10060static Object* Runtime_LiveEditReplaceRefToNestedFunction(Arguments args) {
10061 ASSERT(args.length() == 3);
10062 HandleScope scope;
10063
10064 CONVERT_ARG_CHECKED(JSValue, parent_wrapper, 0);
10065 CONVERT_ARG_CHECKED(JSValue, orig_wrapper, 1);
10066 CONVERT_ARG_CHECKED(JSValue, subst_wrapper, 2);
10067
10068 LiveEdit::ReplaceRefToNestedFunction(parent_wrapper, orig_wrapper,
10069 subst_wrapper);
10070
10071 return Heap::undefined_value();
10072}
10073
10074
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010075// Updates positions of a shared function info (first parameter) according
10076// to script source change. Text change is described in second parameter as
10077// array of groups of 3 numbers:
10078// (change_begin, change_end, change_end_new_position).
10079// Each group describes a change in text; groups are sorted by change_begin.
10080static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
10081 ASSERT(args.length() == 2);
10082 HandleScope scope;
10083 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
10084 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
10085
ager@chromium.orgac091b72010-05-05 07:34:42 +000010086 return LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010087}
10088
10089
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010090// For array of SharedFunctionInfo's (each wrapped in JSValue)
10091// checks that none of them have activations on stacks (of any thread).
10092// Returns array of the same length with corresponding results of
10093// LiveEdit::FunctionPatchabilityStatus type.
ager@chromium.org357bf652010-04-12 11:30:10 +000010094static Object* Runtime_LiveEditCheckAndDropActivations(Arguments args) {
10095 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010096 HandleScope scope;
10097 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +000010098 CONVERT_BOOLEAN_CHECKED(do_drop, args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010099
ager@chromium.org357bf652010-04-12 11:30:10 +000010100 return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010101}
10102
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010103// Compares 2 strings line-by-line and returns diff in form of JSArray of
fschneider@chromium.org013f3e12010-04-26 13:27:52 +000010104// triplets (pos1, pos1_end, pos2_end) describing list of diff chunks.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010105static Object* Runtime_LiveEditCompareStringsLinewise(Arguments args) {
10106 ASSERT(args.length() == 2);
10107 HandleScope scope;
10108 CONVERT_ARG_CHECKED(String, s1, 0);
10109 CONVERT_ARG_CHECKED(String, s2, 1);
10110
10111 return *LiveEdit::CompareStringsLinewise(s1, s2);
10112}
10113
10114
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010115
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010116// A testing entry. Returns statement position which is the closest to
10117// source_position.
10118static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
10119 ASSERT(args.length() == 2);
10120 HandleScope scope;
10121 CONVERT_ARG_CHECKED(JSFunction, function, 0);
10122 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
10123
10124 Handle<Code> code(function->code());
10125
10126 RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
10127 int closest_pc = 0;
10128 int distance = kMaxInt;
10129 while (!it.done()) {
10130 int statement_position = static_cast<int>(it.rinfo()->data());
10131 // Check if this break point is closer that what was previously found.
10132 if (source_position <= statement_position &&
10133 statement_position - source_position < distance) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +000010134 closest_pc =
10135 static_cast<int>(it.rinfo()->pc() - code->instruction_start());
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010136 distance = statement_position - source_position;
10137 // Check whether we can't get any closer.
10138 if (distance == 0) break;
10139 }
10140 it.next();
10141 }
10142
10143 return Smi::FromInt(closest_pc);
10144}
10145
10146
ager@chromium.org357bf652010-04-12 11:30:10 +000010147// Calls specified function with or without entering the debugger.
10148// This is used in unit tests to run code as if debugger is entered or simply
10149// to have a stack with C++ frame in the middle.
10150static Object* Runtime_ExecuteInDebugContext(Arguments args) {
10151 ASSERT(args.length() == 2);
10152 HandleScope scope;
10153 CONVERT_ARG_CHECKED(JSFunction, function, 0);
10154 CONVERT_BOOLEAN_CHECKED(without_debugger, args[1]);
10155
10156 Handle<Object> result;
10157 bool pending_exception;
10158 {
10159 if (without_debugger) {
10160 result = Execution::Call(function, Top::global(), 0, NULL,
10161 &pending_exception);
10162 } else {
10163 EnterDebugger enter_debugger;
10164 result = Execution::Call(function, Top::global(), 0, NULL,
10165 &pending_exception);
10166 }
10167 }
10168 if (!pending_exception) {
10169 return *result;
10170 } else {
10171 return Failure::Exception();
10172 }
10173}
10174
10175
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010176#endif // ENABLE_DEBUGGER_SUPPORT
10177
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010178#ifdef ENABLE_LOGGING_AND_PROFILING
10179
10180static Object* Runtime_ProfilerResume(Arguments args) {
10181 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010182 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010183
10184 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010185 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10186 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010187 return Heap::undefined_value();
10188}
10189
10190
10191static Object* Runtime_ProfilerPause(Arguments args) {
10192 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010193 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010194
10195 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010196 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10197 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010198 return Heap::undefined_value();
10199}
10200
10201#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010202
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010203// Finds the script object from the script data. NOTE: This operation uses
10204// heap traversal to find the function generated for the source position
10205// for the requested break point. For lazily compiled functions several heap
10206// traversals might be required rendering this operation as a rather slow
10207// operation. However for setting break points which is normally done through
10208// some kind of user interaction the performance is not crucial.
10209static Handle<Object> Runtime_GetScriptFromScriptName(
10210 Handle<String> script_name) {
10211 // Scan the heap for Script objects to find the script with the requested
10212 // script data.
10213 Handle<Script> script;
10214 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010215 HeapObject* obj = NULL;
10216 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010217 // If a script is found check if it has the script data requested.
10218 if (obj->IsScript()) {
10219 if (Script::cast(obj)->name()->IsString()) {
10220 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
10221 script = Handle<Script>(Script::cast(obj));
10222 }
10223 }
10224 }
10225 }
10226
10227 // If no script with the requested script data is found return undefined.
10228 if (script.is_null()) return Factory::undefined_value();
10229
10230 // Return the script found.
10231 return GetScriptWrapper(script);
10232}
10233
10234
10235// Get the script object from script data. NOTE: Regarding performance
10236// see the NOTE for GetScriptFromScriptData.
10237// args[0]: script data for the script to find the source for
10238static Object* Runtime_GetScript(Arguments args) {
10239 HandleScope scope;
10240
10241 ASSERT(args.length() == 1);
10242
10243 CONVERT_CHECKED(String, script_name, args[0]);
10244
10245 // Find the requested script.
10246 Handle<Object> result =
10247 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
10248 return *result;
10249}
10250
10251
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010252// Determines whether the given stack frame should be displayed in
10253// a stack trace. The caller is the error constructor that asked
10254// for the stack trace to be collected. The first time a construct
10255// call to this function is encountered it is skipped. The seen_caller
10256// in/out parameter is used to remember if the caller has been seen
10257// yet.
10258static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
10259 bool* seen_caller) {
10260 // Only display JS frames.
10261 if (!raw_frame->is_java_script())
10262 return false;
10263 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
10264 Object* raw_fun = frame->function();
10265 // Not sure when this can happen but skip it just in case.
10266 if (!raw_fun->IsJSFunction())
10267 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010268 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010269 *seen_caller = true;
10270 return false;
10271 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010272 // Skip all frames until we've seen the caller. Also, skip the most
10273 // obvious builtin calls. Some builtin calls (such as Number.ADD
10274 // which is invoked using 'call') are very difficult to recognize
10275 // so we're leaving them in for now.
10276 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010277}
10278
10279
10280// Collect the raw data for a stack trace. Returns an array of three
10281// element segments each containing a receiver, function and native
10282// code offset.
10283static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010284 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010285 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010286 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
10287
10288 HandleScope scope;
10289
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000010290 limit = Max(limit, 0); // Ensure that limit is not negative.
10291 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010292 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010293
10294 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010295 // If the caller parameter is a function we skip frames until we're
10296 // under it before starting to collect.
10297 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010298 int cursor = 0;
10299 int frames_seen = 0;
10300 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010301 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010302 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010303 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010304 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010305 Object* recv = frame->receiver();
10306 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010307 Address pc = frame->pc();
10308 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010309 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010310 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010311 if (cursor + 2 < elements->length()) {
10312 elements->set(cursor++, recv);
10313 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010314 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010315 } else {
10316 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010317 Handle<Object> recv_handle(recv);
10318 Handle<Object> fun_handle(fun);
10319 SetElement(result, cursor++, recv_handle);
10320 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010321 SetElement(result, cursor++, Handle<Smi>(offset));
10322 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010323 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010324 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010325 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010326
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010327 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010328 return *result;
10329}
10330
10331
ager@chromium.org3811b432009-10-28 14:53:37 +000010332// Returns V8 version as a string.
10333static Object* Runtime_GetV8Version(Arguments args) {
10334 ASSERT_EQ(args.length(), 0);
10335
10336 NoHandleAllocation ha;
10337
10338 const char* version_string = v8::V8::GetVersion();
10339
10340 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
10341}
10342
10343
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010344static Object* Runtime_Abort(Arguments args) {
10345 ASSERT(args.length() == 2);
10346 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
10347 Smi::cast(args[1])->value());
10348 Top::PrintStack();
10349 OS::Abort();
10350 UNREACHABLE();
10351 return NULL;
10352}
10353
10354
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010355static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
10356 ASSERT(args.length() == 0);
10357 HandleScope::DeleteExtensions();
10358 return Heap::undefined_value();
10359}
10360
10361
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010362static Object* CacheMiss(FixedArray* cache_obj, int index, Object* key_obj) {
10363 ASSERT(index % 2 == 0); // index of the key
10364 ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
10365 ASSERT(index < cache_obj->length());
10366
10367 HandleScope scope;
10368
10369 Handle<FixedArray> cache(cache_obj);
10370 Handle<Object> key(key_obj);
10371 Handle<JSFunction> factory(JSFunction::cast(
10372 cache->get(JSFunctionResultCache::kFactoryIndex)));
10373 // TODO(antonm): consider passing a receiver when constructing a cache.
10374 Handle<Object> receiver(Top::global_context()->global());
10375
10376 Handle<Object> value;
10377 {
10378 // This handle is nor shared, nor used later, so it's safe.
10379 Object** argv[] = { key.location() };
10380 bool pending_exception = false;
10381 value = Execution::Call(factory,
10382 receiver,
10383 1,
10384 argv,
10385 &pending_exception);
10386 if (pending_exception) return Failure::Exception();
10387 }
10388
10389 cache->set(index, *key);
10390 cache->set(index + 1, *value);
10391 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(index));
10392
10393 return *value;
10394}
10395
10396
10397static Object* Runtime_GetFromCache(Arguments args) {
10398 // This is only called from codegen, so checks might be more lax.
10399 CONVERT_CHECKED(FixedArray, cache, args[0]);
10400 Object* key = args[1];
10401
10402 const int finger_index =
10403 Smi::cast(cache->get(JSFunctionResultCache::kFingerIndex))->value();
10404
10405 Object* o = cache->get(finger_index);
10406 if (o == key) {
10407 // The fastest case: hit the same place again.
10408 return cache->get(finger_index + 1);
10409 }
10410
10411 for (int i = finger_index - 2;
10412 i >= JSFunctionResultCache::kEntriesIndex;
10413 i -= 2) {
10414 o = cache->get(i);
10415 if (o == key) {
10416 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10417 return cache->get(i + 1);
10418 }
10419 }
10420
10421 const int size =
10422 Smi::cast(cache->get(JSFunctionResultCache::kCacheSizeIndex))->value();
10423 ASSERT(size <= cache->length());
10424
10425 for (int i = size - 2; i > finger_index; i -= 2) {
10426 o = cache->get(i);
10427 if (o == key) {
10428 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10429 return cache->get(i + 1);
10430 }
10431 }
10432
10433 // Cache miss. If we have spare room, put new data into it, otherwise
10434 // evict post finger entry which must be least recently used.
10435 if (size < cache->length()) {
10436 cache->set(JSFunctionResultCache::kCacheSizeIndex, Smi::FromInt(size + 2));
10437 return CacheMiss(cache, size, key);
10438 } else {
antonm@chromium.org397e23c2010-04-21 12:00:05 +000010439 int target_index = finger_index + JSFunctionResultCache::kEntrySize;
10440 if (target_index == cache->length()) {
10441 target_index = JSFunctionResultCache::kEntriesIndex;
10442 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010443 return CacheMiss(cache, target_index, key);
10444 }
10445}
10446
kasper.lund44510672008-07-25 07:37:58 +000010447#ifdef DEBUG
10448// ListNatives is ONLY used by the fuzz-natives.js in debug mode
10449// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010450static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +000010451 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010452 HandleScope scope;
10453 Handle<JSArray> result = Factory::NewJSArray(0);
10454 int index = 0;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010455 bool inline_runtime_functions = false;
ager@chromium.orga1645e22009-09-09 19:27:10 +000010456#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010457 { \
10458 HandleScope inner; \
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010459 Handle<String> name; \
10460 /* Inline runtime functions have an underscore in front of the name. */ \
10461 if (inline_runtime_functions) { \
10462 name = Factory::NewStringFromAscii( \
10463 Vector<const char>("_" #Name, StrLength("_" #Name))); \
10464 } else { \
10465 name = Factory::NewStringFromAscii( \
10466 Vector<const char>(#Name, StrLength(#Name))); \
10467 } \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010468 Handle<JSArray> pair = Factory::NewJSArray(0); \
10469 SetElement(pair, 0, name); \
10470 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
10471 SetElement(result, index++, pair); \
10472 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010473 inline_runtime_functions = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010474 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010475 inline_runtime_functions = true;
10476 INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010477#undef ADD_ENTRY
10478 return *result;
10479}
kasper.lund44510672008-07-25 07:37:58 +000010480#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010481
10482
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010483static Object* Runtime_Log(Arguments args) {
10484 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +000010485 CONVERT_CHECKED(String, format, args[0]);
10486 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010487 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010488 Logger::LogRuntime(chars, elms);
10489 return Heap::undefined_value();
10490}
10491
10492
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010493static Object* Runtime_IS_VAR(Arguments args) {
10494 UNREACHABLE(); // implemented as macro in the parser
10495 return NULL;
10496}
10497
10498
10499// ----------------------------------------------------------------------------
10500// Implementation of Runtime
10501
ager@chromium.orga1645e22009-09-09 19:27:10 +000010502#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010503 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +000010504 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010505
10506static Runtime::Function Runtime_functions[] = {
10507 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010508 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010509};
10510
10511#undef F
10512
10513
10514Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
10515 ASSERT(0 <= fid && fid < kNofFunctions);
10516 return &Runtime_functions[fid];
10517}
10518
10519
10520Runtime::Function* Runtime::FunctionForName(const char* name) {
10521 for (Function* f = Runtime_functions; f->name != NULL; f++) {
10522 if (strcmp(f->name, name) == 0) {
10523 return f;
10524 }
10525 }
10526 return NULL;
10527}
10528
10529
10530void Runtime::PerformGC(Object* result) {
10531 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010532 if (failure->IsRetryAfterGC()) {
10533 // Try to do a garbage collection; ignore it if it fails. The C
10534 // entry stub will throw an out-of-memory exception in that case.
10535 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
10536 } else {
10537 // Handle last resort GC and make sure to allow future allocations
10538 // to grow the heap without causing GCs (if possible).
10539 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +000010540 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010541 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010542}
10543
10544
10545} } // namespace v8::internal