blob: 535d9a973b3a18872d19c1d2951d373bac515512 [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 }
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00001609 // Set the code, scope info, formal parameter count,
1610 // and the length of the target function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001611 target->set_code(fun->code());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00001612 target->shared()->set_scope_info(shared->scope_info());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001613 target->shared()->set_length(shared->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001614 target->shared()->set_formal_parameter_count(
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001615 shared->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001616 // Set the source code of the target function to undefined.
1617 // SetCode is only used for built-in constructors like String,
1618 // Array, and Object, and some web code
1619 // doesn't like seeing source code for constructors.
1620 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001621 // Clear the optimization hints related to the compiled code as these are no
1622 // longer valid when the code is overwritten.
1623 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001624 context = Handle<Context>(fun->context());
1625
1626 // Make sure we get a fresh copy of the literal vector to avoid
1627 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001628 int number_of_literals = fun->NumberOfLiterals();
1629 Handle<FixedArray> literals =
1630 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001631 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001632 // Insert the object, regexp and array functions in the literals
1633 // array prefix. These are the functions that will be used when
1634 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001635 literals->set(JSFunction::kLiteralGlobalContextIndex,
1636 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001637 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001638 // It's okay to skip the write barrier here because the literals
1639 // are guaranteed to be in old space.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001640 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001641 }
1642
1643 target->set_context(*context);
1644 return *target;
1645}
1646
1647
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001648static Object* CharFromCode(Object* char_code) {
1649 uint32_t code;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001650 if (char_code->ToArrayIndex(&code)) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001651 if (code <= 0xffff) {
1652 return Heap::LookupSingleCharacterStringFromCode(code);
1653 }
1654 }
1655 return Heap::empty_string();
1656}
1657
1658
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001659static Object* Runtime_StringCharCodeAt(Arguments args) {
1660 NoHandleAllocation ha;
1661 ASSERT(args.length() == 2);
1662
1663 CONVERT_CHECKED(String, subject, args[0]);
1664 Object* index = args[1];
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001665 RUNTIME_ASSERT(index->IsNumber());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001666
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001667 uint32_t i = 0;
1668 if (index->IsSmi()) {
1669 int value = Smi::cast(index)->value();
1670 if (value < 0) return Heap::nan_value();
1671 i = value;
1672 } else {
1673 ASSERT(index->IsHeapNumber());
1674 double value = HeapNumber::cast(index)->value();
1675 i = static_cast<uint32_t>(DoubleToInteger(value));
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001676 }
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001677
1678 // Flatten the string. If someone wants to get a char at an index
1679 // in a cons string, it is likely that more indices will be
1680 // accessed.
1681 Object* flat = subject->TryFlatten();
1682 if (flat->IsFailure()) return flat;
1683 subject = String::cast(flat);
1684
1685 if (i >= static_cast<uint32_t>(subject->length())) {
1686 return Heap::nan_value();
1687 }
1688
1689 return Smi::FromInt(subject->Get(i));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001690}
1691
1692
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001693static Object* Runtime_CharFromCode(Arguments args) {
1694 NoHandleAllocation ha;
1695 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001696 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001697}
1698
lrn@chromium.org25156de2010-04-06 13:10:27 +00001699
1700class FixedArrayBuilder {
1701 public:
1702 explicit FixedArrayBuilder(int initial_capacity)
1703 : array_(Factory::NewFixedArrayWithHoles(initial_capacity)),
1704 length_(0) {
1705 // Require a non-zero initial size. Ensures that doubling the size to
1706 // extend the array will work.
1707 ASSERT(initial_capacity > 0);
1708 }
1709
1710 explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
1711 : array_(backing_store),
1712 length_(0) {
1713 // Require a non-zero initial size. Ensures that doubling the size to
1714 // extend the array will work.
1715 ASSERT(backing_store->length() > 0);
1716 }
1717
1718 bool HasCapacity(int elements) {
1719 int length = array_->length();
1720 int required_length = length_ + elements;
1721 return (length >= required_length);
1722 }
1723
1724 void EnsureCapacity(int elements) {
1725 int length = array_->length();
1726 int required_length = length_ + elements;
1727 if (length < required_length) {
1728 int new_length = length;
1729 do {
1730 new_length *= 2;
1731 } while (new_length < required_length);
1732 Handle<FixedArray> extended_array =
1733 Factory::NewFixedArrayWithHoles(new_length);
1734 array_->CopyTo(0, *extended_array, 0, length_);
1735 array_ = extended_array;
1736 }
1737 }
1738
1739 void Add(Object* value) {
1740 ASSERT(length_ < capacity());
1741 array_->set(length_, value);
1742 length_++;
1743 }
1744
1745 void Add(Smi* value) {
1746 ASSERT(length_ < capacity());
1747 array_->set(length_, value);
1748 length_++;
1749 }
1750
1751 Handle<FixedArray> array() {
1752 return array_;
1753 }
1754
1755 int length() {
1756 return length_;
1757 }
1758
1759 int capacity() {
1760 return array_->length();
1761 }
1762
1763 Handle<JSArray> ToJSArray() {
1764 Handle<JSArray> result_array = Factory::NewJSArrayWithElements(array_);
1765 result_array->set_length(Smi::FromInt(length_));
1766 return result_array;
1767 }
1768
1769 Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
1770 target_array->set_elements(*array_);
1771 target_array->set_length(Smi::FromInt(length_));
1772 return target_array;
1773 }
1774
1775 private:
1776 Handle<FixedArray> array_;
1777 int length_;
1778};
1779
1780
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001781// Forward declarations.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001782const int kStringBuilderConcatHelperLengthBits = 11;
1783const int kStringBuilderConcatHelperPositionBits = 19;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001784
1785template <typename schar>
1786static inline void StringBuilderConcatHelper(String*,
1787 schar*,
1788 FixedArray*,
1789 int);
1790
lrn@chromium.org25156de2010-04-06 13:10:27 +00001791typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
1792 StringBuilderSubstringLength;
1793typedef BitField<int,
1794 kStringBuilderConcatHelperLengthBits,
1795 kStringBuilderConcatHelperPositionBits>
1796 StringBuilderSubstringPosition;
1797
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001798
1799class ReplacementStringBuilder {
1800 public:
1801 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
lrn@chromium.org25156de2010-04-06 13:10:27 +00001802 : array_builder_(estimated_part_count),
1803 subject_(subject),
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001804 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001805 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001806 // Require a non-zero initial size. Ensures that doubling the size to
1807 // extend the array will work.
1808 ASSERT(estimated_part_count > 0);
1809 }
1810
lrn@chromium.org25156de2010-04-06 13:10:27 +00001811 static inline void AddSubjectSlice(FixedArrayBuilder* builder,
1812 int from,
1813 int to) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001814 ASSERT(from >= 0);
1815 int length = to - from;
1816 ASSERT(length > 0);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001817 if (StringBuilderSubstringLength::is_valid(length) &&
1818 StringBuilderSubstringPosition::is_valid(from)) {
1819 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1820 StringBuilderSubstringPosition::encode(from);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001821 builder->Add(Smi::FromInt(encoded_slice));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001822 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001823 // Otherwise encode as two smis.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001824 builder->Add(Smi::FromInt(-length));
1825 builder->Add(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001826 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00001827 }
1828
1829
1830 void EnsureCapacity(int elements) {
1831 array_builder_.EnsureCapacity(elements);
1832 }
1833
1834
1835 void AddSubjectSlice(int from, int to) {
1836 AddSubjectSlice(&array_builder_, from, to);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001837 IncrementCharacterCount(to - from);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001838 }
1839
1840
1841 void AddString(Handle<String> string) {
1842 int length = string->length();
1843 ASSERT(length > 0);
1844 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001845 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001846 is_ascii_ = false;
1847 }
1848 IncrementCharacterCount(length);
1849 }
1850
1851
1852 Handle<String> ToString() {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001853 if (array_builder_.length() == 0) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001854 return Factory::empty_string();
1855 }
1856
1857 Handle<String> joined_string;
1858 if (is_ascii_) {
1859 joined_string = NewRawAsciiString(character_count_);
1860 AssertNoAllocation no_alloc;
1861 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1862 char* char_buffer = seq->GetChars();
1863 StringBuilderConcatHelper(*subject_,
1864 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001865 *array_builder_.array(),
1866 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001867 } else {
1868 // Non-ASCII.
1869 joined_string = NewRawTwoByteString(character_count_);
1870 AssertNoAllocation no_alloc;
1871 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1872 uc16* char_buffer = seq->GetChars();
1873 StringBuilderConcatHelper(*subject_,
1874 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001875 *array_builder_.array(),
1876 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001877 }
1878 return joined_string;
1879 }
1880
1881
1882 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001883 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001884 V8::FatalProcessOutOfMemory("String.replace result too large.");
1885 }
1886 character_count_ += by;
1887 }
1888
lrn@chromium.org25156de2010-04-06 13:10:27 +00001889 Handle<JSArray> GetParts() {
1890 Handle<JSArray> result =
1891 Factory::NewJSArrayWithElements(array_builder_.array());
1892 result->set_length(Smi::FromInt(array_builder_.length()));
1893 return result;
1894 }
kmillikin@chromium.orgd9825192010-03-30 08:36:16 +00001895
lrn@chromium.org25156de2010-04-06 13:10:27 +00001896 private:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001897 Handle<String> NewRawAsciiString(int size) {
1898 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1899 }
1900
1901
1902 Handle<String> NewRawTwoByteString(int size) {
1903 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1904 }
1905
1906
1907 void AddElement(Object* element) {
1908 ASSERT(element->IsSmi() || element->IsString());
lrn@chromium.org25156de2010-04-06 13:10:27 +00001909 ASSERT(array_builder_.capacity() > array_builder_.length());
1910 array_builder_.Add(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001911 }
1912
lrn@chromium.org25156de2010-04-06 13:10:27 +00001913 FixedArrayBuilder array_builder_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001914 Handle<String> subject_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001915 int character_count_;
1916 bool is_ascii_;
1917};
1918
1919
1920class CompiledReplacement {
1921 public:
1922 CompiledReplacement()
1923 : parts_(1), replacement_substrings_(0) {}
1924
1925 void Compile(Handle<String> replacement,
1926 int capture_count,
1927 int subject_length);
1928
1929 void Apply(ReplacementStringBuilder* builder,
1930 int match_from,
1931 int match_to,
1932 Handle<JSArray> last_match_info);
1933
1934 // Number of distinct parts of the replacement pattern.
1935 int parts() {
1936 return parts_.length();
1937 }
1938 private:
1939 enum PartType {
1940 SUBJECT_PREFIX = 1,
1941 SUBJECT_SUFFIX,
1942 SUBJECT_CAPTURE,
1943 REPLACEMENT_SUBSTRING,
1944 REPLACEMENT_STRING,
1945
1946 NUMBER_OF_PART_TYPES
1947 };
1948
1949 struct ReplacementPart {
1950 static inline ReplacementPart SubjectMatch() {
1951 return ReplacementPart(SUBJECT_CAPTURE, 0);
1952 }
1953 static inline ReplacementPart SubjectCapture(int capture_index) {
1954 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1955 }
1956 static inline ReplacementPart SubjectPrefix() {
1957 return ReplacementPart(SUBJECT_PREFIX, 0);
1958 }
1959 static inline ReplacementPart SubjectSuffix(int subject_length) {
1960 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1961 }
1962 static inline ReplacementPart ReplacementString() {
1963 return ReplacementPart(REPLACEMENT_STRING, 0);
1964 }
1965 static inline ReplacementPart ReplacementSubString(int from, int to) {
1966 ASSERT(from >= 0);
1967 ASSERT(to > from);
1968 return ReplacementPart(-from, to);
1969 }
1970
1971 // If tag <= 0 then it is the negation of a start index of a substring of
1972 // the replacement pattern, otherwise it's a value from PartType.
1973 ReplacementPart(int tag, int data)
1974 : tag(tag), data(data) {
1975 // Must be non-positive or a PartType value.
1976 ASSERT(tag < NUMBER_OF_PART_TYPES);
1977 }
1978 // Either a value of PartType or a non-positive number that is
1979 // the negation of an index into the replacement string.
1980 int tag;
1981 // The data value's interpretation depends on the value of tag:
1982 // tag == SUBJECT_PREFIX ||
1983 // tag == SUBJECT_SUFFIX: data is unused.
1984 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1985 // tag == REPLACEMENT_SUBSTRING ||
1986 // tag == REPLACEMENT_STRING: data is index into array of substrings
1987 // of the replacement string.
1988 // tag <= 0: Temporary representation of the substring of the replacement
1989 // string ranging over -tag .. data.
1990 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1991 // substring objects.
1992 int data;
1993 };
1994
1995 template<typename Char>
1996 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1997 Vector<Char> characters,
1998 int capture_count,
1999 int subject_length) {
2000 int length = characters.length();
2001 int last = 0;
2002 for (int i = 0; i < length; i++) {
2003 Char c = characters[i];
2004 if (c == '$') {
2005 int next_index = i + 1;
2006 if (next_index == length) { // No next character!
2007 break;
2008 }
2009 Char c2 = characters[next_index];
2010 switch (c2) {
2011 case '$':
2012 if (i > last) {
2013 // There is a substring before. Include the first "$".
2014 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
2015 last = next_index + 1; // Continue after the second "$".
2016 } else {
2017 // Let the next substring start with the second "$".
2018 last = next_index;
2019 }
2020 i = next_index;
2021 break;
2022 case '`':
2023 if (i > last) {
2024 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2025 }
2026 parts->Add(ReplacementPart::SubjectPrefix());
2027 i = next_index;
2028 last = i + 1;
2029 break;
2030 case '\'':
2031 if (i > last) {
2032 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2033 }
2034 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
2035 i = next_index;
2036 last = i + 1;
2037 break;
2038 case '&':
2039 if (i > last) {
2040 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2041 }
2042 parts->Add(ReplacementPart::SubjectMatch());
2043 i = next_index;
2044 last = i + 1;
2045 break;
2046 case '0':
2047 case '1':
2048 case '2':
2049 case '3':
2050 case '4':
2051 case '5':
2052 case '6':
2053 case '7':
2054 case '8':
2055 case '9': {
2056 int capture_ref = c2 - '0';
2057 if (capture_ref > capture_count) {
2058 i = next_index;
2059 continue;
2060 }
2061 int second_digit_index = next_index + 1;
2062 if (second_digit_index < length) {
2063 // Peek ahead to see if we have two digits.
2064 Char c3 = characters[second_digit_index];
2065 if ('0' <= c3 && c3 <= '9') { // Double digits.
2066 int double_digit_ref = capture_ref * 10 + c3 - '0';
2067 if (double_digit_ref <= capture_count) {
2068 next_index = second_digit_index;
2069 capture_ref = double_digit_ref;
2070 }
2071 }
2072 }
2073 if (capture_ref > 0) {
2074 if (i > last) {
2075 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2076 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002077 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002078 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
2079 last = next_index + 1;
2080 }
2081 i = next_index;
2082 break;
2083 }
2084 default:
2085 i = next_index;
2086 break;
2087 }
2088 }
2089 }
2090 if (length > last) {
2091 if (last == 0) {
2092 parts->Add(ReplacementPart::ReplacementString());
2093 } else {
2094 parts->Add(ReplacementPart::ReplacementSubString(last, length));
2095 }
2096 }
2097 }
2098
2099 ZoneList<ReplacementPart> parts_;
2100 ZoneList<Handle<String> > replacement_substrings_;
2101};
2102
2103
2104void CompiledReplacement::Compile(Handle<String> replacement,
2105 int capture_count,
2106 int subject_length) {
2107 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00002108 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002109 AssertNoAllocation no_alloc;
2110 ParseReplacementPattern(&parts_,
2111 replacement->ToAsciiVector(),
2112 capture_count,
2113 subject_length);
2114 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00002115 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002116 AssertNoAllocation no_alloc;
2117
2118 ParseReplacementPattern(&parts_,
2119 replacement->ToUC16Vector(),
2120 capture_count,
2121 subject_length);
2122 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002123 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002124 int substring_index = 0;
2125 for (int i = 0, n = parts_.length(); i < n; i++) {
2126 int tag = parts_[i].tag;
2127 if (tag <= 0) { // A replacement string slice.
2128 int from = -tag;
2129 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002130 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002131 parts_[i].tag = REPLACEMENT_SUBSTRING;
2132 parts_[i].data = substring_index;
2133 substring_index++;
2134 } else if (tag == REPLACEMENT_STRING) {
2135 replacement_substrings_.Add(replacement);
2136 parts_[i].data = substring_index;
2137 substring_index++;
2138 }
2139 }
2140}
2141
2142
2143void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
2144 int match_from,
2145 int match_to,
2146 Handle<JSArray> last_match_info) {
2147 for (int i = 0, n = parts_.length(); i < n; i++) {
2148 ReplacementPart part = parts_[i];
2149 switch (part.tag) {
2150 case SUBJECT_PREFIX:
2151 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
2152 break;
2153 case SUBJECT_SUFFIX: {
2154 int subject_length = part.data;
2155 if (match_to < subject_length) {
2156 builder->AddSubjectSlice(match_to, subject_length);
2157 }
2158 break;
2159 }
2160 case SUBJECT_CAPTURE: {
2161 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002162 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002163 int from = RegExpImpl::GetCapture(match_info, capture * 2);
2164 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
2165 if (from >= 0 && to > from) {
2166 builder->AddSubjectSlice(from, to);
2167 }
2168 break;
2169 }
2170 case REPLACEMENT_SUBSTRING:
2171 case REPLACEMENT_STRING:
2172 builder->AddString(replacement_substrings_[part.data]);
2173 break;
2174 default:
2175 UNREACHABLE();
2176 }
2177 }
2178}
2179
2180
2181
2182static Object* StringReplaceRegExpWithString(String* subject,
2183 JSRegExp* regexp,
2184 String* replacement,
2185 JSArray* last_match_info) {
2186 ASSERT(subject->IsFlat());
2187 ASSERT(replacement->IsFlat());
2188
2189 HandleScope handles;
2190
2191 int length = subject->length();
2192 Handle<String> subject_handle(subject);
2193 Handle<JSRegExp> regexp_handle(regexp);
2194 Handle<String> replacement_handle(replacement);
2195 Handle<JSArray> last_match_info_handle(last_match_info);
2196 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2197 subject_handle,
2198 0,
2199 last_match_info_handle);
2200 if (match.is_null()) {
2201 return Failure::Exception();
2202 }
2203 if (match->IsNull()) {
2204 return *subject_handle;
2205 }
2206
2207 int capture_count = regexp_handle->CaptureCount();
2208
2209 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002210 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002211 CompiledReplacement compiled_replacement;
2212 compiled_replacement.Compile(replacement_handle,
2213 capture_count,
2214 length);
2215
2216 bool is_global = regexp_handle->GetFlags().is_global();
2217
2218 // Guessing the number of parts that the final result string is built
2219 // from. Global regexps can match any number of times, so we guess
2220 // conservatively.
2221 int expected_parts =
2222 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
2223 ReplacementStringBuilder builder(subject_handle, expected_parts);
2224
2225 // Index of end of last match.
2226 int prev = 0;
2227
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002228 // Number of parts added by compiled replacement plus preceeding
2229 // string and possibly suffix after last match. It is possible for
2230 // all components to use two elements when encoded as two smis.
2231 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002232 bool matched = true;
2233 do {
2234 ASSERT(last_match_info_handle->HasFastElements());
2235 // Increase the capacity of the builder before entering local handle-scope,
2236 // so its internal buffer can safely allocate a new handle if it grows.
2237 builder.EnsureCapacity(parts_added_per_loop);
2238
2239 HandleScope loop_scope;
2240 int start, end;
2241 {
2242 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002243 FixedArray* match_info_array =
2244 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002245
2246 ASSERT_EQ(capture_count * 2 + 2,
2247 RegExpImpl::GetLastCaptureCount(match_info_array));
2248 start = RegExpImpl::GetCapture(match_info_array, 0);
2249 end = RegExpImpl::GetCapture(match_info_array, 1);
2250 }
2251
2252 if (prev < start) {
2253 builder.AddSubjectSlice(prev, start);
2254 }
2255 compiled_replacement.Apply(&builder,
2256 start,
2257 end,
2258 last_match_info_handle);
2259 prev = end;
2260
2261 // Only continue checking for global regexps.
2262 if (!is_global) break;
2263
2264 // Continue from where the match ended, unless it was an empty match.
2265 int next = end;
2266 if (start == end) {
2267 next = end + 1;
2268 if (next > length) break;
2269 }
2270
2271 match = RegExpImpl::Exec(regexp_handle,
2272 subject_handle,
2273 next,
2274 last_match_info_handle);
2275 if (match.is_null()) {
2276 return Failure::Exception();
2277 }
2278 matched = !match->IsNull();
2279 } while (matched);
2280
2281 if (prev < length) {
2282 builder.AddSubjectSlice(prev, length);
2283 }
2284
2285 return *(builder.ToString());
2286}
2287
2288
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00002289template <typename ResultSeqString>
2290static Object* StringReplaceRegExpWithEmptyString(String* subject,
2291 JSRegExp* regexp,
2292 JSArray* last_match_info) {
2293 ASSERT(subject->IsFlat());
2294
2295 HandleScope handles;
2296
2297 Handle<String> subject_handle(subject);
2298 Handle<JSRegExp> regexp_handle(regexp);
2299 Handle<JSArray> last_match_info_handle(last_match_info);
2300 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2301 subject_handle,
2302 0,
2303 last_match_info_handle);
2304 if (match.is_null()) return Failure::Exception();
2305 if (match->IsNull()) return *subject_handle;
2306
2307 ASSERT(last_match_info_handle->HasFastElements());
2308
2309 HandleScope loop_scope;
2310 int start, end;
2311 {
2312 AssertNoAllocation match_info_array_is_not_in_a_handle;
2313 FixedArray* match_info_array =
2314 FixedArray::cast(last_match_info_handle->elements());
2315
2316 start = RegExpImpl::GetCapture(match_info_array, 0);
2317 end = RegExpImpl::GetCapture(match_info_array, 1);
2318 }
2319
2320 int length = subject->length();
2321 int new_length = length - (end - start);
2322 if (new_length == 0) {
2323 return Heap::empty_string();
2324 }
2325 Handle<ResultSeqString> answer;
2326 if (ResultSeqString::kHasAsciiEncoding) {
2327 answer =
2328 Handle<ResultSeqString>::cast(Factory::NewRawAsciiString(new_length));
2329 } else {
2330 answer =
2331 Handle<ResultSeqString>::cast(Factory::NewRawTwoByteString(new_length));
2332 }
2333
2334 // If the regexp isn't global, only match once.
2335 if (!regexp_handle->GetFlags().is_global()) {
2336 if (start > 0) {
2337 String::WriteToFlat(*subject_handle,
2338 answer->GetChars(),
2339 0,
2340 start);
2341 }
2342 if (end < length) {
2343 String::WriteToFlat(*subject_handle,
2344 answer->GetChars() + start,
2345 end,
2346 length);
2347 }
2348 return *answer;
2349 }
2350
2351 int prev = 0; // Index of end of last match.
2352 int next = 0; // Start of next search (prev unless last match was empty).
2353 int position = 0;
2354
2355 do {
2356 if (prev < start) {
2357 // Add substring subject[prev;start] to answer string.
2358 String::WriteToFlat(*subject_handle,
2359 answer->GetChars() + position,
2360 prev,
2361 start);
2362 position += start - prev;
2363 }
2364 prev = end;
2365 next = end;
2366 // Continue from where the match ended, unless it was an empty match.
2367 if (start == end) {
2368 next++;
2369 if (next > length) break;
2370 }
2371 match = RegExpImpl::Exec(regexp_handle,
2372 subject_handle,
2373 next,
2374 last_match_info_handle);
2375 if (match.is_null()) return Failure::Exception();
2376 if (match->IsNull()) break;
2377
2378 ASSERT(last_match_info_handle->HasFastElements());
2379 HandleScope loop_scope;
2380 {
2381 AssertNoAllocation match_info_array_is_not_in_a_handle;
2382 FixedArray* match_info_array =
2383 FixedArray::cast(last_match_info_handle->elements());
2384 start = RegExpImpl::GetCapture(match_info_array, 0);
2385 end = RegExpImpl::GetCapture(match_info_array, 1);
2386 }
2387 } while (true);
2388
2389 if (prev < length) {
2390 // Add substring subject[prev;length] to answer string.
2391 String::WriteToFlat(*subject_handle,
2392 answer->GetChars() + position,
2393 prev,
2394 length);
2395 position += length - prev;
2396 }
2397
2398 if (position == 0) {
2399 return Heap::empty_string();
2400 }
2401
2402 // Shorten string and fill
2403 int string_size = ResultSeqString::SizeFor(position);
2404 int allocated_string_size = ResultSeqString::SizeFor(new_length);
2405 int delta = allocated_string_size - string_size;
2406
2407 answer->set_length(position);
2408 if (delta == 0) return *answer;
2409
2410 Address end_of_string = answer->address() + string_size;
2411 Heap::CreateFillerObjectAt(end_of_string, delta);
2412
2413 return *answer;
2414}
2415
2416
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002417static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2418 ASSERT(args.length() == 4);
2419
2420 CONVERT_CHECKED(String, subject, args[0]);
2421 if (!subject->IsFlat()) {
2422 Object* flat_subject = subject->TryFlatten();
2423 if (flat_subject->IsFailure()) {
2424 return flat_subject;
2425 }
2426 subject = String::cast(flat_subject);
2427 }
2428
2429 CONVERT_CHECKED(String, replacement, args[2]);
2430 if (!replacement->IsFlat()) {
2431 Object* flat_replacement = replacement->TryFlatten();
2432 if (flat_replacement->IsFailure()) {
2433 return flat_replacement;
2434 }
2435 replacement = String::cast(flat_replacement);
2436 }
2437
2438 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2439 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2440
2441 ASSERT(last_match_info->HasFastElements());
2442
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00002443 if (replacement->length() == 0) {
2444 if (subject->HasOnlyAsciiChars()) {
2445 return StringReplaceRegExpWithEmptyString<SeqAsciiString>(
2446 subject, regexp, last_match_info);
2447 } else {
2448 return StringReplaceRegExpWithEmptyString<SeqTwoByteString>(
2449 subject, regexp, last_match_info);
2450 }
2451 }
2452
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002453 return StringReplaceRegExpWithString(subject,
2454 regexp,
2455 replacement,
2456 last_match_info);
2457}
2458
2459
ager@chromium.org7c537e22008-10-16 08:43:32 +00002460// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2461// limit, we can fix the size of tables.
2462static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002463// Reduce alphabet to this size.
2464static const int kBMAlphabetSize = 0x100;
2465// For patterns below this length, the skip length of Boyer-Moore is too short
2466// to compensate for the algorithmic overhead compared to simple brute force.
2467static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002468
ager@chromium.org7c537e22008-10-16 08:43:32 +00002469// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2470// shift. Only allows the last kBMMaxShift characters of the needle
2471// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002472class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002473 public:
2474 BMGoodSuffixBuffers() {}
2475 inline void init(int needle_length) {
2476 ASSERT(needle_length > 1);
2477 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2478 int len = needle_length - start;
2479 biased_suffixes_ = suffixes_ - start;
2480 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2481 for (int i = 0; i <= len; i++) {
2482 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002483 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002484 }
2485 inline int& suffix(int index) {
2486 ASSERT(biased_suffixes_ + index >= suffixes_);
2487 return biased_suffixes_[index];
2488 }
2489 inline int& shift(int index) {
2490 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2491 return biased_good_suffix_shift_[index];
2492 }
2493 private:
2494 int suffixes_[kBMMaxShift + 1];
2495 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002496 int* biased_suffixes_;
2497 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002498 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2499};
2500
2501// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002502static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002503static BMGoodSuffixBuffers bmgs_buffers;
2504
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002505// State of the string match tables.
2506// SIMPLE: No usable content in the buffers.
2507// BOYER_MOORE_HORSPOOL: The bad_char_occurences table has been populated.
2508// BOYER_MOORE: The bmgs_buffers tables have also been populated.
2509// Whenever starting with a new needle, one should call InitializeStringSearch
2510// to determine which search strategy to use, and in the case of a long-needle
2511// strategy, the call also initializes the algorithm to SIMPLE.
2512enum StringSearchAlgorithm { SIMPLE_SEARCH, BOYER_MOORE_HORSPOOL, BOYER_MOORE };
2513static StringSearchAlgorithm algorithm;
2514
2515
ager@chromium.org7c537e22008-10-16 08:43:32 +00002516// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002517template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002518static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern) {
2519 // Only preprocess at most kBMMaxShift last characters of pattern.
2520 int start = pattern.length() < kBMMaxShift ? 0
2521 : pattern.length() - kBMMaxShift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002522 // Run forwards to populate bad_char_table, so that *last* instance
2523 // of character equivalence class is the one registered.
2524 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002525 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2526 : kBMAlphabetSize;
2527 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002528 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002529 } else {
2530 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002531 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002532 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002533 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002534 for (int i = start; i < pattern.length() - 1; i++) {
2535 pchar c = pattern[i];
2536 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002537 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002538 }
2539}
2540
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002541
ager@chromium.org7c537e22008-10-16 08:43:32 +00002542template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002543static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002544 int m = pattern.length();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002545 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002546 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002547 // Compute Good Suffix tables.
2548 bmgs_buffers.init(m);
2549
2550 bmgs_buffers.shift(m-1) = 1;
2551 bmgs_buffers.suffix(m) = m + 1;
2552 pchar last_char = pattern[m - 1];
2553 int suffix = m + 1;
2554 for (int i = m; i > start;) {
2555 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2556 if (bmgs_buffers.shift(suffix) == len) {
2557 bmgs_buffers.shift(suffix) = suffix - i;
2558 }
2559 suffix = bmgs_buffers.suffix(suffix);
2560 }
2561 i--;
2562 suffix--;
2563 bmgs_buffers.suffix(i) = suffix;
2564 if (suffix == m) {
2565 // No suffix to extend, so we check against last_char only.
2566 while (i > start && pattern[i - 1] != last_char) {
2567 if (bmgs_buffers.shift(m) == len) {
2568 bmgs_buffers.shift(m) = m - i;
2569 }
2570 i--;
2571 bmgs_buffers.suffix(i) = m;
2572 }
2573 if (i > start) {
2574 i--;
2575 suffix--;
2576 bmgs_buffers.suffix(i) = suffix;
2577 }
2578 }
2579 }
2580 if (suffix < m) {
2581 for (int i = start; i <= m; i++) {
2582 if (bmgs_buffers.shift(i) == len) {
2583 bmgs_buffers.shift(i) = suffix - start;
2584 }
2585 if (i == suffix) {
2586 suffix = bmgs_buffers.suffix(suffix);
2587 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002588 }
2589 }
2590}
2591
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002592
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002593template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002594static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002595 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002596 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002597 }
2598 if (sizeof(pchar) == 1) {
2599 if (char_code > String::kMaxAsciiCharCode) {
2600 return -1;
2601 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002602 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002603 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002604 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002605}
2606
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002607
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002608// Restricted simplified Boyer-Moore string matching.
2609// Uses only the bad-shift table of Boyer-Moore and only uses it
2610// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002611template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002612static int BoyerMooreHorspool(Vector<const schar> subject,
2613 Vector<const pchar> pattern,
2614 int start_index,
2615 bool* complete) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002616 ASSERT(algorithm <= BOYER_MOORE_HORSPOOL);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002617 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002618 int m = pattern.length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002619
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002620 int badness = -m;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002621
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002622 // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002623 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002624 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002625 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002626 // Perform search
2627 for (idx = start_index; idx <= n - m;) {
2628 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002629 int c;
2630 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002631 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002632 int shift = j - bc_occ;
2633 idx += shift;
2634 badness += 1 - shift; // at most zero, so badness cannot increase.
2635 if (idx > n - m) {
2636 *complete = true;
2637 return -1;
2638 }
2639 }
2640 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002641 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002642 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002643 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002644 return idx;
2645 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002646 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002647 // Badness increases by the number of characters we have
2648 // checked, and decreases by the number of characters we
2649 // can skip by shifting. It's a measure of how we are doing
2650 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002651 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002652 if (badness > 0) {
2653 *complete = false;
2654 return idx;
2655 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002656 }
2657 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002658 *complete = true;
2659 return -1;
2660}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002661
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002662
2663template <typename schar, typename pchar>
2664static int BoyerMooreIndexOf(Vector<const schar> subject,
2665 Vector<const pchar> pattern,
2666 int idx) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002667 ASSERT(algorithm <= BOYER_MOORE);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002668 int n = subject.length();
2669 int m = pattern.length();
2670 // Only preprocess at most kBMMaxShift last characters of pattern.
2671 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2672
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002673 pchar last_char = pattern[m - 1];
2674 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002675 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002676 int j = m - 1;
2677 schar c;
2678 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002679 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002680 idx += shift;
2681 if (idx > n - m) {
2682 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002683 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002684 }
2685 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2686 if (j < 0) {
2687 return idx;
2688 } else if (j < start) {
2689 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002690 // Fall back on BMH shift.
2691 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002692 } else {
2693 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002694 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002695 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002696 if (gs_shift > shift) {
2697 shift = gs_shift;
2698 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002699 idx += shift;
2700 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002701 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002702
2703 return -1;
2704}
2705
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002706
2707template <typename schar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002708static inline int SingleCharIndexOf(Vector<const schar> string,
2709 schar pattern_char,
2710 int start_index) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002711 if (sizeof(schar) == 1) {
2712 const schar* pos = reinterpret_cast<const schar*>(
2713 memchr(string.start() + start_index,
2714 pattern_char,
2715 string.length() - start_index));
2716 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002717 return static_cast<int>(pos - string.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002718 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002719 for (int i = start_index, n = string.length(); i < n; i++) {
2720 if (pattern_char == string[i]) {
2721 return i;
2722 }
2723 }
2724 return -1;
2725}
2726
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002727
2728template <typename schar>
2729static int SingleCharLastIndexOf(Vector<const schar> string,
2730 schar pattern_char,
2731 int start_index) {
2732 for (int i = start_index; i >= 0; i--) {
2733 if (pattern_char == string[i]) {
2734 return i;
2735 }
2736 }
2737 return -1;
2738}
2739
2740
ager@chromium.org7c537e22008-10-16 08:43:32 +00002741// Trivial string search for shorter strings.
2742// On return, if "complete" is set to true, the return value is the
2743// final result of searching for the patter in the subject.
2744// If "complete" is set to false, the return value is the index where
2745// further checking should start, i.e., it's guaranteed that the pattern
2746// does not occur at a position prior to the returned index.
2747template <typename pchar, typename schar>
2748static int SimpleIndexOf(Vector<const schar> subject,
2749 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002750 int idx,
2751 bool* complete) {
2752 // Badness is a count of how much work we have done. When we have
2753 // done enough work we decide it's probably worth switching to a better
2754 // algorithm.
2755 int badness = -10 - (pattern.length() << 2);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002756
ager@chromium.org7c537e22008-10-16 08:43:32 +00002757 // We know our pattern is at least 2 characters, we cache the first so
2758 // the common case of the first character not matching is faster.
2759 pchar pattern_first_char = pattern[0];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002760 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2761 badness++;
2762 if (badness > 0) {
2763 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002764 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002765 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002766 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2767 const schar* pos = reinterpret_cast<const schar*>(
2768 memchr(subject.start() + i,
2769 pattern_first_char,
2770 n - i + 1));
2771 if (pos == NULL) {
2772 *complete = true;
2773 return -1;
2774 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002775 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002776 } else {
2777 if (subject[i] != pattern_first_char) continue;
2778 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002779 int j = 1;
2780 do {
2781 if (pattern[j] != subject[i+j]) {
2782 break;
2783 }
2784 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002785 } while (j < pattern.length());
2786 if (j == pattern.length()) {
2787 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002788 return i;
2789 }
2790 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002791 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002792 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002793 return -1;
2794}
2795
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002796// Simple indexOf that never bails out. For short patterns only.
2797template <typename pchar, typename schar>
2798static int SimpleIndexOf(Vector<const schar> subject,
2799 Vector<const pchar> pattern,
2800 int idx) {
2801 pchar pattern_first_char = pattern[0];
2802 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002803 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2804 const schar* pos = reinterpret_cast<const schar*>(
2805 memchr(subject.start() + i,
2806 pattern_first_char,
2807 n - i + 1));
2808 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002809 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002810 } else {
2811 if (subject[i] != pattern_first_char) continue;
2812 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002813 int j = 1;
2814 do {
2815 if (pattern[j] != subject[i+j]) {
2816 break;
2817 }
2818 j++;
2819 } while (j < pattern.length());
2820 if (j == pattern.length()) {
2821 return i;
2822 }
2823 }
2824 return -1;
2825}
2826
2827
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002828// Strategy for searching for a string in another string.
2829enum StringSearchStrategy { SEARCH_FAIL, SEARCH_SHORT, SEARCH_LONG };
ager@chromium.org7c537e22008-10-16 08:43:32 +00002830
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002831
2832template <typename pchar>
2833static inline StringSearchStrategy InitializeStringSearch(
2834 Vector<const pchar> pat, bool ascii_subject) {
2835 ASSERT(pat.length() > 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002836 // We have an ASCII haystack and a non-ASCII needle. Check if there
2837 // really is a non-ASCII character in the needle and bail out if there
2838 // is.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002839 if (ascii_subject && sizeof(pchar) > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002840 for (int i = 0; i < pat.length(); i++) {
2841 uc16 c = pat[i];
2842 if (c > String::kMaxAsciiCharCode) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002843 return SEARCH_FAIL;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002844 }
2845 }
2846 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002847 if (pat.length() < kBMMinPatternLength) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002848 return SEARCH_SHORT;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002849 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002850 algorithm = SIMPLE_SEARCH;
2851 return SEARCH_LONG;
2852}
2853
2854
2855// Dispatch long needle searches to different algorithms.
2856template <typename schar, typename pchar>
2857static int ComplexIndexOf(Vector<const schar> sub,
2858 Vector<const pchar> pat,
2859 int start_index) {
2860 ASSERT(pat.length() >= kBMMinPatternLength);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002861 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002862 bool complete;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002863 int idx = start_index;
2864 switch (algorithm) {
2865 case SIMPLE_SEARCH:
2866 idx = SimpleIndexOf(sub, pat, idx, &complete);
2867 if (complete) return idx;
2868 BoyerMoorePopulateBadCharTable(pat);
2869 algorithm = BOYER_MOORE_HORSPOOL;
2870 // FALLTHROUGH.
2871 case BOYER_MOORE_HORSPOOL:
2872 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
2873 if (complete) return idx;
2874 // Build the Good Suffix table and continue searching.
2875 BoyerMoorePopulateGoodSuffixTable(pat);
2876 algorithm = BOYER_MOORE;
2877 // FALLTHROUGH.
2878 case BOYER_MOORE:
2879 return BoyerMooreIndexOf(sub, pat, idx);
2880 }
2881 UNREACHABLE();
2882 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002883}
2884
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002885
2886// Dispatch to different search strategies for a single search.
2887// If searching multiple times on the same needle, the search
2888// strategy should only be computed once and then dispatch to different
2889// loops.
2890template <typename schar, typename pchar>
2891static int StringSearch(Vector<const schar> sub,
2892 Vector<const pchar> pat,
2893 int start_index) {
2894 bool ascii_subject = (sizeof(schar) == 1);
2895 StringSearchStrategy strategy = InitializeStringSearch(pat, ascii_subject);
2896 switch (strategy) {
2897 case SEARCH_FAIL: return -1;
2898 case SEARCH_SHORT: return SimpleIndexOf(sub, pat, start_index);
2899 case SEARCH_LONG: return ComplexIndexOf(sub, pat, start_index);
2900 }
2901 UNREACHABLE();
2902 return -1;
2903}
2904
2905
ager@chromium.org7c537e22008-10-16 08:43:32 +00002906// Perform string match of pattern on subject, starting at start index.
2907// Caller must ensure that 0 <= start_index <= sub->length(),
2908// and should check that pat->length() + start_index <= sub->length()
2909int Runtime::StringMatch(Handle<String> sub,
2910 Handle<String> pat,
2911 int start_index) {
2912 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002913 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002914
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002915 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002916 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002917
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002918 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002919 if (start_index + pattern_length > subject_length) return -1;
2920
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002921 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002922 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002923 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002924
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002925 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002926 // character patterns linear search is necessary, so any smart
2927 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002928 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002929 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002930 String* seq_sub = *sub;
2931 if (seq_sub->IsConsString()) {
2932 seq_sub = ConsString::cast(seq_sub)->first();
2933 }
2934 if (seq_sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002935 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002936 if (pchar > String::kMaxAsciiCharCode) {
2937 return -1;
2938 }
2939 Vector<const char> ascii_vector =
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002940 seq_sub->ToAsciiVector().SubVector(start_index, subject_length);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002941 const void* pos = memchr(ascii_vector.start(),
2942 static_cast<const char>(pchar),
2943 static_cast<size_t>(ascii_vector.length()));
2944 if (pos == NULL) {
2945 return -1;
2946 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002947 return static_cast<int>(reinterpret_cast<const char*>(pos)
2948 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002949 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002950 return SingleCharIndexOf(seq_sub->ToUC16Vector(),
2951 pat->Get(0),
2952 start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002953 }
2954
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002955 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002956 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002957 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002958
ager@chromium.org7c537e22008-10-16 08:43:32 +00002959 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002960 // Extract flattened substrings of cons strings before determining asciiness.
2961 String* seq_sub = *sub;
2962 if (seq_sub->IsConsString()) {
2963 seq_sub = ConsString::cast(seq_sub)->first();
2964 }
2965 String* seq_pat = *pat;
2966 if (seq_pat->IsConsString()) {
2967 seq_pat = ConsString::cast(seq_pat)->first();
2968 }
2969
ager@chromium.org7c537e22008-10-16 08:43:32 +00002970 // dispatch on type of strings
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002971 if (seq_pat->IsAsciiRepresentation()) {
2972 Vector<const char> pat_vector = seq_pat->ToAsciiVector();
2973 if (seq_sub->IsAsciiRepresentation()) {
2974 return StringSearch(seq_sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002975 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002976 return StringSearch(seq_sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002977 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002978 Vector<const uc16> pat_vector = seq_pat->ToUC16Vector();
2979 if (seq_sub->IsAsciiRepresentation()) {
2980 return StringSearch(seq_sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002981 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002982 return StringSearch(seq_sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002983}
2984
2985
2986static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002987 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002988 ASSERT(args.length() == 3);
2989
ager@chromium.org7c537e22008-10-16 08:43:32 +00002990 CONVERT_ARG_CHECKED(String, sub, 0);
2991 CONVERT_ARG_CHECKED(String, pat, 1);
2992
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002993 Object* index = args[2];
2994 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002995 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002996
ager@chromium.org870a0b62008-11-04 11:43:05 +00002997 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002998 int position = Runtime::StringMatch(sub, pat, start_index);
2999 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003000}
3001
3002
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003003template <typename schar, typename pchar>
3004static int StringMatchBackwards(Vector<const schar> sub,
3005 Vector<const pchar> pat,
3006 int idx) {
3007 ASSERT(pat.length() >= 1);
3008 ASSERT(idx + pat.length() <= sub.length());
3009
3010 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
3011 for (int i = 0; i < pat.length(); i++) {
3012 uc16 c = pat[i];
3013 if (c > String::kMaxAsciiCharCode) {
3014 return -1;
3015 }
3016 }
3017 }
3018
3019 pchar pattern_first_char = pat[0];
3020 for (int i = idx; i >= 0; i--) {
3021 if (sub[i] != pattern_first_char) continue;
3022 int j = 1;
3023 while (j < pat.length()) {
3024 if (pat[j] != sub[i+j]) {
3025 break;
3026 }
3027 j++;
3028 }
3029 if (j == pat.length()) {
3030 return i;
3031 }
3032 }
3033 return -1;
3034}
3035
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003036static Object* Runtime_StringLastIndexOf(Arguments args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003037 HandleScope scope; // create a new handle scope
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003038 ASSERT(args.length() == 3);
3039
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003040 CONVERT_ARG_CHECKED(String, sub, 0);
3041 CONVERT_ARG_CHECKED(String, pat, 1);
3042
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003043 Object* index = args[2];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003044 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003045 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003046
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003047 uint32_t pat_length = pat->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003048 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003049
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003050 if (start_index + pat_length > sub_length) {
3051 start_index = sub_length - pat_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00003052 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003053
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003054 if (pat_length == 0) {
3055 return Smi::FromInt(start_index);
3056 }
3057
3058 if (!sub->IsFlat()) {
3059 FlattenString(sub);
3060 }
3061
3062 if (pat_length == 1) {
3063 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
3064 if (sub->IsAsciiRepresentation()) {
3065 uc16 pchar = pat->Get(0);
3066 if (pchar > String::kMaxAsciiCharCode) {
3067 return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003068 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003069 return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
3070 static_cast<char>(pat->Get(0)),
3071 start_index));
3072 } else {
3073 return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
3074 pat->Get(0),
3075 start_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003076 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003077 }
3078
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003079 if (!pat->IsFlat()) {
3080 FlattenString(pat);
3081 }
3082
3083 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
3084
3085 int position = -1;
3086
3087 if (pat->IsAsciiRepresentation()) {
3088 Vector<const char> pat_vector = pat->ToAsciiVector();
3089 if (sub->IsAsciiRepresentation()) {
3090 position = StringMatchBackwards(sub->ToAsciiVector(),
3091 pat_vector,
3092 start_index);
3093 } else {
3094 position = StringMatchBackwards(sub->ToUC16Vector(),
3095 pat_vector,
3096 start_index);
3097 }
3098 } else {
3099 Vector<const uc16> pat_vector = pat->ToUC16Vector();
3100 if (sub->IsAsciiRepresentation()) {
3101 position = StringMatchBackwards(sub->ToAsciiVector(),
3102 pat_vector,
3103 start_index);
3104 } else {
3105 position = StringMatchBackwards(sub->ToUC16Vector(),
3106 pat_vector,
3107 start_index);
3108 }
3109 }
3110
3111 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003112}
3113
3114
3115static Object* Runtime_StringLocaleCompare(Arguments args) {
3116 NoHandleAllocation ha;
3117 ASSERT(args.length() == 2);
3118
3119 CONVERT_CHECKED(String, str1, args[0]);
3120 CONVERT_CHECKED(String, str2, args[1]);
3121
3122 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003123 int str1_length = str1->length();
3124 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003125
3126 // Decide trivial cases without flattening.
3127 if (str1_length == 0) {
3128 if (str2_length == 0) return Smi::FromInt(0); // Equal.
3129 return Smi::FromInt(-str2_length);
3130 } else {
3131 if (str2_length == 0) return Smi::FromInt(str1_length);
3132 }
3133
3134 int end = str1_length < str2_length ? str1_length : str2_length;
3135
3136 // No need to flatten if we are going to find the answer on the first
3137 // character. At this point we know there is at least one character
3138 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003139 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003140 if (d != 0) return Smi::FromInt(d);
3141
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003142 str1->TryFlatten();
3143 str2->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003144
3145 static StringInputBuffer buf1;
3146 static StringInputBuffer buf2;
3147
3148 buf1.Reset(str1);
3149 buf2.Reset(str2);
3150
3151 for (int i = 0; i < end; i++) {
3152 uint16_t char1 = buf1.GetNext();
3153 uint16_t char2 = buf2.GetNext();
3154 if (char1 != char2) return Smi::FromInt(char1 - char2);
3155 }
3156
3157 return Smi::FromInt(str1_length - str2_length);
3158}
3159
3160
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003161static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003162 NoHandleAllocation ha;
3163 ASSERT(args.length() == 3);
3164
3165 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00003166 Object* from = args[1];
3167 Object* to = args[2];
3168 int start, end;
3169 // We have a fast integer-only case here to avoid a conversion to double in
3170 // the common case where from and to are Smis.
3171 if (from->IsSmi() && to->IsSmi()) {
3172 start = Smi::cast(from)->value();
3173 end = Smi::cast(to)->value();
3174 } else {
3175 CONVERT_DOUBLE_CHECKED(from_number, from);
3176 CONVERT_DOUBLE_CHECKED(to_number, to);
3177 start = FastD2I(from_number);
3178 end = FastD2I(to_number);
3179 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003180 RUNTIME_ASSERT(end >= start);
3181 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003182 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003183 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003184 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003185}
3186
3187
ager@chromium.org41826e72009-03-30 13:30:57 +00003188static Object* Runtime_StringMatch(Arguments args) {
3189 ASSERT_EQ(3, args.length());
3190
3191 CONVERT_ARG_CHECKED(String, subject, 0);
3192 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
3193 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
3194 HandleScope handles;
3195
3196 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
3197
3198 if (match.is_null()) {
3199 return Failure::Exception();
3200 }
3201 if (match->IsNull()) {
3202 return Heap::null_value();
3203 }
3204 int length = subject->length();
3205
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00003206 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00003207 ZoneList<int> offsets(8);
3208 do {
3209 int start;
3210 int end;
3211 {
3212 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003213 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00003214 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
3215 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
3216 }
3217 offsets.Add(start);
3218 offsets.Add(end);
3219 int index = start < end ? end : end + 1;
3220 if (index > length) break;
3221 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
3222 if (match.is_null()) {
3223 return Failure::Exception();
3224 }
3225 } while (!match->IsNull());
3226 int matches = offsets.length() / 2;
3227 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
3228 for (int i = 0; i < matches ; i++) {
3229 int from = offsets.at(i * 2);
3230 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003231 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00003232 }
3233 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
3234 result->set_length(Smi::FromInt(matches));
3235 return *result;
3236}
3237
3238
lrn@chromium.org25156de2010-04-06 13:10:27 +00003239// Two smis before and after the match, for very long strings.
3240const int kMaxBuilderEntriesPerRegExpMatch = 5;
3241
3242
3243static void SetLastMatchInfoNoCaptures(Handle<String> subject,
3244 Handle<JSArray> last_match_info,
3245 int match_start,
3246 int match_end) {
3247 // Fill last_match_info with a single capture.
3248 last_match_info->EnsureSize(2 + RegExpImpl::kLastMatchOverhead);
3249 AssertNoAllocation no_gc;
3250 FixedArray* elements = FixedArray::cast(last_match_info->elements());
3251 RegExpImpl::SetLastCaptureCount(elements, 2);
3252 RegExpImpl::SetLastInput(elements, *subject);
3253 RegExpImpl::SetLastSubject(elements, *subject);
3254 RegExpImpl::SetCapture(elements, 0, match_start);
3255 RegExpImpl::SetCapture(elements, 1, match_end);
3256}
3257
3258
3259template <typename schar>
3260static bool SearchCharMultiple(Vector<schar> subject,
3261 String* pattern,
3262 schar pattern_char,
3263 FixedArrayBuilder* builder,
3264 int* match_pos) {
3265 // Position of last match.
3266 int pos = *match_pos;
3267 int subject_length = subject.length();
3268 while (pos < subject_length) {
3269 int match_end = pos + 1;
3270 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3271 *match_pos = pos;
3272 return false;
3273 }
3274 int new_pos = SingleCharIndexOf(subject, pattern_char, match_end);
3275 if (new_pos >= 0) {
3276 // Match has been found.
3277 if (new_pos > match_end) {
3278 ReplacementStringBuilder::AddSubjectSlice(builder, match_end, new_pos);
3279 }
3280 pos = new_pos;
3281 builder->Add(pattern);
3282 } else {
3283 break;
3284 }
3285 }
3286 if (pos + 1 < subject_length) {
3287 ReplacementStringBuilder::AddSubjectSlice(builder, pos + 1, subject_length);
3288 }
3289 *match_pos = pos;
3290 return true;
3291}
3292
3293
3294static bool SearchCharMultiple(Handle<String> subject,
3295 Handle<String> pattern,
3296 Handle<JSArray> last_match_info,
3297 FixedArrayBuilder* builder) {
3298 ASSERT(subject->IsFlat());
3299 ASSERT_EQ(1, pattern->length());
3300 uc16 pattern_char = pattern->Get(0);
3301 // Treating position before first as initial "previous match position".
3302 int match_pos = -1;
3303
3304 for (;;) { // Break when search complete.
3305 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3306 AssertNoAllocation no_gc;
3307 if (subject->IsAsciiRepresentation()) {
3308 if (pattern_char > String::kMaxAsciiCharCode) {
3309 break;
3310 }
3311 Vector<const char> subject_vector = subject->ToAsciiVector();
3312 char pattern_ascii_char = static_cast<char>(pattern_char);
3313 bool complete = SearchCharMultiple<const char>(subject_vector,
3314 *pattern,
3315 pattern_ascii_char,
3316 builder,
3317 &match_pos);
3318 if (complete) break;
3319 } else {
3320 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3321 bool complete = SearchCharMultiple<const uc16>(subject_vector,
3322 *pattern,
3323 pattern_char,
3324 builder,
3325 &match_pos);
3326 if (complete) break;
3327 }
3328 }
3329
3330 if (match_pos >= 0) {
3331 SetLastMatchInfoNoCaptures(subject,
3332 last_match_info,
3333 match_pos,
3334 match_pos + 1);
3335 return true;
3336 }
3337 return false; // No matches at all.
3338}
3339
3340
3341template <typename schar, typename pchar>
3342static bool SearchStringMultiple(Vector<schar> subject,
3343 String* pattern,
3344 Vector<pchar> pattern_string,
3345 FixedArrayBuilder* builder,
3346 int* match_pos) {
3347 int pos = *match_pos;
3348 int subject_length = subject.length();
3349 int pattern_length = pattern_string.length();
3350 int max_search_start = subject_length - pattern_length;
3351 bool is_ascii = (sizeof(schar) == 1);
3352 StringSearchStrategy strategy =
3353 InitializeStringSearch(pattern_string, is_ascii);
3354 switch (strategy) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003355 case SEARCH_FAIL: break;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003356 case SEARCH_SHORT:
3357 while (pos <= max_search_start) {
3358 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3359 *match_pos = pos;
3360 return false;
3361 }
3362 // Position of end of previous match.
3363 int match_end = pos + pattern_length;
3364 int new_pos = SimpleIndexOf(subject, pattern_string, match_end);
3365 if (new_pos >= 0) {
3366 // A match.
3367 if (new_pos > match_end) {
3368 ReplacementStringBuilder::AddSubjectSlice(builder,
3369 match_end,
3370 new_pos);
3371 }
3372 pos = new_pos;
3373 builder->Add(pattern);
3374 } else {
3375 break;
3376 }
3377 }
3378 break;
3379 case SEARCH_LONG:
3380 while (pos <= max_search_start) {
3381 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003382 *match_pos = pos;
3383 return false;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003384 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003385 int match_end = pos + pattern_length;
3386 int new_pos = ComplexIndexOf(subject, pattern_string, match_end);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003387 if (new_pos >= 0) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003388 // A match has been found.
3389 if (new_pos > match_end) {
3390 ReplacementStringBuilder::AddSubjectSlice(builder,
3391 match_end,
3392 new_pos);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003393 }
3394 pos = new_pos;
3395 builder->Add(pattern);
3396 } else {
3397 break;
3398 }
3399 }
3400 break;
3401 }
3402 if (pos < max_search_start) {
3403 ReplacementStringBuilder::AddSubjectSlice(builder,
3404 pos + pattern_length,
3405 subject_length);
3406 }
3407 *match_pos = pos;
3408 return true;
3409}
3410
3411
3412static bool SearchStringMultiple(Handle<String> subject,
3413 Handle<String> pattern,
3414 Handle<JSArray> last_match_info,
3415 FixedArrayBuilder* builder) {
3416 ASSERT(subject->IsFlat());
3417 ASSERT(pattern->IsFlat());
3418 ASSERT(pattern->length() > 1);
3419
3420 // Treating as if a previous match was before first character.
3421 int match_pos = -pattern->length();
3422
3423 for (;;) { // Break when search complete.
3424 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3425 AssertNoAllocation no_gc;
3426 if (subject->IsAsciiRepresentation()) {
3427 Vector<const char> subject_vector = subject->ToAsciiVector();
3428 if (pattern->IsAsciiRepresentation()) {
3429 if (SearchStringMultiple(subject_vector,
3430 *pattern,
3431 pattern->ToAsciiVector(),
3432 builder,
3433 &match_pos)) break;
3434 } else {
3435 if (SearchStringMultiple(subject_vector,
3436 *pattern,
3437 pattern->ToUC16Vector(),
3438 builder,
3439 &match_pos)) break;
3440 }
3441 } else {
3442 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3443 if (pattern->IsAsciiRepresentation()) {
3444 if (SearchStringMultiple(subject_vector,
3445 *pattern,
3446 pattern->ToAsciiVector(),
3447 builder,
3448 &match_pos)) break;
3449 } else {
3450 if (SearchStringMultiple(subject_vector,
3451 *pattern,
3452 pattern->ToUC16Vector(),
3453 builder,
3454 &match_pos)) break;
3455 }
3456 }
3457 }
3458
3459 if (match_pos >= 0) {
3460 SetLastMatchInfoNoCaptures(subject,
3461 last_match_info,
3462 match_pos,
3463 match_pos + pattern->length());
3464 return true;
3465 }
3466 return false; // No matches at all.
3467}
3468
3469
3470static RegExpImpl::IrregexpResult SearchRegExpNoCaptureMultiple(
3471 Handle<String> subject,
3472 Handle<JSRegExp> regexp,
3473 Handle<JSArray> last_match_array,
3474 FixedArrayBuilder* builder) {
3475 ASSERT(subject->IsFlat());
3476 int match_start = -1;
3477 int match_end = 0;
3478 int pos = 0;
3479 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3480 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3481
3482 OffsetsVector registers(required_registers);
3483 Vector<int> register_vector(registers.vector(), registers.length());
3484 int subject_length = subject->length();
3485
3486 for (;;) { // Break on failure, return on exception.
3487 RegExpImpl::IrregexpResult result =
3488 RegExpImpl::IrregexpExecOnce(regexp,
3489 subject,
3490 pos,
3491 register_vector);
3492 if (result == RegExpImpl::RE_SUCCESS) {
3493 match_start = register_vector[0];
3494 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3495 if (match_end < match_start) {
3496 ReplacementStringBuilder::AddSubjectSlice(builder,
3497 match_end,
3498 match_start);
3499 }
3500 match_end = register_vector[1];
3501 HandleScope loop_scope;
3502 builder->Add(*Factory::NewSubString(subject, match_start, match_end));
3503 if (match_start != match_end) {
3504 pos = match_end;
3505 } else {
3506 pos = match_end + 1;
3507 if (pos > subject_length) break;
3508 }
3509 } else if (result == RegExpImpl::RE_FAILURE) {
3510 break;
3511 } else {
3512 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3513 return result;
3514 }
3515 }
3516
3517 if (match_start >= 0) {
3518 if (match_end < subject_length) {
3519 ReplacementStringBuilder::AddSubjectSlice(builder,
3520 match_end,
3521 subject_length);
3522 }
3523 SetLastMatchInfoNoCaptures(subject,
3524 last_match_array,
3525 match_start,
3526 match_end);
3527 return RegExpImpl::RE_SUCCESS;
3528 } else {
3529 return RegExpImpl::RE_FAILURE; // No matches at all.
3530 }
3531}
3532
3533
3534static RegExpImpl::IrregexpResult SearchRegExpMultiple(
3535 Handle<String> subject,
3536 Handle<JSRegExp> regexp,
3537 Handle<JSArray> last_match_array,
3538 FixedArrayBuilder* builder) {
3539
3540 ASSERT(subject->IsFlat());
3541 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3542 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3543
3544 OffsetsVector registers(required_registers);
3545 Vector<int> register_vector(registers.vector(), registers.length());
3546
3547 RegExpImpl::IrregexpResult result =
3548 RegExpImpl::IrregexpExecOnce(regexp,
3549 subject,
3550 0,
3551 register_vector);
3552
3553 int capture_count = regexp->CaptureCount();
3554 int subject_length = subject->length();
3555
3556 // Position to search from.
3557 int pos = 0;
3558 // End of previous match. Differs from pos if match was empty.
3559 int match_end = 0;
3560 if (result == RegExpImpl::RE_SUCCESS) {
3561 // Need to keep a copy of the previous match for creating last_match_info
3562 // at the end, so we have two vectors that we swap between.
3563 OffsetsVector registers2(required_registers);
3564 Vector<int> prev_register_vector(registers2.vector(), registers2.length());
3565
3566 do {
3567 int match_start = register_vector[0];
3568 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3569 if (match_end < match_start) {
3570 ReplacementStringBuilder::AddSubjectSlice(builder,
3571 match_end,
3572 match_start);
3573 }
3574 match_end = register_vector[1];
3575
3576 {
3577 // Avoid accumulating new handles inside loop.
3578 HandleScope temp_scope;
3579 // Arguments array to replace function is match, captures, index and
3580 // subject, i.e., 3 + capture count in total.
3581 Handle<FixedArray> elements = Factory::NewFixedArray(3 + capture_count);
3582 elements->set(0, *Factory::NewSubString(subject,
3583 match_start,
3584 match_end));
3585 for (int i = 1; i <= capture_count; i++) {
3586 int start = register_vector[i * 2];
3587 if (start >= 0) {
3588 int end = register_vector[i * 2 + 1];
3589 ASSERT(start <= end);
3590 Handle<String> substring = Factory::NewSubString(subject,
3591 start,
3592 end);
3593 elements->set(i, *substring);
3594 } else {
3595 ASSERT(register_vector[i * 2 + 1] < 0);
3596 elements->set(i, Heap::undefined_value());
3597 }
3598 }
3599 elements->set(capture_count + 1, Smi::FromInt(match_start));
3600 elements->set(capture_count + 2, *subject);
3601 builder->Add(*Factory::NewJSArrayWithElements(elements));
3602 }
3603 // Swap register vectors, so the last successful match is in
3604 // prev_register_vector.
3605 Vector<int> tmp = prev_register_vector;
3606 prev_register_vector = register_vector;
3607 register_vector = tmp;
3608
3609 if (match_end > match_start) {
3610 pos = match_end;
3611 } else {
3612 pos = match_end + 1;
3613 if (pos > subject_length) {
3614 break;
3615 }
3616 }
3617
3618 result = RegExpImpl::IrregexpExecOnce(regexp,
3619 subject,
3620 pos,
3621 register_vector);
3622 } while (result == RegExpImpl::RE_SUCCESS);
3623
3624 if (result != RegExpImpl::RE_EXCEPTION) {
3625 // Finished matching, with at least one match.
3626 if (match_end < subject_length) {
3627 ReplacementStringBuilder::AddSubjectSlice(builder,
3628 match_end,
3629 subject_length);
3630 }
3631
3632 int last_match_capture_count = (capture_count + 1) * 2;
3633 int last_match_array_size =
3634 last_match_capture_count + RegExpImpl::kLastMatchOverhead;
3635 last_match_array->EnsureSize(last_match_array_size);
3636 AssertNoAllocation no_gc;
3637 FixedArray* elements = FixedArray::cast(last_match_array->elements());
3638 RegExpImpl::SetLastCaptureCount(elements, last_match_capture_count);
3639 RegExpImpl::SetLastSubject(elements, *subject);
3640 RegExpImpl::SetLastInput(elements, *subject);
3641 for (int i = 0; i < last_match_capture_count; i++) {
3642 RegExpImpl::SetCapture(elements, i, prev_register_vector[i]);
3643 }
3644 return RegExpImpl::RE_SUCCESS;
3645 }
3646 }
3647 // No matches at all, return failure or exception result directly.
3648 return result;
3649}
3650
3651
3652static Object* Runtime_RegExpExecMultiple(Arguments args) {
3653 ASSERT(args.length() == 4);
3654 HandleScope handles;
3655
3656 CONVERT_ARG_CHECKED(String, subject, 1);
3657 if (!subject->IsFlat()) { FlattenString(subject); }
3658 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
3659 CONVERT_ARG_CHECKED(JSArray, last_match_info, 2);
3660 CONVERT_ARG_CHECKED(JSArray, result_array, 3);
3661
3662 ASSERT(last_match_info->HasFastElements());
3663 ASSERT(regexp->GetFlags().is_global());
3664 Handle<FixedArray> result_elements;
3665 if (result_array->HasFastElements()) {
3666 result_elements =
3667 Handle<FixedArray>(FixedArray::cast(result_array->elements()));
3668 } else {
3669 result_elements = Factory::NewFixedArrayWithHoles(16);
3670 }
3671 FixedArrayBuilder builder(result_elements);
3672
3673 if (regexp->TypeTag() == JSRegExp::ATOM) {
3674 Handle<String> pattern(
3675 String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex)));
3676 int pattern_length = pattern->length();
3677 if (pattern_length == 1) {
3678 if (SearchCharMultiple(subject, pattern, last_match_info, &builder)) {
3679 return *builder.ToJSArray(result_array);
3680 }
3681 return Heap::null_value();
3682 }
3683
3684 if (!pattern->IsFlat()) FlattenString(pattern);
3685 if (SearchStringMultiple(subject, pattern, last_match_info, &builder)) {
3686 return *builder.ToJSArray(result_array);
3687 }
3688 return Heap::null_value();
3689 }
3690
3691 ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
3692
3693 RegExpImpl::IrregexpResult result;
3694 if (regexp->CaptureCount() == 0) {
3695 result = SearchRegExpNoCaptureMultiple(subject,
3696 regexp,
3697 last_match_info,
3698 &builder);
3699 } else {
3700 result = SearchRegExpMultiple(subject, regexp, last_match_info, &builder);
3701 }
3702 if (result == RegExpImpl::RE_SUCCESS) return *builder.ToJSArray(result_array);
3703 if (result == RegExpImpl::RE_FAILURE) return Heap::null_value();
3704 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3705 return Failure::Exception();
3706}
3707
3708
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003709static Object* Runtime_NumberToRadixString(Arguments args) {
3710 NoHandleAllocation ha;
3711 ASSERT(args.length() == 2);
3712
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003713 // Fast case where the result is a one character string.
3714 if (args[0]->IsSmi() && args[1]->IsSmi()) {
3715 int value = Smi::cast(args[0])->value();
3716 int radix = Smi::cast(args[1])->value();
3717 if (value >= 0 && value < radix) {
3718 RUNTIME_ASSERT(radix <= 36);
3719 // Character array used for conversion.
3720 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
3721 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
3722 }
3723 }
3724
3725 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003726 CONVERT_DOUBLE_CHECKED(value, args[0]);
3727 if (isnan(value)) {
3728 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3729 }
3730 if (isinf(value)) {
3731 if (value < 0) {
3732 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3733 }
3734 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3735 }
3736 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
3737 int radix = FastD2I(radix_number);
3738 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3739 char* str = DoubleToRadixCString(value, radix);
3740 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3741 DeleteArray(str);
3742 return result;
3743}
3744
3745
3746static Object* Runtime_NumberToFixed(Arguments args) {
3747 NoHandleAllocation ha;
3748 ASSERT(args.length() == 2);
3749
3750 CONVERT_DOUBLE_CHECKED(value, args[0]);
3751 if (isnan(value)) {
3752 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3753 }
3754 if (isinf(value)) {
3755 if (value < 0) {
3756 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3757 }
3758 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3759 }
3760 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3761 int f = FastD2I(f_number);
3762 RUNTIME_ASSERT(f >= 0);
3763 char* str = DoubleToFixedCString(value, f);
3764 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3765 DeleteArray(str);
3766 return res;
3767}
3768
3769
3770static Object* Runtime_NumberToExponential(Arguments args) {
3771 NoHandleAllocation ha;
3772 ASSERT(args.length() == 2);
3773
3774 CONVERT_DOUBLE_CHECKED(value, args[0]);
3775 if (isnan(value)) {
3776 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3777 }
3778 if (isinf(value)) {
3779 if (value < 0) {
3780 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3781 }
3782 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3783 }
3784 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3785 int f = FastD2I(f_number);
3786 RUNTIME_ASSERT(f >= -1 && f <= 20);
3787 char* str = DoubleToExponentialCString(value, f);
3788 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3789 DeleteArray(str);
3790 return res;
3791}
3792
3793
3794static Object* Runtime_NumberToPrecision(Arguments args) {
3795 NoHandleAllocation ha;
3796 ASSERT(args.length() == 2);
3797
3798 CONVERT_DOUBLE_CHECKED(value, args[0]);
3799 if (isnan(value)) {
3800 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3801 }
3802 if (isinf(value)) {
3803 if (value < 0) {
3804 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3805 }
3806 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3807 }
3808 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3809 int f = FastD2I(f_number);
3810 RUNTIME_ASSERT(f >= 1 && f <= 21);
3811 char* str = DoubleToPrecisionCString(value, f);
3812 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3813 DeleteArray(str);
3814 return res;
3815}
3816
3817
3818// Returns a single character string where first character equals
3819// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003820static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003821 if (index < static_cast<uint32_t>(string->length())) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003822 string->TryFlatten();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003823 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003824 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003825 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003826 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003827}
3828
3829
3830Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
3831 // Handle [] indexing on Strings
3832 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003833 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
3834 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003835 }
3836
3837 // Handle [] indexing on String objects
3838 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003839 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
3840 Handle<Object> result =
3841 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
3842 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003843 }
3844
3845 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003846 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003847 return prototype->GetElement(index);
3848 }
3849
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003850 return GetElement(object, index);
3851}
3852
3853
3854Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003855 return object->GetElement(index);
3856}
3857
3858
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003859Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
3860 HandleScope scope;
3861
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003862 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003863 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003864 Handle<Object> error =
3865 Factory::NewTypeError("non_object_property_load",
3866 HandleVector(args, 2));
3867 return Top::Throw(*error);
3868 }
3869
3870 // Check if the given key is an array index.
3871 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003872 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003873 return GetElementOrCharAt(object, index);
3874 }
3875
3876 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003877 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003878 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003879 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003880 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003881 bool has_pending_exception = false;
3882 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003883 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003884 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003885 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003886 }
3887
ager@chromium.org32912102009-01-16 10:38:43 +00003888 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003889 // the element if so.
3890 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003891 return GetElementOrCharAt(object, index);
3892 } else {
3893 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003894 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003895 }
3896}
3897
3898
3899static Object* Runtime_GetProperty(Arguments args) {
3900 NoHandleAllocation ha;
3901 ASSERT(args.length() == 2);
3902
3903 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003904 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003905
3906 return Runtime::GetObjectProperty(object, key);
3907}
3908
3909
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003910// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003911static Object* Runtime_KeyedGetProperty(Arguments args) {
3912 NoHandleAllocation ha;
3913 ASSERT(args.length() == 2);
3914
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003915 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00003916 // itself.
3917 //
3918 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00003919 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00003920 // global proxy object never has properties. This is the case
3921 // because the global proxy object forwards everything to its hidden
3922 // prototype including local lookups.
3923 //
3924 // Additionally, we need to make sure that we do not cache results
3925 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003926 if (args[0]->IsJSObject() &&
3927 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00003928 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003929 args[1]->IsString()) {
3930 JSObject* receiver = JSObject::cast(args[0]);
3931 String* key = String::cast(args[1]);
3932 if (receiver->HasFastProperties()) {
3933 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003934 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003935 int offset = KeyedLookupCache::Lookup(receiver_map, key);
3936 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003937 Object* value = receiver->FastPropertyAt(offset);
3938 return value->IsTheHole() ? Heap::undefined_value() : value;
3939 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003940 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003941 LookupResult result;
3942 receiver->LocalLookup(key, &result);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00003943 if (result.IsProperty() && result.type() == FIELD) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003944 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003945 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003946 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003947 }
3948 } else {
3949 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003950 StringDictionary* dictionary = receiver->property_dictionary();
3951 int entry = dictionary->FindEntry(key);
3952 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003953 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003954 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003955 if (!receiver->IsGlobalObject()) return value;
3956 value = JSGlobalPropertyCell::cast(value)->value();
3957 if (!value->IsTheHole()) return value;
3958 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003959 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003960 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003961 } else if (args[0]->IsString() && args[1]->IsSmi()) {
3962 // Fast case for string indexing using [] with a smi index.
3963 HandleScope scope;
3964 Handle<String> str = args.at<String>(0);
3965 int index = Smi::cast(args[1])->value();
3966 Handle<Object> result = GetCharAt(str, index);
3967 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003968 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003969
3970 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003971 return Runtime::GetObjectProperty(args.at<Object>(0),
3972 args.at<Object>(1));
3973}
3974
3975
ager@chromium.org5c838252010-02-19 08:53:10 +00003976static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
3977 ASSERT(args.length() == 5);
3978 HandleScope scope;
3979 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3980 CONVERT_CHECKED(String, name, args[1]);
3981 CONVERT_CHECKED(Smi, flag_setter, args[2]);
3982 CONVERT_CHECKED(JSFunction, fun, args[3]);
3983 CONVERT_CHECKED(Smi, flag_attr, args[4]);
3984 int unchecked = flag_attr->value();
3985 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3986 RUNTIME_ASSERT(!obj->IsNull());
3987 LookupResult result;
3988 obj->LocalLookupRealNamedProperty(name, &result);
3989
3990 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3991 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
3992 // delete it to avoid running into trouble in DefineAccessor, which
3993 // handles this incorrectly if the property is readonly (does nothing)
3994 if (result.IsProperty() &&
3995 (result.type() == FIELD || result.type() == NORMAL
3996 || result.type() == CONSTANT_FUNCTION)) {
3997 obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
3998 }
3999 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
4000}
4001
4002static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
4003 ASSERT(args.length() == 4);
4004 HandleScope scope;
4005 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
4006 CONVERT_ARG_CHECKED(String, name, 1);
4007 Handle<Object> obj_value = args.at<Object>(2);
4008
4009 CONVERT_CHECKED(Smi, flag, args[3]);
4010 int unchecked = flag->value();
4011 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4012
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004013 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
4014
4015 // Check if this is an element.
4016 uint32_t index;
4017 bool is_element = name->AsArrayIndex(&index);
4018
4019 // Special case for elements if any of the flags are true.
4020 // If elements are in fast case we always implicitly assume that:
4021 // DONT_DELETE: false, DONT_ENUM: false, READ_ONLY: false.
4022 if (((unchecked & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) &&
4023 is_element) {
4024 // Normalize the elements to enable attributes on the property.
4025 js_object->NormalizeElements();
4026 NumberDictionary* dictionary = js_object->element_dictionary();
4027 // Make sure that we never go back to fast case.
4028 dictionary->set_requires_slow_elements();
4029 PropertyDetails details = PropertyDetails(attr, NORMAL);
4030 dictionary->Set(index, *obj_value, details);
4031 }
4032
ager@chromium.org5c838252010-02-19 08:53:10 +00004033 LookupResult result;
4034 js_object->LocalLookupRealNamedProperty(*name, &result);
4035
ager@chromium.org5c838252010-02-19 08:53:10 +00004036 // Take special care when attributes are different and there is already
4037 // a property. For simplicity we normalize the property which enables us
4038 // to not worry about changing the instance_descriptor and creating a new
4039 // map. The current version of SetObjectProperty does not handle attributes
4040 // correctly in the case where a property is a field and is reset with
4041 // new attributes.
4042 if (result.IsProperty() && attr != result.GetAttributes()) {
4043 // New attributes - normalize to avoid writing to instance descriptor
4044 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
4045 // Use IgnoreAttributes version since a readonly property may be
4046 // overridden and SetProperty does not allow this.
4047 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
4048 *obj_value,
4049 attr);
4050 }
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004051
ager@chromium.org5c838252010-02-19 08:53:10 +00004052 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
4053}
4054
4055
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004056Object* Runtime::SetObjectProperty(Handle<Object> object,
4057 Handle<Object> key,
4058 Handle<Object> value,
4059 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004060 HandleScope scope;
4061
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004062 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004063 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004064 Handle<Object> error =
4065 Factory::NewTypeError("non_object_property_store",
4066 HandleVector(args, 2));
4067 return Top::Throw(*error);
4068 }
4069
4070 // If the object isn't a JavaScript object, we ignore the store.
4071 if (!object->IsJSObject()) return *value;
4072
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004073 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
4074
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004075 // Check if the given key is an array index.
4076 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004077 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004078 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
4079 // of a string using [] notation. We need to support this too in
4080 // JavaScript.
4081 // In the case of a String object we just need to redirect the assignment to
4082 // the underlying string if the index is in range. Since the underlying
4083 // string does nothing with the assignment then we can ignore such
4084 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004085 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004086 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004087 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004088
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004089 Handle<Object> result = SetElement(js_object, index, value);
4090 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004091 return *value;
4092 }
4093
4094 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004095 Handle<Object> result;
4096 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004097 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004098 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004099 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004100 key_string->TryFlatten();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004101 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004102 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004103 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004104 return *value;
4105 }
4106
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004107 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004108 bool has_pending_exception = false;
4109 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4110 if (has_pending_exception) return Failure::Exception();
4111 Handle<String> name = Handle<String>::cast(converted);
4112
4113 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004114 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004115 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004116 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004117 }
4118}
4119
4120
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004121Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
4122 Handle<Object> key,
4123 Handle<Object> value,
4124 PropertyAttributes attr) {
4125 HandleScope scope;
4126
4127 // Check if the given key is an array index.
4128 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004129 if (key->ToArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004130 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
4131 // of a string using [] notation. We need to support this too in
4132 // JavaScript.
4133 // In the case of a String object we just need to redirect the assignment to
4134 // the underlying string if the index is in range. Since the underlying
4135 // string does nothing with the assignment then we can ignore such
4136 // assignments.
4137 if (js_object->IsStringObjectWithCharacterAt(index)) {
4138 return *value;
4139 }
4140
4141 return js_object->SetElement(index, *value);
4142 }
4143
4144 if (key->IsString()) {
4145 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004146 return js_object->SetElement(index, *value);
4147 } else {
4148 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004149 key_string->TryFlatten();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004150 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
4151 *value,
4152 attr);
4153 }
4154 }
4155
4156 // Call-back into JavaScript to convert the key to a string.
4157 bool has_pending_exception = false;
4158 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4159 if (has_pending_exception) return Failure::Exception();
4160 Handle<String> name = Handle<String>::cast(converted);
4161
4162 if (name->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004163 return js_object->SetElement(index, *value);
4164 } else {
4165 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
4166 }
4167}
4168
4169
ager@chromium.orge2902be2009-06-08 12:21:35 +00004170Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
4171 Handle<Object> key) {
4172 HandleScope scope;
4173
4174 // Check if the given key is an array index.
4175 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004176 if (key->ToArrayIndex(&index)) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00004177 // In Firefox/SpiderMonkey, Safari and Opera you can access the
4178 // characters of a string using [] notation. In the case of a
4179 // String object we just need to redirect the deletion to the
4180 // underlying string if the index is in range. Since the
4181 // underlying string does nothing with the deletion, we can ignore
4182 // such deletions.
4183 if (js_object->IsStringObjectWithCharacterAt(index)) {
4184 return Heap::true_value();
4185 }
4186
4187 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
4188 }
4189
4190 Handle<String> key_string;
4191 if (key->IsString()) {
4192 key_string = Handle<String>::cast(key);
4193 } else {
4194 // Call-back into JavaScript to convert the key to a string.
4195 bool has_pending_exception = false;
4196 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4197 if (has_pending_exception) return Failure::Exception();
4198 key_string = Handle<String>::cast(converted);
4199 }
4200
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004201 key_string->TryFlatten();
ager@chromium.orge2902be2009-06-08 12:21:35 +00004202 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
4203}
4204
4205
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004206static Object* Runtime_SetProperty(Arguments args) {
4207 NoHandleAllocation ha;
4208 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
4209
4210 Handle<Object> object = args.at<Object>(0);
4211 Handle<Object> key = args.at<Object>(1);
4212 Handle<Object> value = args.at<Object>(2);
4213
4214 // Compute attributes.
4215 PropertyAttributes attributes = NONE;
4216 if (args.length() == 4) {
4217 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004218 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004219 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004220 RUNTIME_ASSERT(
4221 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4222 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004223 }
4224 return Runtime::SetObjectProperty(object, key, value, attributes);
4225}
4226
4227
4228// Set a local property, even if it is READ_ONLY. If the property does not
4229// exist, it will be added with attributes NONE.
4230static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
4231 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004232 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004233 CONVERT_CHECKED(JSObject, object, args[0]);
4234 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004235 // Compute attributes.
4236 PropertyAttributes attributes = NONE;
4237 if (args.length() == 4) {
4238 CONVERT_CHECKED(Smi, value_obj, args[3]);
4239 int unchecked_value = value_obj->value();
4240 // Only attribute bits should be set.
4241 RUNTIME_ASSERT(
4242 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4243 attributes = static_cast<PropertyAttributes>(unchecked_value);
4244 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004245
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004246 return object->
4247 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004248}
4249
4250
4251static Object* Runtime_DeleteProperty(Arguments args) {
4252 NoHandleAllocation ha;
4253 ASSERT(args.length() == 2);
4254
4255 CONVERT_CHECKED(JSObject, object, args[0]);
4256 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00004257 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004258}
4259
4260
ager@chromium.org9085a012009-05-11 19:22:57 +00004261static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
4262 Handle<String> key) {
4263 if (object->HasLocalProperty(*key)) return Heap::true_value();
4264 // Handle hidden prototypes. If there's a hidden prototype above this thing
4265 // then we have to check it for properties, because they are supposed to
4266 // look like they are on this object.
4267 Handle<Object> proto(object->GetPrototype());
4268 if (proto->IsJSObject() &&
4269 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
4270 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
4271 }
4272 return Heap::false_value();
4273}
4274
4275
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004276static Object* Runtime_HasLocalProperty(Arguments args) {
4277 NoHandleAllocation ha;
4278 ASSERT(args.length() == 2);
4279 CONVERT_CHECKED(String, key, args[1]);
4280
ager@chromium.org9085a012009-05-11 19:22:57 +00004281 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004282 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00004283 if (obj->IsJSObject()) {
4284 JSObject* object = JSObject::cast(obj);
4285 // Fast case - no interceptors.
4286 if (object->HasRealNamedProperty(key)) return Heap::true_value();
4287 // Slow case. Either it's not there or we have an interceptor. We should
4288 // have handles for this kind of deal.
4289 HandleScope scope;
4290 return HasLocalPropertyImplementation(Handle<JSObject>(object),
4291 Handle<String>(key));
4292 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004293 // Well, there is one exception: Handle [] on strings.
4294 uint32_t index;
4295 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00004296 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004297 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004298 return Heap::true_value();
4299 }
4300 }
4301 return Heap::false_value();
4302}
4303
4304
4305static Object* Runtime_HasProperty(Arguments args) {
4306 NoHandleAllocation na;
4307 ASSERT(args.length() == 2);
4308
4309 // Only JS objects can have properties.
4310 if (args[0]->IsJSObject()) {
4311 JSObject* object = JSObject::cast(args[0]);
4312 CONVERT_CHECKED(String, key, args[1]);
4313 if (object->HasProperty(key)) return Heap::true_value();
4314 }
4315 return Heap::false_value();
4316}
4317
4318
4319static Object* Runtime_HasElement(Arguments args) {
4320 NoHandleAllocation na;
4321 ASSERT(args.length() == 2);
4322
4323 // Only JS objects can have elements.
4324 if (args[0]->IsJSObject()) {
4325 JSObject* object = JSObject::cast(args[0]);
4326 CONVERT_CHECKED(Smi, index_obj, args[1]);
4327 uint32_t index = index_obj->value();
4328 if (object->HasElement(index)) return Heap::true_value();
4329 }
4330 return Heap::false_value();
4331}
4332
4333
4334static Object* Runtime_IsPropertyEnumerable(Arguments args) {
4335 NoHandleAllocation ha;
4336 ASSERT(args.length() == 2);
4337
4338 CONVERT_CHECKED(JSObject, object, args[0]);
4339 CONVERT_CHECKED(String, key, args[1]);
4340
4341 uint32_t index;
4342 if (key->AsArrayIndex(&index)) {
4343 return Heap::ToBoolean(object->HasElement(index));
4344 }
4345
ager@chromium.org870a0b62008-11-04 11:43:05 +00004346 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
4347 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004348}
4349
4350
4351static Object* Runtime_GetPropertyNames(Arguments args) {
4352 HandleScope scope;
4353 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004354 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004355 return *GetKeysFor(object);
4356}
4357
4358
4359// Returns either a FixedArray as Runtime_GetPropertyNames,
4360// or, if the given object has an enum cache that contains
4361// all enumerable properties of the object and its prototypes
4362// have none, the map of the object. This is used to speed up
4363// the check for deletions during a for-in.
4364static Object* Runtime_GetPropertyNamesFast(Arguments args) {
4365 ASSERT(args.length() == 1);
4366
4367 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4368
4369 if (raw_object->IsSimpleEnum()) return raw_object->map();
4370
4371 HandleScope scope;
4372 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004373 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
4374 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004375
4376 // Test again, since cache may have been built by preceding call.
4377 if (object->IsSimpleEnum()) return object->map();
4378
4379 return *content;
4380}
4381
4382
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004383// Find the length of the prototype chain that is to to handled as one. If a
4384// prototype object is hidden it is to be viewed as part of the the object it
4385// is prototype for.
4386static int LocalPrototypeChainLength(JSObject* obj) {
4387 int count = 1;
4388 Object* proto = obj->GetPrototype();
4389 while (proto->IsJSObject() &&
4390 JSObject::cast(proto)->map()->is_hidden_prototype()) {
4391 count++;
4392 proto = JSObject::cast(proto)->GetPrototype();
4393 }
4394 return count;
4395}
4396
4397
4398// Return the names of the local named properties.
4399// args[0]: object
4400static Object* Runtime_GetLocalPropertyNames(Arguments args) {
4401 HandleScope scope;
4402 ASSERT(args.length() == 1);
4403 if (!args[0]->IsJSObject()) {
4404 return Heap::undefined_value();
4405 }
4406 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4407
4408 // Skip the global proxy as it has no properties and always delegates to the
4409 // real global object.
4410 if (obj->IsJSGlobalProxy()) {
4411 // Only collect names if access is permitted.
4412 if (obj->IsAccessCheckNeeded() &&
4413 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
4414 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
4415 return *Factory::NewJSArray(0);
4416 }
4417 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
4418 }
4419
4420 // Find the number of objects making up this.
4421 int length = LocalPrototypeChainLength(*obj);
4422
4423 // Find the number of local properties for each of the objects.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004424 ScopedVector<int> local_property_count(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004425 int total_property_count = 0;
4426 Handle<JSObject> jsproto = obj;
4427 for (int i = 0; i < length; i++) {
4428 // Only collect names if access is permitted.
4429 if (jsproto->IsAccessCheckNeeded() &&
4430 !Top::MayNamedAccess(*jsproto,
4431 Heap::undefined_value(),
4432 v8::ACCESS_KEYS)) {
4433 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
4434 return *Factory::NewJSArray(0);
4435 }
4436 int n;
4437 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4438 local_property_count[i] = n;
4439 total_property_count += n;
4440 if (i < length - 1) {
4441 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4442 }
4443 }
4444
4445 // Allocate an array with storage for all the property names.
4446 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
4447
4448 // Get the property names.
4449 jsproto = obj;
4450 int proto_with_hidden_properties = 0;
4451 for (int i = 0; i < length; i++) {
4452 jsproto->GetLocalPropertyNames(*names,
4453 i == 0 ? 0 : local_property_count[i - 1]);
4454 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
4455 proto_with_hidden_properties++;
4456 }
4457 if (i < length - 1) {
4458 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4459 }
4460 }
4461
4462 // Filter out name of hidden propeties object.
4463 if (proto_with_hidden_properties > 0) {
4464 Handle<FixedArray> old_names = names;
4465 names = Factory::NewFixedArray(
4466 names->length() - proto_with_hidden_properties);
4467 int dest_pos = 0;
4468 for (int i = 0; i < total_property_count; i++) {
4469 Object* name = old_names->get(i);
4470 if (name == Heap::hidden_symbol()) {
4471 continue;
4472 }
4473 names->set(dest_pos++, name);
4474 }
4475 }
4476
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004477 return *Factory::NewJSArrayWithElements(names);
4478}
4479
4480
4481// Return the names of the local indexed properties.
4482// args[0]: object
4483static Object* Runtime_GetLocalElementNames(Arguments args) {
4484 HandleScope scope;
4485 ASSERT(args.length() == 1);
4486 if (!args[0]->IsJSObject()) {
4487 return Heap::undefined_value();
4488 }
4489 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4490
4491 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4492 Handle<FixedArray> names = Factory::NewFixedArray(n);
4493 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4494 return *Factory::NewJSArrayWithElements(names);
4495}
4496
4497
4498// Return information on whether an object has a named or indexed interceptor.
4499// args[0]: object
4500static Object* Runtime_GetInterceptorInfo(Arguments args) {
4501 HandleScope scope;
4502 ASSERT(args.length() == 1);
4503 if (!args[0]->IsJSObject()) {
4504 return Smi::FromInt(0);
4505 }
4506 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4507
4508 int result = 0;
4509 if (obj->HasNamedInterceptor()) result |= 2;
4510 if (obj->HasIndexedInterceptor()) result |= 1;
4511
4512 return Smi::FromInt(result);
4513}
4514
4515
4516// Return property names from named interceptor.
4517// args[0]: object
4518static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
4519 HandleScope scope;
4520 ASSERT(args.length() == 1);
4521 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4522
4523 if (obj->HasNamedInterceptor()) {
4524 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4525 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4526 }
4527 return Heap::undefined_value();
4528}
4529
4530
4531// Return element names from indexed interceptor.
4532// args[0]: object
4533static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
4534 HandleScope scope;
4535 ASSERT(args.length() == 1);
4536 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4537
4538 if (obj->HasIndexedInterceptor()) {
4539 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4540 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4541 }
4542 return Heap::undefined_value();
4543}
4544
4545
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004546static Object* Runtime_LocalKeys(Arguments args) {
4547 ASSERT_EQ(args.length(), 1);
4548 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4549 HandleScope scope;
4550 Handle<JSObject> object(raw_object);
4551 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
4552 LOCAL_ONLY);
4553 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
4554 // property array and since the result is mutable we have to create
4555 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004556 int length = contents->length();
4557 Handle<FixedArray> copy = Factory::NewFixedArray(length);
4558 for (int i = 0; i < length; i++) {
4559 Object* entry = contents->get(i);
4560 if (entry->IsString()) {
4561 copy->set(i, entry);
4562 } else {
4563 ASSERT(entry->IsNumber());
4564 HandleScope scope;
4565 Handle<Object> entry_handle(entry);
4566 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
4567 copy->set(i, *entry_str);
4568 }
4569 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004570 return *Factory::NewJSArrayWithElements(copy);
4571}
4572
4573
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004574static Object* Runtime_GetArgumentsProperty(Arguments args) {
4575 NoHandleAllocation ha;
4576 ASSERT(args.length() == 1);
4577
4578 // Compute the frame holding the arguments.
4579 JavaScriptFrameIterator it;
4580 it.AdvanceToArgumentsFrame();
4581 JavaScriptFrame* frame = it.frame();
4582
4583 // Get the actual number of provided arguments.
4584 const uint32_t n = frame->GetProvidedParametersCount();
4585
4586 // Try to convert the key to an index. If successful and within
4587 // index return the the argument from the frame.
4588 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004589 if (args[0]->ToArrayIndex(&index) && index < n) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004590 return frame->GetParameter(index);
4591 }
4592
4593 // Convert the key to a string.
4594 HandleScope scope;
4595 bool exception = false;
4596 Handle<Object> converted =
4597 Execution::ToString(args.at<Object>(0), &exception);
4598 if (exception) return Failure::Exception();
4599 Handle<String> key = Handle<String>::cast(converted);
4600
4601 // Try to convert the string key into an array index.
4602 if (key->AsArrayIndex(&index)) {
4603 if (index < n) {
4604 return frame->GetParameter(index);
4605 } else {
4606 return Top::initial_object_prototype()->GetElement(index);
4607 }
4608 }
4609
4610 // Handle special arguments properties.
4611 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
4612 if (key->Equals(Heap::callee_symbol())) return frame->function();
4613
4614 // Lookup in the initial Object.prototype object.
4615 return Top::initial_object_prototype()->GetProperty(*key);
4616}
4617
4618
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004619static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004620 HandleScope scope;
4621
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004622 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004623 Handle<Object> object = args.at<Object>(0);
4624 if (object->IsJSObject()) {
4625 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00004626 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
4627 js_object->TransformToFastProperties(0);
4628 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004629 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004630 return *object;
4631}
4632
4633
4634static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004635 HandleScope scope;
4636
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004637 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004638 Handle<Object> object = args.at<Object>(0);
4639 if (object->IsJSObject()) {
4640 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004641 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004642 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004643 return *object;
4644}
4645
4646
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004647static Object* Runtime_ToBool(Arguments args) {
4648 NoHandleAllocation ha;
4649 ASSERT(args.length() == 1);
4650
4651 return args[0]->ToBoolean();
4652}
4653
4654
4655// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
4656// Possible optimizations: put the type string into the oddballs.
4657static Object* Runtime_Typeof(Arguments args) {
4658 NoHandleAllocation ha;
4659
4660 Object* obj = args[0];
4661 if (obj->IsNumber()) return Heap::number_symbol();
4662 HeapObject* heap_obj = HeapObject::cast(obj);
4663
4664 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004665 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004666
4667 InstanceType instance_type = heap_obj->map()->instance_type();
4668 if (instance_type < FIRST_NONSTRING_TYPE) {
4669 return Heap::string_symbol();
4670 }
4671
4672 switch (instance_type) {
4673 case ODDBALL_TYPE:
4674 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
4675 return Heap::boolean_symbol();
4676 }
4677 if (heap_obj->IsNull()) {
4678 return Heap::object_symbol();
4679 }
4680 ASSERT(heap_obj->IsUndefined());
4681 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004682 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004683 return Heap::function_symbol();
4684 default:
4685 // For any kind of object not handled above, the spec rule for
4686 // host objects gives that it is okay to return "object"
4687 return Heap::object_symbol();
4688 }
4689}
4690
4691
lrn@chromium.org25156de2010-04-06 13:10:27 +00004692static bool AreDigits(const char*s, int from, int to) {
4693 for (int i = from; i < to; i++) {
4694 if (s[i] < '0' || s[i] > '9') return false;
4695 }
4696
4697 return true;
4698}
4699
4700
4701static int ParseDecimalInteger(const char*s, int from, int to) {
4702 ASSERT(to - from < 10); // Overflow is not possible.
4703 ASSERT(from < to);
4704 int d = s[from] - '0';
4705
4706 for (int i = from + 1; i < to; i++) {
4707 d = 10 * d + (s[i] - '0');
4708 }
4709
4710 return d;
4711}
4712
4713
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004714static Object* Runtime_StringToNumber(Arguments args) {
4715 NoHandleAllocation ha;
4716 ASSERT(args.length() == 1);
4717 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004718 subject->TryFlatten();
lrn@chromium.org25156de2010-04-06 13:10:27 +00004719
4720 // Fast case: short integer or some sorts of junk values.
4721 int len = subject->length();
4722 if (subject->IsSeqAsciiString()) {
4723 if (len == 0) return Smi::FromInt(0);
4724
4725 char const* data = SeqAsciiString::cast(subject)->GetChars();
4726 bool minus = (data[0] == '-');
4727 int start_pos = (minus ? 1 : 0);
4728
4729 if (start_pos == len) {
4730 return Heap::nan_value();
4731 } else if (data[start_pos] > '9') {
4732 // Fast check for a junk value. A valid string may start from a
4733 // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit or
4734 // the 'I' character ('Infinity'). All of that have codes not greater than
4735 // '9' except 'I'.
4736 if (data[start_pos] != 'I') {
4737 return Heap::nan_value();
4738 }
4739 } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
4740 // The maximal/minimal smi has 10 digits. If the string has less digits we
4741 // know it will fit into the smi-data type.
4742 int d = ParseDecimalInteger(data, start_pos, len);
4743 if (minus) {
4744 if (d == 0) return Heap::minus_zero_value();
4745 d = -d;
4746 }
4747 return Smi::FromInt(d);
4748 }
4749 }
4750
4751 // Slower case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004752 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
4753}
4754
4755
4756static Object* Runtime_StringFromCharCodeArray(Arguments args) {
4757 NoHandleAllocation ha;
4758 ASSERT(args.length() == 1);
4759
4760 CONVERT_CHECKED(JSArray, codes, args[0]);
4761 int length = Smi::cast(codes->length())->value();
4762
4763 // Check if the string can be ASCII.
4764 int i;
4765 for (i = 0; i < length; i++) {
4766 Object* element = codes->GetElement(i);
4767 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
4768 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
4769 break;
4770 }
4771
4772 Object* object = NULL;
4773 if (i == length) { // The string is ASCII.
4774 object = Heap::AllocateRawAsciiString(length);
4775 } else { // The string is not ASCII.
4776 object = Heap::AllocateRawTwoByteString(length);
4777 }
4778
4779 if (object->IsFailure()) return object;
4780 String* result = String::cast(object);
4781 for (int i = 0; i < length; i++) {
4782 Object* element = codes->GetElement(i);
4783 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004784 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004785 }
4786 return result;
4787}
4788
4789
4790// kNotEscaped is generated by the following:
4791//
4792// #!/bin/perl
4793// for (my $i = 0; $i < 256; $i++) {
4794// print "\n" if $i % 16 == 0;
4795// my $c = chr($i);
4796// my $escaped = 1;
4797// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
4798// print $escaped ? "0, " : "1, ";
4799// }
4800
4801
4802static bool IsNotEscaped(uint16_t character) {
4803 // Only for 8 bit characters, the rest are always escaped (in a different way)
4804 ASSERT(character < 256);
4805 static const char kNotEscaped[256] = {
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, 0, 0, 0, 0, 0, 0,
4808 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
4809 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
4810 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4811 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
4812 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4813 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4822 };
4823 return kNotEscaped[character] != 0;
4824}
4825
4826
4827static Object* Runtime_URIEscape(Arguments args) {
4828 const char hex_chars[] = "0123456789ABCDEF";
4829 NoHandleAllocation ha;
4830 ASSERT(args.length() == 1);
4831 CONVERT_CHECKED(String, source, args[0]);
4832
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004833 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004834
4835 int escaped_length = 0;
4836 int length = source->length();
4837 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004838 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004839 buffer->Reset(source);
4840 while (buffer->has_more()) {
4841 uint16_t character = buffer->GetNext();
4842 if (character >= 256) {
4843 escaped_length += 6;
4844 } else if (IsNotEscaped(character)) {
4845 escaped_length++;
4846 } else {
4847 escaped_length += 3;
4848 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004849 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004850 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004851 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004852 Top::context()->mark_out_of_memory();
4853 return Failure::OutOfMemoryException();
4854 }
4855 }
4856 }
4857 // No length change implies no change. Return original string if no change.
4858 if (escaped_length == length) {
4859 return source;
4860 }
4861 Object* o = Heap::AllocateRawAsciiString(escaped_length);
4862 if (o->IsFailure()) return o;
4863 String* destination = String::cast(o);
4864 int dest_position = 0;
4865
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004866 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004867 buffer->Rewind();
4868 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00004869 uint16_t chr = buffer->GetNext();
4870 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004871 destination->Set(dest_position, '%');
4872 destination->Set(dest_position+1, 'u');
4873 destination->Set(dest_position+2, hex_chars[chr >> 12]);
4874 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
4875 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
4876 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004877 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00004878 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004879 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004880 dest_position++;
4881 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004882 destination->Set(dest_position, '%');
4883 destination->Set(dest_position+1, hex_chars[chr >> 4]);
4884 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004885 dest_position += 3;
4886 }
4887 }
4888 return destination;
4889}
4890
4891
4892static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
4893 static const signed char kHexValue['g'] = {
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 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4897 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
4898 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4899 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4900 -1, 10, 11, 12, 13, 14, 15 };
4901
4902 if (character1 > 'f') return -1;
4903 int hi = kHexValue[character1];
4904 if (hi == -1) return -1;
4905 if (character2 > 'f') return -1;
4906 int lo = kHexValue[character2];
4907 if (lo == -1) return -1;
4908 return (hi << 4) + lo;
4909}
4910
4911
ager@chromium.org870a0b62008-11-04 11:43:05 +00004912static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004913 int i,
4914 int length,
4915 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004916 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00004917 int32_t hi = 0;
4918 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004919 if (character == '%' &&
4920 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004921 source->Get(i + 1) == 'u' &&
4922 (hi = TwoDigitHex(source->Get(i + 2),
4923 source->Get(i + 3))) != -1 &&
4924 (lo = TwoDigitHex(source->Get(i + 4),
4925 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004926 *step = 6;
4927 return (hi << 8) + lo;
4928 } else if (character == '%' &&
4929 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004930 (lo = TwoDigitHex(source->Get(i + 1),
4931 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004932 *step = 3;
4933 return lo;
4934 } else {
4935 *step = 1;
4936 return character;
4937 }
4938}
4939
4940
4941static Object* Runtime_URIUnescape(Arguments args) {
4942 NoHandleAllocation ha;
4943 ASSERT(args.length() == 1);
4944 CONVERT_CHECKED(String, source, args[0]);
4945
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004946 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004947
4948 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004949 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004950
4951 int unescaped_length = 0;
4952 for (int i = 0; i < length; unescaped_length++) {
4953 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004954 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004955 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004956 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004957 i += step;
4958 }
4959
4960 // No length change implies no change. Return original string if no change.
4961 if (unescaped_length == length)
4962 return source;
4963
4964 Object* o = ascii ?
4965 Heap::AllocateRawAsciiString(unescaped_length) :
4966 Heap::AllocateRawTwoByteString(unescaped_length);
4967 if (o->IsFailure()) return o;
4968 String* destination = String::cast(o);
4969
4970 int dest_position = 0;
4971 for (int i = 0; i < length; dest_position++) {
4972 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004973 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004974 i += step;
4975 }
4976 return destination;
4977}
4978
4979
4980static Object* Runtime_StringParseInt(Arguments args) {
4981 NoHandleAllocation ha;
4982
4983 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004984 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004985
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004986 s->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004987
lrn@chromium.org25156de2010-04-06 13:10:27 +00004988 RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
4989 double value = StringToInt(s, radix);
4990 return Heap::NumberFromDouble(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004991 return Heap::nan_value();
4992}
4993
4994
4995static Object* Runtime_StringParseFloat(Arguments args) {
4996 NoHandleAllocation ha;
4997 CONVERT_CHECKED(String, str, args[0]);
4998
4999 // ECMA-262 section 15.1.2.3, empty string is NaN
5000 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
5001
5002 // Create a number object from the value.
5003 return Heap::NumberFromDouble(value);
5004}
5005
5006
5007static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
5008static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
5009
5010
5011template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005012static Object* ConvertCaseHelper(String* s,
5013 int length,
5014 int input_string_length,
5015 unibrow::Mapping<Converter, 128>* mapping) {
5016 // We try this twice, once with the assumption that the result is no longer
5017 // than the input and, if that assumption breaks, again with the exact
5018 // length. This may not be pretty, but it is nicer than what was here before
5019 // and I hereby claim my vaffel-is.
5020 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005021 // Allocate the resulting string.
5022 //
5023 // NOTE: This assumes that the upper/lower case of an ascii
5024 // character is also ascii. This is currently the case, but it
5025 // might break in the future if we implement more context and locale
5026 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00005027 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005028 ? Heap::AllocateRawAsciiString(length)
5029 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005030 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005031 String* result = String::cast(o);
5032 bool has_changed_character = false;
5033
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005034 // Convert all characters to upper case, assuming that they will fit
5035 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005036 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005037 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005038 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005039 // We can assume that the string is not empty
5040 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005041 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00005042 bool has_next = buffer->has_more();
5043 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005044 int char_length = mapping->get(current, next, chars);
5045 if (char_length == 0) {
5046 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005047 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005048 i++;
5049 } else if (char_length == 1) {
5050 // Common case: converting the letter resulted in one character.
5051 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005052 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005053 has_changed_character = true;
5054 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005055 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005056 // We've assumed that the result would be as long as the
5057 // input but here is a character that converts to several
5058 // characters. No matter, we calculate the exact length
5059 // of the result and try the whole thing again.
5060 //
5061 // Note that this leaves room for optimization. We could just
5062 // memcpy what we already have to the result string. Also,
5063 // the result string is the last object allocated we could
5064 // "realloc" it and probably, in the vast majority of cases,
5065 // extend the existing string to be able to hold the full
5066 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00005067 int next_length = 0;
5068 if (has_next) {
5069 next_length = mapping->get(next, 0, chars);
5070 if (next_length == 0) next_length = 1;
5071 }
5072 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005073 while (buffer->has_more()) {
5074 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00005075 // NOTE: we use 0 as the next character here because, while
5076 // the next character may affect what a character converts to,
5077 // it does not in any case affect the length of what it convert
5078 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005079 int char_length = mapping->get(current, 0, chars);
5080 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00005081 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005082 if (current_length > Smi::kMaxValue) {
5083 Top::context()->mark_out_of_memory();
5084 return Failure::OutOfMemoryException();
5085 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005086 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005087 // Try again with the real length.
5088 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005089 } else {
5090 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005091 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005092 i++;
5093 }
5094 has_changed_character = true;
5095 }
5096 current = next;
5097 }
5098 if (has_changed_character) {
5099 return result;
5100 } else {
5101 // If we didn't actually change anything in doing the conversion
5102 // we simple return the result and let the converted string
5103 // become garbage; there is no reason to keep two identical strings
5104 // alive.
5105 return s;
5106 }
5107}
5108
5109
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005110namespace {
5111
5112struct ToLowerTraits {
5113 typedef unibrow::ToLowercase UnibrowConverter;
5114
5115 static bool ConvertAscii(char* dst, char* src, int length) {
5116 bool changed = false;
5117 for (int i = 0; i < length; ++i) {
5118 char c = src[i];
5119 if ('A' <= c && c <= 'Z') {
5120 c += ('a' - 'A');
5121 changed = true;
5122 }
5123 dst[i] = c;
5124 }
5125 return changed;
5126 }
5127};
5128
5129
5130struct ToUpperTraits {
5131 typedef unibrow::ToUppercase UnibrowConverter;
5132
5133 static bool ConvertAscii(char* dst, char* src, int length) {
5134 bool changed = false;
5135 for (int i = 0; i < length; ++i) {
5136 char c = src[i];
5137 if ('a' <= c && c <= 'z') {
5138 c -= ('a' - 'A');
5139 changed = true;
5140 }
5141 dst[i] = c;
5142 }
5143 return changed;
5144 }
5145};
5146
5147} // namespace
5148
5149
5150template <typename ConvertTraits>
5151static Object* ConvertCase(
5152 Arguments args,
5153 unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005154 NoHandleAllocation ha;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005155 CONVERT_CHECKED(String, s, args[0]);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005156 s = s->TryFlattenGetString();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005157
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005158 const int length = s->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005159 // Assume that the string is not empty; we need this assumption later
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005160 if (length == 0) return s;
5161
5162 // Simpler handling of ascii strings.
5163 //
5164 // NOTE: This assumes that the upper/lower case of an ascii
5165 // character is also ascii. This is currently the case, but it
5166 // might break in the future if we implement more context and locale
5167 // dependent upper/lower conversions.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005168 if (s->IsSeqAsciiString()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005169 Object* o = Heap::AllocateRawAsciiString(length);
5170 if (o->IsFailure()) return o;
5171 SeqAsciiString* result = SeqAsciiString::cast(o);
5172 bool has_changed_character = ConvertTraits::ConvertAscii(
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005173 result->GetChars(), SeqAsciiString::cast(s)->GetChars(), length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005174 return has_changed_character ? result : s;
5175 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005176
5177 Object* answer = ConvertCaseHelper(s, length, length, mapping);
5178 if (answer->IsSmi()) {
5179 // Retry with correct length.
5180 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
5181 }
5182 return answer; // This may be a failure.
5183}
5184
5185
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005186static Object* Runtime_StringToLowerCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005187 return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005188}
5189
5190
5191static Object* Runtime_StringToUpperCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005192 return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005193}
5194
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005195
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005196static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
5197 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
5198}
5199
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005200
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005201static Object* Runtime_StringTrim(Arguments args) {
5202 NoHandleAllocation ha;
5203 ASSERT(args.length() == 3);
5204
5205 CONVERT_CHECKED(String, s, args[0]);
5206 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
5207 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
5208
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005209 s->TryFlatten();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005210 int length = s->length();
5211
5212 int left = 0;
5213 if (trimLeft) {
5214 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
5215 left++;
5216 }
5217 }
5218
5219 int right = length;
5220 if (trimRight) {
5221 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
5222 right--;
5223 }
5224 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005225 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005226}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005227
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005228
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005229template <typename schar, typename pchar>
5230void FindStringIndices(Vector<const schar> subject,
5231 Vector<const pchar> pattern,
5232 ZoneList<int>* indices,
5233 unsigned int limit) {
5234 ASSERT(limit > 0);
5235 // Collect indices of pattern in subject, and the end-of-string index.
5236 // Stop after finding at most limit values.
5237 StringSearchStrategy strategy =
5238 InitializeStringSearch(pattern, sizeof(schar) == 1);
5239 switch (strategy) {
5240 case SEARCH_FAIL: return;
5241 case SEARCH_SHORT: {
5242 int pattern_length = pattern.length();
5243 int index = 0;
5244 while (limit > 0) {
5245 index = SimpleIndexOf(subject, pattern, index);
5246 if (index < 0) return;
5247 indices->Add(index);
5248 index += pattern_length;
5249 limit--;
5250 }
5251 return;
5252 }
5253 case SEARCH_LONG: {
5254 int pattern_length = pattern.length();
5255 int index = 0;
5256 while (limit > 0) {
5257 index = ComplexIndexOf(subject, pattern, index);
5258 if (index < 0) return;
5259 indices->Add(index);
5260 index += pattern_length;
5261 limit--;
5262 }
5263 return;
5264 }
5265 default:
5266 UNREACHABLE();
5267 return;
5268 }
5269}
5270
5271template <typename schar>
5272inline void FindCharIndices(Vector<const schar> subject,
5273 const schar pattern_char,
5274 ZoneList<int>* indices,
5275 unsigned int limit) {
5276 // Collect indices of pattern_char in subject, and the end-of-string index.
5277 // Stop after finding at most limit values.
5278 int index = 0;
5279 while (limit > 0) {
5280 index = SingleCharIndexOf(subject, pattern_char, index);
5281 if (index < 0) return;
5282 indices->Add(index);
5283 index++;
5284 limit--;
5285 }
5286}
5287
5288
5289static Object* Runtime_StringSplit(Arguments args) {
5290 ASSERT(args.length() == 3);
5291 HandleScope handle_scope;
5292 CONVERT_ARG_CHECKED(String, subject, 0);
5293 CONVERT_ARG_CHECKED(String, pattern, 1);
5294 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
5295
5296 int subject_length = subject->length();
5297 int pattern_length = pattern->length();
5298 RUNTIME_ASSERT(pattern_length > 0);
5299
5300 // The limit can be very large (0xffffffffu), but since the pattern
5301 // isn't empty, we can never create more parts than ~half the length
5302 // of the subject.
5303
5304 if (!subject->IsFlat()) FlattenString(subject);
5305
5306 static const int kMaxInitialListCapacity = 16;
5307
5308 ZoneScope scope(DELETE_ON_EXIT);
5309
5310 // Find (up to limit) indices of separator and end-of-string in subject
5311 int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
5312 ZoneList<int> indices(initial_capacity);
5313 if (pattern_length == 1) {
5314 // Special case, go directly to fast single-character split.
5315 AssertNoAllocation nogc;
5316 uc16 pattern_char = pattern->Get(0);
5317 if (subject->IsTwoByteRepresentation()) {
5318 FindCharIndices(subject->ToUC16Vector(), pattern_char,
5319 &indices,
5320 limit);
5321 } else if (pattern_char <= String::kMaxAsciiCharCode) {
5322 FindCharIndices(subject->ToAsciiVector(),
5323 static_cast<char>(pattern_char),
5324 &indices,
5325 limit);
5326 }
5327 } else {
5328 if (!pattern->IsFlat()) FlattenString(pattern);
5329 AssertNoAllocation nogc;
5330 if (subject->IsAsciiRepresentation()) {
5331 Vector<const char> subject_vector = subject->ToAsciiVector();
5332 if (pattern->IsAsciiRepresentation()) {
5333 FindStringIndices(subject_vector,
5334 pattern->ToAsciiVector(),
5335 &indices,
5336 limit);
5337 } else {
5338 FindStringIndices(subject_vector,
5339 pattern->ToUC16Vector(),
5340 &indices,
5341 limit);
5342 }
5343 } else {
5344 Vector<const uc16> subject_vector = subject->ToUC16Vector();
5345 if (pattern->IsAsciiRepresentation()) {
5346 FindStringIndices(subject_vector,
5347 pattern->ToAsciiVector(),
5348 &indices,
5349 limit);
5350 } else {
5351 FindStringIndices(subject_vector,
5352 pattern->ToUC16Vector(),
5353 &indices,
5354 limit);
5355 }
5356 }
5357 }
5358 if (static_cast<uint32_t>(indices.length()) < limit) {
5359 indices.Add(subject_length);
5360 }
5361 // The list indices now contains the end of each part to create.
5362
5363
5364 // Create JSArray of substrings separated by separator.
5365 int part_count = indices.length();
5366
5367 Handle<JSArray> result = Factory::NewJSArray(part_count);
5368 result->set_length(Smi::FromInt(part_count));
5369
5370 ASSERT(result->HasFastElements());
5371
5372 if (part_count == 1 && indices.at(0) == subject_length) {
5373 FixedArray::cast(result->elements())->set(0, *subject);
5374 return *result;
5375 }
5376
5377 Handle<FixedArray> elements(FixedArray::cast(result->elements()));
5378 int part_start = 0;
5379 for (int i = 0; i < part_count; i++) {
5380 HandleScope local_loop_handle;
5381 int part_end = indices.at(i);
5382 Handle<String> substring =
5383 Factory::NewSubString(subject, part_start, part_end);
5384 elements->set(i, *substring);
5385 part_start = part_end + pattern_length;
5386 }
5387
5388 return *result;
5389}
5390
5391
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005392// Copies ascii characters to the given fixed array looking up
5393// one-char strings in the cache. Gives up on the first char that is
5394// not in the cache and fills the remainder with smi zeros. Returns
5395// the length of the successfully copied prefix.
5396static int CopyCachedAsciiCharsToArray(const char* chars,
5397 FixedArray* elements,
5398 int length) {
5399 AssertNoAllocation nogc;
5400 FixedArray* ascii_cache = Heap::single_character_string_cache();
5401 Object* undefined = Heap::undefined_value();
5402 int i;
5403 for (i = 0; i < length; ++i) {
5404 Object* value = ascii_cache->get(chars[i]);
5405 if (value == undefined) break;
5406 ASSERT(!Heap::InNewSpace(value));
5407 elements->set(i, value, SKIP_WRITE_BARRIER);
5408 }
5409 if (i < length) {
5410 ASSERT(Smi::FromInt(0) == 0);
5411 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
5412 }
5413#ifdef DEBUG
5414 for (int j = 0; j < length; ++j) {
5415 Object* element = elements->get(j);
5416 ASSERT(element == Smi::FromInt(0) ||
5417 (element->IsString() && String::cast(element)->LooksValid()));
5418 }
5419#endif
5420 return i;
5421}
5422
5423
5424// Converts a String to JSArray.
5425// For example, "foo" => ["f", "o", "o"].
5426static Object* Runtime_StringToArray(Arguments args) {
5427 HandleScope scope;
5428 ASSERT(args.length() == 1);
5429 CONVERT_ARG_CHECKED(String, s, 0);
5430
5431 s->TryFlatten();
5432 const int length = s->length();
5433
5434 Handle<FixedArray> elements;
5435 if (s->IsFlat() && s->IsAsciiRepresentation()) {
5436 Object* obj = Heap::AllocateUninitializedFixedArray(length);
5437 if (obj->IsFailure()) return obj;
5438 elements = Handle<FixedArray>(FixedArray::cast(obj));
5439
5440 Vector<const char> chars = s->ToAsciiVector();
5441 // Note, this will initialize all elements (not only the prefix)
5442 // to prevent GC from seeing partially initialized array.
5443 int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
5444 *elements,
5445 length);
5446
5447 for (int i = num_copied_from_cache; i < length; ++i) {
5448 elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
5449 }
5450 } else {
5451 elements = Factory::NewFixedArray(length);
5452 for (int i = 0; i < length; ++i) {
5453 elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
5454 }
5455 }
5456
5457#ifdef DEBUG
5458 for (int i = 0; i < length; ++i) {
5459 ASSERT(String::cast(elements->get(i))->length() == 1);
5460 }
5461#endif
5462
5463 return *Factory::NewJSArrayWithElements(elements);
5464}
5465
5466
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00005467bool Runtime::IsUpperCaseChar(uint16_t ch) {
5468 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
5469 int char_length = to_upper_mapping.get(ch, 0, chars);
5470 return char_length == 0;
5471}
5472
5473
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005474static Object* Runtime_NumberToString(Arguments args) {
5475 NoHandleAllocation ha;
5476 ASSERT(args.length() == 1);
5477
5478 Object* number = args[0];
5479 RUNTIME_ASSERT(number->IsNumber());
5480
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005481 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005482}
5483
5484
ager@chromium.org357bf652010-04-12 11:30:10 +00005485static Object* Runtime_NumberToStringSkipCache(Arguments args) {
5486 NoHandleAllocation ha;
5487 ASSERT(args.length() == 1);
5488
5489 Object* number = args[0];
5490 RUNTIME_ASSERT(number->IsNumber());
5491
5492 return Heap::NumberToString(number, false);
5493}
5494
5495
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005496static Object* Runtime_NumberToInteger(Arguments args) {
5497 NoHandleAllocation ha;
5498 ASSERT(args.length() == 1);
5499
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005500 CONVERT_DOUBLE_CHECKED(number, args[0]);
5501
5502 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5503 if (number > 0 && number <= Smi::kMaxValue) {
5504 return Smi::FromInt(static_cast<int>(number));
5505 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005506 return Heap::NumberFromDouble(DoubleToInteger(number));
5507}
5508
5509
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005510static Object* Runtime_NumberToIntegerMapMinusZero(Arguments args) {
5511 NoHandleAllocation ha;
5512 ASSERT(args.length() == 1);
5513
5514 CONVERT_DOUBLE_CHECKED(number, args[0]);
5515
5516 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5517 if (number > 0 && number <= Smi::kMaxValue) {
5518 return Smi::FromInt(static_cast<int>(number));
5519 }
5520
5521 double double_value = DoubleToInteger(number);
5522 // Map both -0 and +0 to +0.
5523 if (double_value == 0) double_value = 0;
5524
5525 return Heap::NumberFromDouble(double_value);
5526}
5527
5528
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005529static Object* Runtime_NumberToJSUint32(Arguments args) {
5530 NoHandleAllocation ha;
5531 ASSERT(args.length() == 1);
5532
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005533 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005534 return Heap::NumberFromUint32(number);
5535}
5536
5537
5538static Object* Runtime_NumberToJSInt32(Arguments args) {
5539 NoHandleAllocation ha;
5540 ASSERT(args.length() == 1);
5541
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005542 CONVERT_DOUBLE_CHECKED(number, args[0]);
5543
5544 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5545 if (number > 0 && number <= Smi::kMaxValue) {
5546 return Smi::FromInt(static_cast<int>(number));
5547 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005548 return Heap::NumberFromInt32(DoubleToInt32(number));
5549}
5550
5551
ager@chromium.org870a0b62008-11-04 11:43:05 +00005552// Converts a Number to a Smi, if possible. Returns NaN if the number is not
5553// a small integer.
5554static Object* Runtime_NumberToSmi(Arguments args) {
5555 NoHandleAllocation ha;
5556 ASSERT(args.length() == 1);
5557
5558 Object* obj = args[0];
5559 if (obj->IsSmi()) {
5560 return obj;
5561 }
5562 if (obj->IsHeapNumber()) {
5563 double value = HeapNumber::cast(obj)->value();
5564 int int_value = FastD2I(value);
5565 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
5566 return Smi::FromInt(int_value);
5567 }
5568 }
5569 return Heap::nan_value();
5570}
5571
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005572
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005573static Object* Runtime_NumberAdd(Arguments args) {
5574 NoHandleAllocation ha;
5575 ASSERT(args.length() == 2);
5576
5577 CONVERT_DOUBLE_CHECKED(x, args[0]);
5578 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005579 return Heap::NumberFromDouble(x + y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005580}
5581
5582
5583static Object* Runtime_NumberSub(Arguments args) {
5584 NoHandleAllocation ha;
5585 ASSERT(args.length() == 2);
5586
5587 CONVERT_DOUBLE_CHECKED(x, args[0]);
5588 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005589 return Heap::NumberFromDouble(x - y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005590}
5591
5592
5593static Object* Runtime_NumberMul(Arguments args) {
5594 NoHandleAllocation ha;
5595 ASSERT(args.length() == 2);
5596
5597 CONVERT_DOUBLE_CHECKED(x, args[0]);
5598 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005599 return Heap::NumberFromDouble(x * y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005600}
5601
5602
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005603static Object* Runtime_NumberUnaryMinus(Arguments args) {
5604 NoHandleAllocation ha;
5605 ASSERT(args.length() == 1);
5606
5607 CONVERT_DOUBLE_CHECKED(x, args[0]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005608 return Heap::NumberFromDouble(-x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005609}
5610
5611
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00005612static Object* Runtime_NumberAlloc(Arguments args) {
5613 NoHandleAllocation ha;
5614 ASSERT(args.length() == 0);
5615
5616 return Heap::NumberFromDouble(9876543210.0);
5617}
5618
5619
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005620static Object* Runtime_NumberDiv(Arguments args) {
5621 NoHandleAllocation ha;
5622 ASSERT(args.length() == 2);
5623
5624 CONVERT_DOUBLE_CHECKED(x, args[0]);
5625 CONVERT_DOUBLE_CHECKED(y, args[1]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005626 return Heap::NumberFromDouble(x / y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005627}
5628
5629
5630static Object* Runtime_NumberMod(Arguments args) {
5631 NoHandleAllocation ha;
5632 ASSERT(args.length() == 2);
5633
5634 CONVERT_DOUBLE_CHECKED(x, args[0]);
5635 CONVERT_DOUBLE_CHECKED(y, args[1]);
5636
ager@chromium.org3811b432009-10-28 14:53:37 +00005637 x = modulo(x, y);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005638 // NumberFromDouble may return a Smi instead of a Number object
5639 return Heap::NumberFromDouble(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005640}
5641
5642
5643static Object* Runtime_StringAdd(Arguments args) {
5644 NoHandleAllocation ha;
5645 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005646 CONVERT_CHECKED(String, str1, args[0]);
5647 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005648 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00005649 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005650}
5651
5652
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005653template <typename sinkchar>
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005654static inline void StringBuilderConcatHelper(String* special,
5655 sinkchar* sink,
5656 FixedArray* fixed_array,
5657 int array_length) {
5658 int position = 0;
5659 for (int i = 0; i < array_length; i++) {
5660 Object* element = fixed_array->get(i);
5661 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005662 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005663 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005664 int pos;
5665 int len;
5666 if (encoded_slice > 0) {
5667 // Position and length encoded in one smi.
5668 pos = StringBuilderSubstringPosition::decode(encoded_slice);
5669 len = StringBuilderSubstringLength::decode(encoded_slice);
5670 } else {
5671 // Position and length encoded in two smis.
5672 Object* obj = fixed_array->get(++i);
5673 ASSERT(obj->IsSmi());
5674 pos = Smi::cast(obj)->value();
5675 len = -encoded_slice;
5676 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00005677 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00005678 sink + position,
5679 pos,
5680 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005681 position += len;
5682 } else {
5683 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005684 int element_length = string->length();
5685 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005686 position += element_length;
5687 }
5688 }
5689}
5690
5691
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005692static Object* Runtime_StringBuilderConcat(Arguments args) {
5693 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005694 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005695 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005696 if (!args[1]->IsSmi()) {
5697 Top::context()->mark_out_of_memory();
5698 return Failure::OutOfMemoryException();
5699 }
5700 int array_length = Smi::cast(args[1])->value();
5701 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005702
5703 // This assumption is used by the slice encoding in one or two smis.
5704 ASSERT(Smi::kMaxValue >= String::kMaxLength);
5705
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005706 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005707 if (!array->HasFastElements()) {
5708 return Top::Throw(Heap::illegal_argument_symbol());
5709 }
5710 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005711 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005712 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005713 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005714
5715 if (array_length == 0) {
5716 return Heap::empty_string();
5717 } else if (array_length == 1) {
5718 Object* first = fixed_array->get(0);
5719 if (first->IsString()) return first;
5720 }
5721
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005722 bool ascii = special->HasOnlyAsciiChars();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005723 int position = 0;
5724 for (int i = 0; i < array_length; i++) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005725 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005726 Object* elt = fixed_array->get(i);
5727 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005728 // Smi encoding of position and length.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005729 int smi_value = Smi::cast(elt)->value();
5730 int pos;
5731 int len;
5732 if (smi_value > 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005733 // Position and length encoded in one smi.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005734 pos = StringBuilderSubstringPosition::decode(smi_value);
5735 len = StringBuilderSubstringLength::decode(smi_value);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005736 } else {
5737 // Position and length encoded in two smis.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005738 len = -smi_value;
5739 // Get the position and check that it is a positive smi.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005740 i++;
5741 if (i >= array_length) {
5742 return Top::Throw(Heap::illegal_argument_symbol());
5743 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005744 Object* next_smi = fixed_array->get(i);
5745 if (!next_smi->IsSmi()) {
5746 return Top::Throw(Heap::illegal_argument_symbol());
5747 }
5748 pos = Smi::cast(next_smi)->value();
5749 if (pos < 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005750 return Top::Throw(Heap::illegal_argument_symbol());
5751 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005752 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005753 ASSERT(pos >= 0);
5754 ASSERT(len >= 0);
5755 if (pos > special_length || len > special_length - pos) {
5756 return Top::Throw(Heap::illegal_argument_symbol());
5757 }
5758 increment = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005759 } else if (elt->IsString()) {
5760 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005761 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005762 increment = element_length;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005763 if (ascii && !element->HasOnlyAsciiChars()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005764 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005765 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005766 } else {
5767 return Top::Throw(Heap::illegal_argument_symbol());
5768 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005769 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005770 Top::context()->mark_out_of_memory();
5771 return Failure::OutOfMemoryException();
5772 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005773 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005774 }
5775
5776 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005777 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005778
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005779 if (ascii) {
5780 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005781 if (object->IsFailure()) return object;
5782 SeqAsciiString* answer = SeqAsciiString::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 } else {
5789 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005790 if (object->IsFailure()) return object;
5791 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
5792 StringBuilderConcatHelper(special,
5793 answer->GetChars(),
5794 fixed_array,
5795 array_length);
5796 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005797 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005798}
5799
5800
5801static Object* Runtime_NumberOr(Arguments args) {
5802 NoHandleAllocation ha;
5803 ASSERT(args.length() == 2);
5804
5805 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5806 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5807 return Heap::NumberFromInt32(x | y);
5808}
5809
5810
5811static Object* Runtime_NumberAnd(Arguments args) {
5812 NoHandleAllocation ha;
5813 ASSERT(args.length() == 2);
5814
5815 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5816 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5817 return Heap::NumberFromInt32(x & y);
5818}
5819
5820
5821static Object* Runtime_NumberXor(Arguments args) {
5822 NoHandleAllocation ha;
5823 ASSERT(args.length() == 2);
5824
5825 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5826 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5827 return Heap::NumberFromInt32(x ^ y);
5828}
5829
5830
5831static Object* Runtime_NumberNot(Arguments args) {
5832 NoHandleAllocation ha;
5833 ASSERT(args.length() == 1);
5834
5835 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5836 return Heap::NumberFromInt32(~x);
5837}
5838
5839
5840static Object* Runtime_NumberShl(Arguments args) {
5841 NoHandleAllocation ha;
5842 ASSERT(args.length() == 2);
5843
5844 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5845 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5846 return Heap::NumberFromInt32(x << (y & 0x1f));
5847}
5848
5849
5850static Object* Runtime_NumberShr(Arguments args) {
5851 NoHandleAllocation ha;
5852 ASSERT(args.length() == 2);
5853
5854 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
5855 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5856 return Heap::NumberFromUint32(x >> (y & 0x1f));
5857}
5858
5859
5860static Object* Runtime_NumberSar(Arguments args) {
5861 NoHandleAllocation ha;
5862 ASSERT(args.length() == 2);
5863
5864 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5865 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5866 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
5867}
5868
5869
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005870static Object* Runtime_NumberEquals(Arguments args) {
5871 NoHandleAllocation ha;
5872 ASSERT(args.length() == 2);
5873
5874 CONVERT_DOUBLE_CHECKED(x, args[0]);
5875 CONVERT_DOUBLE_CHECKED(y, args[1]);
5876 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
5877 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
5878 if (x == y) return Smi::FromInt(EQUAL);
5879 Object* result;
5880 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
5881 result = Smi::FromInt(EQUAL);
5882 } else {
5883 result = Smi::FromInt(NOT_EQUAL);
5884 }
5885 return result;
5886}
5887
5888
5889static Object* Runtime_StringEquals(Arguments args) {
5890 NoHandleAllocation ha;
5891 ASSERT(args.length() == 2);
5892
5893 CONVERT_CHECKED(String, x, args[0]);
5894 CONVERT_CHECKED(String, y, args[1]);
5895
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005896 bool not_equal = !x->Equals(y);
5897 // This is slightly convoluted because the value that signifies
5898 // equality is 0 and inequality is 1 so we have to negate the result
5899 // from String::Equals.
5900 ASSERT(not_equal == 0 || not_equal == 1);
5901 STATIC_CHECK(EQUAL == 0);
5902 STATIC_CHECK(NOT_EQUAL == 1);
5903 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005904}
5905
5906
5907static Object* Runtime_NumberCompare(Arguments args) {
5908 NoHandleAllocation ha;
5909 ASSERT(args.length() == 3);
5910
5911 CONVERT_DOUBLE_CHECKED(x, args[0]);
5912 CONVERT_DOUBLE_CHECKED(y, args[1]);
5913 if (isnan(x) || isnan(y)) return args[2];
5914 if (x == y) return Smi::FromInt(EQUAL);
5915 if (isless(x, y)) return Smi::FromInt(LESS);
5916 return Smi::FromInt(GREATER);
5917}
5918
5919
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005920// Compare two Smis as if they were converted to strings and then
5921// compared lexicographically.
5922static Object* Runtime_SmiLexicographicCompare(Arguments args) {
5923 NoHandleAllocation ha;
5924 ASSERT(args.length() == 2);
5925
5926 // Arrays for the individual characters of the two Smis. Smis are
5927 // 31 bit integers and 10 decimal digits are therefore enough.
5928 static int x_elms[10];
5929 static int y_elms[10];
5930
5931 // Extract the integer values from the Smis.
5932 CONVERT_CHECKED(Smi, x, args[0]);
5933 CONVERT_CHECKED(Smi, y, args[1]);
5934 int x_value = x->value();
5935 int y_value = y->value();
5936
5937 // If the integers are equal so are the string representations.
5938 if (x_value == y_value) return Smi::FromInt(EQUAL);
5939
5940 // If one of the integers are zero the normal integer order is the
5941 // same as the lexicographic order of the string representations.
5942 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
5943
ager@chromium.org32912102009-01-16 10:38:43 +00005944 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005945 // smallest because the char code of '-' is less than the char code
5946 // of any digit. Otherwise, we make both values positive.
5947 if (x_value < 0 || y_value < 0) {
5948 if (y_value >= 0) return Smi::FromInt(LESS);
5949 if (x_value >= 0) return Smi::FromInt(GREATER);
5950 x_value = -x_value;
5951 y_value = -y_value;
5952 }
5953
5954 // Convert the integers to arrays of their decimal digits.
5955 int x_index = 0;
5956 int y_index = 0;
5957 while (x_value > 0) {
5958 x_elms[x_index++] = x_value % 10;
5959 x_value /= 10;
5960 }
5961 while (y_value > 0) {
5962 y_elms[y_index++] = y_value % 10;
5963 y_value /= 10;
5964 }
5965
5966 // Loop through the arrays of decimal digits finding the first place
5967 // where they differ.
5968 while (--x_index >= 0 && --y_index >= 0) {
5969 int diff = x_elms[x_index] - y_elms[y_index];
5970 if (diff != 0) return Smi::FromInt(diff);
5971 }
5972
5973 // If one array is a suffix of the other array, the longest array is
5974 // the representation of the largest of the Smis in the
5975 // lexicographic ordering.
5976 return Smi::FromInt(x_index - y_index);
5977}
5978
5979
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005980static Object* StringInputBufferCompare(String* x, String* y) {
5981 static StringInputBuffer bufx;
5982 static StringInputBuffer bufy;
5983 bufx.Reset(x);
5984 bufy.Reset(y);
5985 while (bufx.has_more() && bufy.has_more()) {
5986 int d = bufx.GetNext() - bufy.GetNext();
5987 if (d < 0) return Smi::FromInt(LESS);
5988 else if (d > 0) return Smi::FromInt(GREATER);
5989 }
5990
5991 // x is (non-trivial) prefix of y:
5992 if (bufy.has_more()) return Smi::FromInt(LESS);
5993 // y is prefix of x:
5994 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
5995}
5996
5997
5998static Object* FlatStringCompare(String* x, String* y) {
5999 ASSERT(x->IsFlat());
6000 ASSERT(y->IsFlat());
6001 Object* equal_prefix_result = Smi::FromInt(EQUAL);
6002 int prefix_length = x->length();
6003 if (y->length() < prefix_length) {
6004 prefix_length = y->length();
6005 equal_prefix_result = Smi::FromInt(GREATER);
6006 } else if (y->length() > prefix_length) {
6007 equal_prefix_result = Smi::FromInt(LESS);
6008 }
6009 int r;
6010 if (x->IsAsciiRepresentation()) {
6011 Vector<const char> x_chars = x->ToAsciiVector();
6012 if (y->IsAsciiRepresentation()) {
6013 Vector<const char> y_chars = y->ToAsciiVector();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006014 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006015 } else {
6016 Vector<const uc16> y_chars = y->ToUC16Vector();
6017 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
6018 }
6019 } else {
6020 Vector<const uc16> x_chars = x->ToUC16Vector();
6021 if (y->IsAsciiRepresentation()) {
6022 Vector<const char> y_chars = y->ToAsciiVector();
6023 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
6024 } else {
6025 Vector<const uc16> y_chars = y->ToUC16Vector();
6026 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
6027 }
6028 }
6029 Object* result;
6030 if (r == 0) {
6031 result = equal_prefix_result;
6032 } else {
6033 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
6034 }
6035 ASSERT(result == StringInputBufferCompare(x, y));
6036 return result;
6037}
6038
6039
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006040static Object* Runtime_StringCompare(Arguments args) {
6041 NoHandleAllocation ha;
6042 ASSERT(args.length() == 2);
6043
6044 CONVERT_CHECKED(String, x, args[0]);
6045 CONVERT_CHECKED(String, y, args[1]);
6046
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006047 Counters::string_compare_runtime.Increment();
6048
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006049 // A few fast case tests before we flatten.
6050 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006051 if (y->length() == 0) {
6052 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006053 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006054 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006055 return Smi::FromInt(LESS);
6056 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006057
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006058 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006059 if (d < 0) return Smi::FromInt(LESS);
6060 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006061
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006062 Object* obj = Heap::PrepareForCompare(x);
6063 if (obj->IsFailure()) return obj;
6064 obj = Heap::PrepareForCompare(y);
6065 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006066
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006067 return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
6068 : StringInputBufferCompare(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006069}
6070
6071
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006072static Object* Runtime_Math_acos(Arguments args) {
6073 NoHandleAllocation ha;
6074 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006075 Counters::math_acos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006076
6077 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006078 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006079}
6080
6081
6082static Object* Runtime_Math_asin(Arguments args) {
6083 NoHandleAllocation ha;
6084 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006085 Counters::math_asin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006086
6087 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006088 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006089}
6090
6091
6092static Object* Runtime_Math_atan(Arguments args) {
6093 NoHandleAllocation ha;
6094 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006095 Counters::math_atan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006096
6097 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006098 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006099}
6100
6101
6102static Object* Runtime_Math_atan2(Arguments args) {
6103 NoHandleAllocation ha;
6104 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006105 Counters::math_atan2.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006106
6107 CONVERT_DOUBLE_CHECKED(x, args[0]);
6108 CONVERT_DOUBLE_CHECKED(y, args[1]);
6109 double result;
6110 if (isinf(x) && isinf(y)) {
6111 // Make sure that the result in case of two infinite arguments
6112 // is a multiple of Pi / 4. The sign of the result is determined
6113 // by the first argument (x) and the sign of the second argument
6114 // determines the multiplier: one or three.
6115 static double kPiDividedBy4 = 0.78539816339744830962;
6116 int multiplier = (x < 0) ? -1 : 1;
6117 if (y < 0) multiplier *= 3;
6118 result = multiplier * kPiDividedBy4;
6119 } else {
6120 result = atan2(x, y);
6121 }
6122 return Heap::AllocateHeapNumber(result);
6123}
6124
6125
6126static Object* Runtime_Math_ceil(Arguments args) {
6127 NoHandleAllocation ha;
6128 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006129 Counters::math_ceil.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006130
6131 CONVERT_DOUBLE_CHECKED(x, args[0]);
6132 return Heap::NumberFromDouble(ceiling(x));
6133}
6134
6135
6136static Object* Runtime_Math_cos(Arguments args) {
6137 NoHandleAllocation ha;
6138 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006139 Counters::math_cos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006140
6141 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006142 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006143}
6144
6145
6146static Object* Runtime_Math_exp(Arguments args) {
6147 NoHandleAllocation ha;
6148 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006149 Counters::math_exp.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006150
6151 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006152 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006153}
6154
6155
6156static Object* Runtime_Math_floor(Arguments args) {
6157 NoHandleAllocation ha;
6158 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006159 Counters::math_floor.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006160
6161 CONVERT_DOUBLE_CHECKED(x, args[0]);
6162 return Heap::NumberFromDouble(floor(x));
6163}
6164
6165
6166static Object* Runtime_Math_log(Arguments args) {
6167 NoHandleAllocation ha;
6168 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006169 Counters::math_log.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006170
6171 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006172 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006173}
6174
6175
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006176// Helper function to compute x^y, where y is known to be an
6177// integer. Uses binary decomposition to limit the number of
6178// multiplications; see the discussion in "Hacker's Delight" by Henry
6179// S. Warren, Jr., figure 11-6, page 213.
6180static double powi(double x, int y) {
6181 ASSERT(y != kMinInt);
6182 unsigned n = (y < 0) ? -y : y;
6183 double m = x;
6184 double p = 1;
6185 while (true) {
6186 if ((n & 1) != 0) p *= m;
6187 n >>= 1;
6188 if (n == 0) {
6189 if (y < 0) {
6190 // Unfortunately, we have to be careful when p has reached
6191 // infinity in the computation, because sometimes the higher
6192 // internal precision in the pow() implementation would have
6193 // given us a finite p. This happens very rarely.
6194 double result = 1.0 / p;
6195 return (result == 0 && isinf(p))
6196 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
6197 : result;
6198 } else {
6199 return p;
6200 }
6201 }
6202 m *= m;
6203 }
6204}
6205
6206
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006207static Object* Runtime_Math_pow(Arguments args) {
6208 NoHandleAllocation ha;
6209 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006210 Counters::math_pow.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006211
6212 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006213
6214 // If the second argument is a smi, it is much faster to call the
6215 // custom powi() function than the generic pow().
6216 if (args[1]->IsSmi()) {
6217 int y = Smi::cast(args[1])->value();
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00006218 return Heap::NumberFromDouble(powi(x, y));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006219 }
6220
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006221 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00006222
6223 if (!isinf(x)) {
6224 if (y == 0.5) {
6225 // It's not uncommon to use Math.pow(x, 0.5) to compute the
6226 // square root of a number. To speed up such computations, we
6227 // explictly check for this case and use the sqrt() function
6228 // which is faster than pow().
6229 return Heap::AllocateHeapNumber(sqrt(x));
6230 } else if (y == -0.5) {
6231 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
6232 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
6233 }
6234 }
6235
6236 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006237 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006238 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6239 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006240 } else {
6241 return Heap::AllocateHeapNumber(pow(x, y));
6242 }
6243}
6244
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006245// Fast version of Math.pow if we know that y is not an integer and
6246// y is not -0.5 or 0.5. Used as slowcase from codegen.
6247static Object* Runtime_Math_pow_cfunction(Arguments args) {
6248 NoHandleAllocation ha;
6249 ASSERT(args.length() == 2);
6250 CONVERT_DOUBLE_CHECKED(x, args[0]);
6251 CONVERT_DOUBLE_CHECKED(y, args[1]);
6252 if (y == 0) {
6253 return Smi::FromInt(1);
6254 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6255 return Heap::nan_value();
6256 } else {
6257 return Heap::AllocateHeapNumber(pow(x, y));
6258 }
6259}
6260
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006261
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006262static Object* Runtime_RoundNumber(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006263 NoHandleAllocation ha;
6264 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006265 Counters::math_round.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006266
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006267 if (!args[0]->IsHeapNumber()) {
6268 // Must be smi. Return the argument unchanged for all the other types
6269 // to make fuzz-natives test happy.
6270 return args[0];
6271 }
6272
6273 HeapNumber* number = reinterpret_cast<HeapNumber*>(args[0]);
6274
6275 double value = number->value();
6276 int exponent = number->get_exponent();
6277 int sign = number->get_sign();
6278
6279 // We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
6280 // should be rounded to 2^30, which is not smi.
6281 if (!sign && exponent <= kSmiValueSize - 3) {
6282 return Smi::FromInt(static_cast<int>(value + 0.5));
6283 }
6284
6285 // If the magnitude is big enough, there's no place for fraction part. If we
6286 // try to add 0.5 to this number, 1.0 will be added instead.
6287 if (exponent >= 52) {
6288 return number;
6289 }
6290
6291 if (sign && value >= -0.5) return Heap::minus_zero_value();
6292
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006293 // Do not call NumberFromDouble() to avoid extra checks.
6294 return Heap::AllocateHeapNumber(floor(value + 0.5));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006295}
6296
6297
6298static Object* Runtime_Math_sin(Arguments args) {
6299 NoHandleAllocation ha;
6300 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006301 Counters::math_sin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006302
6303 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006304 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006305}
6306
6307
6308static Object* Runtime_Math_sqrt(Arguments args) {
6309 NoHandleAllocation ha;
6310 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006311 Counters::math_sqrt.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006312
6313 CONVERT_DOUBLE_CHECKED(x, args[0]);
6314 return Heap::AllocateHeapNumber(sqrt(x));
6315}
6316
6317
6318static Object* Runtime_Math_tan(Arguments args) {
6319 NoHandleAllocation ha;
6320 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006321 Counters::math_tan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006322
6323 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006324 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006325}
6326
6327
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006328static int MakeDay(int year, int month, int day) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006329 static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
6330 181, 212, 243, 273, 304, 334};
6331 static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
6332 182, 213, 244, 274, 305, 335};
6333
6334 year += month / 12;
6335 month %= 12;
6336 if (month < 0) {
6337 year--;
6338 month += 12;
6339 }
6340
6341 ASSERT(month >= 0);
6342 ASSERT(month < 12);
6343
6344 // year_delta is an arbitrary number such that:
6345 // a) year_delta = -1 (mod 400)
6346 // b) year + year_delta > 0 for years in the range defined by
6347 // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
6348 // Jan 1 1970. This is required so that we don't run into integer
6349 // division of negative numbers.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006350 // c) there shouldn't be an overflow for 32-bit integers in the following
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006351 // operations.
6352 static const int year_delta = 399999;
6353 static const int base_day = 365 * (1970 + year_delta) +
6354 (1970 + year_delta) / 4 -
6355 (1970 + year_delta) / 100 +
6356 (1970 + year_delta) / 400;
6357
6358 int year1 = year + year_delta;
6359 int day_from_year = 365 * year1 +
6360 year1 / 4 -
6361 year1 / 100 +
6362 year1 / 400 -
6363 base_day;
6364
6365 if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006366 return day_from_year + day_from_month[month] + day - 1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006367 }
6368
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006369 return day_from_year + day_from_month_leap[month] + day - 1;
6370}
6371
6372
6373static Object* Runtime_DateMakeDay(Arguments args) {
6374 NoHandleAllocation ha;
6375 ASSERT(args.length() == 3);
6376
6377 CONVERT_SMI_CHECKED(year, args[0]);
6378 CONVERT_SMI_CHECKED(month, args[1]);
6379 CONVERT_SMI_CHECKED(date, args[2]);
6380
6381 return Smi::FromInt(MakeDay(year, month, date));
6382}
6383
6384
6385static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
6386static const int kDaysIn4Years = 4 * 365 + 1;
6387static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
6388static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
6389static const int kDays1970to2000 = 30 * 365 + 7;
6390static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
6391 kDays1970to2000;
6392static const int kYearsOffset = 400000;
6393
6394static const char kDayInYear[] = {
6395 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6396 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6397 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6398 22, 23, 24, 25, 26, 27, 28,
6399 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6400 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6401 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6402 22, 23, 24, 25, 26, 27, 28, 29, 30,
6403 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6404 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6405 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6406 22, 23, 24, 25, 26, 27, 28, 29, 30,
6407 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6408 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6409 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6410 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
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,
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, 29, 30, 31,
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,
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, 31,
6419
6420 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6421 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6422 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6423 22, 23, 24, 25, 26, 27, 28,
6424 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6425 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6426 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6427 22, 23, 24, 25, 26, 27, 28, 29, 30,
6428 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6429 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6430 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6431 22, 23, 24, 25, 26, 27, 28, 29, 30,
6432 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6433 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6434 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6435 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
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,
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, 30, 31,
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,
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, 31,
6444
6445 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6446 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6447 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6448 22, 23, 24, 25, 26, 27, 28, 29,
6449 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6450 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6451 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6452 22, 23, 24, 25, 26, 27, 28, 29, 30,
6453 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6454 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6455 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6456 22, 23, 24, 25, 26, 27, 28, 29, 30,
6457 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6458 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6459 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6460 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
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,
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, 29, 30, 31,
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,
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, 31,
6469
6470 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6471 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6472 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6473 22, 23, 24, 25, 26, 27, 28,
6474 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6475 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6476 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6477 22, 23, 24, 25, 26, 27, 28, 29, 30,
6478 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6479 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6480 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6481 22, 23, 24, 25, 26, 27, 28, 29, 30,
6482 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6483 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6484 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6485 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6486 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6487 22, 23, 24, 25, 26, 27, 28, 29, 30,
6488 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6489 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6490 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6491 22, 23, 24, 25, 26, 27, 28, 29, 30,
6492 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6493 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
6494
6495static const char kMonthInYear[] = {
6496 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,
6497 0, 0, 0, 0, 0, 0,
6498 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,
6499 1, 1, 1,
6500 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,
6501 2, 2, 2, 2, 2, 2,
6502 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,
6503 3, 3, 3, 3, 3,
6504 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,
6505 4, 4, 4, 4, 4, 4,
6506 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,
6507 5, 5, 5, 5, 5,
6508 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,
6509 6, 6, 6, 6, 6, 6,
6510 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,
6511 7, 7, 7, 7, 7, 7,
6512 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,
6513 8, 8, 8, 8, 8,
6514 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,
6515 9, 9, 9, 9, 9, 9,
6516 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6517 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6518 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6519 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6520
6521 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,
6522 0, 0, 0, 0, 0, 0,
6523 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,
6524 1, 1, 1,
6525 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,
6526 2, 2, 2, 2, 2, 2,
6527 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,
6528 3, 3, 3, 3, 3,
6529 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,
6530 4, 4, 4, 4, 4, 4,
6531 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,
6532 5, 5, 5, 5, 5,
6533 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,
6534 6, 6, 6, 6, 6, 6,
6535 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,
6536 7, 7, 7, 7, 7, 7,
6537 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,
6538 8, 8, 8, 8, 8,
6539 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,
6540 9, 9, 9, 9, 9, 9,
6541 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6542 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6543 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6544 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6545
6546 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,
6547 0, 0, 0, 0, 0, 0,
6548 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,
6549 1, 1, 1, 1,
6550 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,
6551 2, 2, 2, 2, 2, 2,
6552 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,
6553 3, 3, 3, 3, 3,
6554 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,
6555 4, 4, 4, 4, 4, 4,
6556 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,
6557 5, 5, 5, 5, 5,
6558 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,
6559 6, 6, 6, 6, 6, 6,
6560 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,
6561 7, 7, 7, 7, 7, 7,
6562 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,
6563 8, 8, 8, 8, 8,
6564 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,
6565 9, 9, 9, 9, 9, 9,
6566 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6567 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6568 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6569 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6570
6571 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,
6572 0, 0, 0, 0, 0, 0,
6573 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,
6574 1, 1, 1,
6575 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,
6576 2, 2, 2, 2, 2, 2,
6577 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,
6578 3, 3, 3, 3, 3,
6579 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,
6580 4, 4, 4, 4, 4, 4,
6581 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,
6582 5, 5, 5, 5, 5,
6583 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,
6584 6, 6, 6, 6, 6, 6,
6585 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,
6586 7, 7, 7, 7, 7, 7,
6587 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,
6588 8, 8, 8, 8, 8,
6589 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,
6590 9, 9, 9, 9, 9, 9,
6591 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6592 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6593 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6594 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
6595
6596
6597// This function works for dates from 1970 to 2099.
6598static inline void DateYMDFromTimeAfter1970(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006599 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006600#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006601 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006602#endif
6603
6604 year = 1970 + (4 * date + 2) / kDaysIn4Years;
6605 date %= kDaysIn4Years;
6606
6607 month = kMonthInYear[date];
6608 day = kDayInYear[date];
6609
6610 ASSERT(MakeDay(year, month, day) == save_date);
6611}
6612
6613
6614static inline void DateYMDFromTimeSlow(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006615 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006616#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006617 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006618#endif
6619
6620 date += kDaysOffset;
6621 year = 400 * (date / kDaysIn400Years) - kYearsOffset;
6622 date %= kDaysIn400Years;
6623
6624 ASSERT(MakeDay(year, 0, 1) + date == save_date);
6625
6626 date--;
6627 int yd1 = date / kDaysIn100Years;
6628 date %= kDaysIn100Years;
6629 year += 100 * yd1;
6630
6631 date++;
6632 int yd2 = date / kDaysIn4Years;
6633 date %= kDaysIn4Years;
6634 year += 4 * yd2;
6635
6636 date--;
6637 int yd3 = date / 365;
6638 date %= 365;
6639 year += yd3;
6640
6641 bool is_leap = (!yd1 || yd2) && !yd3;
6642
6643 ASSERT(date >= -1);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006644 ASSERT(is_leap || (date >= 0));
6645 ASSERT((date < 365) || (is_leap && (date < 366)));
6646 ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0))));
6647 ASSERT(is_leap || ((MakeDay(year, 0, 1) + date) == save_date));
6648 ASSERT(!is_leap || ((MakeDay(year, 0, 1) + date + 1) == save_date));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006649
6650 if (is_leap) {
6651 day = kDayInYear[2*365 + 1 + date];
6652 month = kMonthInYear[2*365 + 1 + date];
6653 } else {
6654 day = kDayInYear[date];
6655 month = kMonthInYear[date];
6656 }
6657
6658 ASSERT(MakeDay(year, month, day) == save_date);
6659}
6660
6661
6662static inline void DateYMDFromTime(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006663 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006664 if (date >= 0 && date < 32 * kDaysIn4Years) {
6665 DateYMDFromTimeAfter1970(date, year, month, day);
6666 } else {
6667 DateYMDFromTimeSlow(date, year, month, day);
6668 }
6669}
6670
6671
6672static Object* Runtime_DateYMDFromTime(Arguments args) {
6673 NoHandleAllocation ha;
6674 ASSERT(args.length() == 2);
6675
6676 CONVERT_DOUBLE_CHECKED(t, args[0]);
6677 CONVERT_CHECKED(JSArray, res_array, args[1]);
6678
6679 int year, month, day;
6680 DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
6681
6682 res_array->SetElement(0, Smi::FromInt(year));
6683 res_array->SetElement(1, Smi::FromInt(month));
6684 res_array->SetElement(2, Smi::FromInt(day));
6685
6686 return Heap::undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006687}
6688
6689
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006690static Object* Runtime_NewArgumentsFast(Arguments args) {
6691 NoHandleAllocation ha;
6692 ASSERT(args.length() == 3);
6693
6694 JSFunction* callee = JSFunction::cast(args[0]);
6695 Object** parameters = reinterpret_cast<Object**>(args[1]);
6696 const int length = Smi::cast(args[2])->value();
6697
6698 Object* result = Heap::AllocateArgumentsObject(callee, length);
6699 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006700 // Allocate the elements if needed.
6701 if (length > 0) {
6702 // Allocate the fixed array.
6703 Object* obj = Heap::AllocateRawFixedArray(length);
6704 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006705
6706 AssertNoAllocation no_gc;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006707 FixedArray* array = reinterpret_cast<FixedArray*>(obj);
6708 array->set_map(Heap::fixed_array_map());
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006709 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006710
6711 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006712 for (int i = 0; i < length; i++) {
6713 array->set(i, *--parameters, mode);
6714 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006715 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006716 }
6717 return result;
6718}
6719
6720
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006721static Object* Runtime_NewClosure(Arguments args) {
6722 HandleScope scope;
6723 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00006724 CONVERT_ARG_CHECKED(Context, context, 0);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006725 CONVERT_ARG_CHECKED(SharedFunctionInfo, shared, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006726
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00006727 PretenureFlag pretenure = (context->global_context() == *context)
6728 ? TENURED // Allocate global closures in old space.
6729 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006730 Handle<JSFunction> result =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006731 Factory::NewFunctionFromSharedFunctionInfo(shared, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006732 return *result;
6733}
6734
6735
ager@chromium.org5c838252010-02-19 08:53:10 +00006736static Code* ComputeConstructStub(Handle<JSFunction> function) {
6737 Handle<Object> prototype = Factory::null_value();
6738 if (function->has_instance_prototype()) {
6739 prototype = Handle<Object>(function->instance_prototype());
6740 }
6741 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006742 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00006743 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006744 if (code->IsFailure()) {
6745 return Builtins::builtin(Builtins::JSConstructStubGeneric);
6746 }
6747 return Code::cast(code);
6748 }
6749
ager@chromium.org5c838252010-02-19 08:53:10 +00006750 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006751}
6752
6753
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006754static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006755 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006756 ASSERT(args.length() == 1);
6757
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006758 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006759
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006760 // If the constructor isn't a proper function we throw a type error.
6761 if (!constructor->IsJSFunction()) {
6762 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6763 Handle<Object> type_error =
6764 Factory::NewTypeError("not_constructor", arguments);
6765 return Top::Throw(*type_error);
6766 }
6767
6768 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006769
6770 // If function should not have prototype, construction is not allowed. In this
6771 // case generated code bailouts here, since function has no initial_map.
6772 if (!function->should_have_prototype()) {
6773 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6774 Handle<Object> type_error =
6775 Factory::NewTypeError("not_constructor", arguments);
6776 return Top::Throw(*type_error);
6777 }
6778
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006779#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006780 // Handle stepping into constructors if step into is active.
6781 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006782 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006783 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006784#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006785
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006786 if (function->has_initial_map()) {
6787 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006788 // The 'Function' function ignores the receiver object when
6789 // called using 'new' and creates a new JSFunction object that
6790 // is returned. The receiver object is only used for error
6791 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006792 // JSFunction. Factory::NewJSObject() should not be used to
6793 // allocate JSFunctions since it does not properly initialize
6794 // the shared part of the function. Since the receiver is
6795 // ignored anyway, we use the global object as the receiver
6796 // instead of a new JSFunction object. This way, errors are
6797 // reported the same way whether or not 'Function' is called
6798 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006799 return Top::context()->global();
6800 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006801 }
6802
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006803 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006804 Handle<SharedFunctionInfo> shared(function->shared());
6805 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006806
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006807 bool first_allocation = !function->has_initial_map();
6808 Handle<JSObject> result = Factory::NewJSObject(function);
6809 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006810 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00006811 ComputeConstructStub(Handle<JSFunction>(function)));
6812 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006813 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006814
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006815 Counters::constructed_objects.Increment();
6816 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006817
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006818 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006819}
6820
6821
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006822static Object* Runtime_LazyCompile(Arguments args) {
6823 HandleScope scope;
6824 ASSERT(args.length() == 1);
6825
6826 Handle<JSFunction> function = args.at<JSFunction>(0);
6827#ifdef DEBUG
6828 if (FLAG_trace_lazy) {
6829 PrintF("[lazy: ");
6830 function->shared()->name()->Print();
6831 PrintF("]\n");
6832 }
6833#endif
6834
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006835 // Compile the target function. Here we compile using CompileLazyInLoop in
6836 // order to get the optimized version. This helps code like delta-blue
6837 // that calls performance-critical routines through constructors. A
6838 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
6839 // direct call. Since the in-loop tracking takes place through CallICs
6840 // this means that things called through constructors are never known to
6841 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006842 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006843 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006844 return Failure::Exception();
6845 }
6846
6847 return function->code();
6848}
6849
6850
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006851static Object* Runtime_GetFunctionDelegate(Arguments args) {
6852 HandleScope scope;
6853 ASSERT(args.length() == 1);
6854 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6855 return *Execution::GetFunctionDelegate(args.at<Object>(0));
6856}
6857
6858
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00006859static Object* Runtime_GetConstructorDelegate(Arguments args) {
6860 HandleScope scope;
6861 ASSERT(args.length() == 1);
6862 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6863 return *Execution::GetConstructorDelegate(args.at<Object>(0));
6864}
6865
6866
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006867static Object* Runtime_NewContext(Arguments args) {
6868 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00006869 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006870
kasper.lund7276f142008-07-30 08:49:36 +00006871 CONVERT_CHECKED(JSFunction, function, args[0]);
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00006872 int length =
6873 ScopeInfo<>::NumberOfContextSlots(function->shared()->scope_info());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006874 Object* result = Heap::AllocateFunctionContext(length, function);
6875 if (result->IsFailure()) return result;
6876
6877 Top::set_context(Context::cast(result));
6878
kasper.lund7276f142008-07-30 08:49:36 +00006879 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006880}
6881
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006882static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006883 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006884 Object* js_object = object;
6885 if (!js_object->IsJSObject()) {
6886 js_object = js_object->ToObject();
6887 if (js_object->IsFailure()) {
6888 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006889 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006890 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006891 Handle<Object> result =
6892 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
6893 return Top::Throw(*result);
6894 }
6895 }
6896
6897 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006898 Heap::AllocateWithContext(Top::context(),
6899 JSObject::cast(js_object),
6900 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006901 if (result->IsFailure()) return result;
6902
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006903 Context* context = Context::cast(result);
6904 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006905
kasper.lund7276f142008-07-30 08:49:36 +00006906 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006907}
6908
6909
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006910static Object* Runtime_PushContext(Arguments args) {
6911 NoHandleAllocation ha;
6912 ASSERT(args.length() == 1);
6913 return PushContextHelper(args[0], false);
6914}
6915
6916
6917static Object* Runtime_PushCatchContext(Arguments args) {
6918 NoHandleAllocation ha;
6919 ASSERT(args.length() == 1);
6920 return PushContextHelper(args[0], true);
6921}
6922
6923
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006924static Object* Runtime_LookupContext(Arguments args) {
6925 HandleScope scope;
6926 ASSERT(args.length() == 2);
6927
6928 CONVERT_ARG_CHECKED(Context, context, 0);
6929 CONVERT_ARG_CHECKED(String, name, 1);
6930
6931 int index;
6932 PropertyAttributes attributes;
6933 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006934 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006935 context->Lookup(name, flags, &index, &attributes);
6936
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006937 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006938 ASSERT(holder->IsJSObject());
6939 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006940 }
6941
6942 // No intermediate context found. Use global object by default.
6943 return Top::context()->global();
6944}
6945
6946
ager@chromium.orga1645e22009-09-09 19:27:10 +00006947// A mechanism to return a pair of Object pointers in registers (if possible).
6948// How this is achieved is calling convention-dependent.
6949// All currently supported x86 compiles uses calling conventions that are cdecl
6950// variants where a 64-bit value is returned in two 32-bit registers
6951// (edx:eax on ia32, r1:r0 on ARM).
6952// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
6953// In Win64 calling convention, a struct of two pointers is returned in memory,
6954// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006955#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006956struct ObjectPair {
6957 Object* x;
6958 Object* y;
6959};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006960
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006961static inline ObjectPair MakePair(Object* x, Object* y) {
6962 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006963 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
6964 // In Win64 they are assigned to a hidden first argument.
6965 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006966}
6967#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006968typedef uint64_t ObjectPair;
6969static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006970 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006971 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006972}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006973#endif
6974
6975
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006976static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006977 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
6978 USE(attributes);
6979 return x->IsTheHole() ? Heap::undefined_value() : x;
6980}
6981
6982
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006983static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
6984 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006985 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006986 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006987 JSFunction* context_extension_function =
6988 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006989 // If the holder isn't a context extension object, we just return it
6990 // as the receiver. This allows arguments objects to be used as
6991 // receivers, but only if they are put in the context scope chain
6992 // explicitly via a with-statement.
6993 Object* constructor = holder->map()->constructor();
6994 if (constructor != context_extension_function) return holder;
6995 // Fall back to using the global object as the receiver if the
6996 // property turns out to be a local variable allocated in a context
6997 // extension object - introduced via eval.
6998 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006999}
7000
7001
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007002static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007003 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00007004 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007005
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007006 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00007007 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007008 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007009 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007010 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007011
7012 int index;
7013 PropertyAttributes attributes;
7014 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007015 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007016 context->Lookup(name, flags, &index, &attributes);
7017
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007018 // If the index is non-negative, the slot has been found in a local
7019 // variable or a parameter. Read it from the context object or the
7020 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007021 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007022 // If the "property" we were looking for is a local variable or an
7023 // argument in a context, the receiver is the global object; see
7024 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
7025 JSObject* receiver = Top::context()->global()->global_receiver();
7026 Object* value = (holder->IsContext())
7027 ? Context::cast(*holder)->get(index)
7028 : JSObject::cast(*holder)->GetElement(index);
7029 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007030 }
7031
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007032 // If the holder is found, we read the property from it.
7033 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007034 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007035 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007036 JSObject* receiver;
7037 if (object->IsGlobalObject()) {
7038 receiver = GlobalObject::cast(object)->global_receiver();
7039 } else if (context->is_exception_holder(*holder)) {
7040 receiver = Top::context()->global()->global_receiver();
7041 } else {
7042 receiver = ComputeReceiverForNonGlobal(object);
7043 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007044 // No need to unhole the value here. This is taken care of by the
7045 // GetProperty function.
7046 Object* value = object->GetProperty(*name);
7047 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007048 }
7049
7050 if (throw_error) {
7051 // The property doesn't exist - throw exception.
7052 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007053 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007054 return MakePair(Top::Throw(*reference_error), NULL);
7055 } else {
7056 // The property doesn't exist - return undefined
7057 return MakePair(Heap::undefined_value(), Heap::undefined_value());
7058 }
7059}
7060
7061
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007062static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007063 return LoadContextSlotHelper(args, true);
7064}
7065
7066
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007067static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007068 return LoadContextSlotHelper(args, false);
7069}
7070
7071
7072static Object* Runtime_StoreContextSlot(Arguments args) {
7073 HandleScope scope;
7074 ASSERT(args.length() == 3);
7075
7076 Handle<Object> value(args[0]);
7077 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007078 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007079
7080 int index;
7081 PropertyAttributes attributes;
7082 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007083 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007084 context->Lookup(name, flags, &index, &attributes);
7085
7086 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007087 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007088 // Ignore if read_only variable.
7089 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007090 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007091 }
7092 } else {
7093 ASSERT((attributes & READ_ONLY) == 0);
7094 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007095 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007096 USE(result);
7097 ASSERT(!result->IsFailure());
7098 }
7099 return *value;
7100 }
7101
7102 // Slow case: The property is not in a FixedArray context.
7103 // It is either in an JSObject extension context or it was not found.
7104 Handle<JSObject> context_ext;
7105
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007106 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007107 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007108 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007109 } else {
7110 // The property was not found. It needs to be stored in the global context.
7111 ASSERT(attributes == ABSENT);
7112 attributes = NONE;
7113 context_ext = Handle<JSObject>(Top::context()->global());
7114 }
7115
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007116 // Set the property, but ignore if read_only variable on the context
7117 // extension object itself.
7118 if ((attributes & READ_ONLY) == 0 ||
7119 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007120 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
7121 if (set.is_null()) {
7122 // Failure::Exception is converted to a null handle in the
7123 // handle-based methods such as SetProperty. We therefore need
7124 // to convert null handles back to exceptions.
7125 ASSERT(Top::has_pending_exception());
7126 return Failure::Exception();
7127 }
7128 }
7129 return *value;
7130}
7131
7132
7133static Object* Runtime_Throw(Arguments args) {
7134 HandleScope scope;
7135 ASSERT(args.length() == 1);
7136
7137 return Top::Throw(args[0]);
7138}
7139
7140
7141static Object* Runtime_ReThrow(Arguments args) {
7142 HandleScope scope;
7143 ASSERT(args.length() == 1);
7144
7145 return Top::ReThrow(args[0]);
7146}
7147
7148
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007149static Object* Runtime_PromoteScheduledException(Arguments args) {
7150 ASSERT_EQ(0, args.length());
7151 return Top::PromoteScheduledException();
7152}
7153
7154
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007155static Object* Runtime_ThrowReferenceError(Arguments args) {
7156 HandleScope scope;
7157 ASSERT(args.length() == 1);
7158
7159 Handle<Object> name(args[0]);
7160 Handle<Object> reference_error =
7161 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
7162 return Top::Throw(*reference_error);
7163}
7164
7165
7166static Object* Runtime_StackOverflow(Arguments args) {
7167 NoHandleAllocation na;
7168 return Top::StackOverflow();
7169}
7170
7171
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007172static Object* Runtime_StackGuard(Arguments args) {
7173 ASSERT(args.length() == 1);
7174
7175 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007176 if (StackGuard::IsStackOverflow()) {
7177 return Runtime_StackOverflow(args);
7178 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007179
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007180 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007181}
7182
7183
7184// NOTE: These PrintXXX functions are defined for all builds (not just
7185// DEBUG builds) because we may want to be able to trace function
7186// calls in all modes.
7187static void PrintString(String* str) {
7188 // not uncommon to have empty strings
7189 if (str->length() > 0) {
7190 SmartPointer<char> s =
7191 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
7192 PrintF("%s", *s);
7193 }
7194}
7195
7196
7197static void PrintObject(Object* obj) {
7198 if (obj->IsSmi()) {
7199 PrintF("%d", Smi::cast(obj)->value());
7200 } else if (obj->IsString() || obj->IsSymbol()) {
7201 PrintString(String::cast(obj));
7202 } else if (obj->IsNumber()) {
7203 PrintF("%g", obj->Number());
7204 } else if (obj->IsFailure()) {
7205 PrintF("<failure>");
7206 } else if (obj->IsUndefined()) {
7207 PrintF("<undefined>");
7208 } else if (obj->IsNull()) {
7209 PrintF("<null>");
7210 } else if (obj->IsTrue()) {
7211 PrintF("<true>");
7212 } else if (obj->IsFalse()) {
7213 PrintF("<false>");
7214 } else {
7215 PrintF("%p", obj);
7216 }
7217}
7218
7219
7220static int StackSize() {
7221 int n = 0;
7222 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
7223 return n;
7224}
7225
7226
7227static void PrintTransition(Object* result) {
7228 // indentation
7229 { const int nmax = 80;
7230 int n = StackSize();
7231 if (n <= nmax)
7232 PrintF("%4d:%*s", n, n, "");
7233 else
7234 PrintF("%4d:%*s", n, nmax, "...");
7235 }
7236
7237 if (result == NULL) {
7238 // constructor calls
7239 JavaScriptFrameIterator it;
7240 JavaScriptFrame* frame = it.frame();
7241 if (frame->IsConstructor()) PrintF("new ");
7242 // function name
7243 Object* fun = frame->function();
7244 if (fun->IsJSFunction()) {
7245 PrintObject(JSFunction::cast(fun)->shared()->name());
7246 } else {
7247 PrintObject(fun);
7248 }
7249 // function arguments
7250 // (we are intentionally only printing the actually
7251 // supplied parameters, not all parameters required)
7252 PrintF("(this=");
7253 PrintObject(frame->receiver());
7254 const int length = frame->GetProvidedParametersCount();
7255 for (int i = 0; i < length; i++) {
7256 PrintF(", ");
7257 PrintObject(frame->GetParameter(i));
7258 }
7259 PrintF(") {\n");
7260
7261 } else {
7262 // function result
7263 PrintF("} -> ");
7264 PrintObject(result);
7265 PrintF("\n");
7266 }
7267}
7268
7269
7270static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007271 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007272 NoHandleAllocation ha;
7273 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007274 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007275}
7276
7277
7278static Object* Runtime_TraceExit(Arguments args) {
7279 NoHandleAllocation ha;
7280 PrintTransition(args[0]);
7281 return args[0]; // return TOS
7282}
7283
7284
7285static Object* Runtime_DebugPrint(Arguments args) {
7286 NoHandleAllocation ha;
7287 ASSERT(args.length() == 1);
7288
7289#ifdef DEBUG
7290 if (args[0]->IsString()) {
7291 // If we have a string, assume it's a code "marker"
7292 // and print some interesting cpu debugging info.
7293 JavaScriptFrameIterator it;
7294 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007295 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
7296 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007297 } else {
7298 PrintF("DebugPrint: ");
7299 }
7300 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007301 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007302 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007303 HeapObject::cast(args[0])->map()->Print();
7304 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007305#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007306 // ShortPrint is available in release mode. Print is not.
7307 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007308#endif
7309 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00007310 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007311
7312 return args[0]; // return TOS
7313}
7314
7315
7316static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007317 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007318 NoHandleAllocation ha;
7319 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007320 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007321}
7322
7323
mads.s.ager31e71382008-08-13 09:32:07 +00007324static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007325 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007326 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007327
7328 // According to ECMA-262, section 15.9.1, page 117, the precision of
7329 // the number in a Date object representing a particular instant in
7330 // time is milliseconds. Therefore, we floor the result of getting
7331 // the OS time.
7332 double millis = floor(OS::TimeCurrentMillis());
7333 return Heap::NumberFromDouble(millis);
7334}
7335
7336
7337static Object* Runtime_DateParseString(Arguments args) {
7338 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007339 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007340
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007341 CONVERT_ARG_CHECKED(String, str, 0);
7342 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007343
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007344 CONVERT_ARG_CHECKED(JSArray, output, 1);
7345 RUNTIME_ASSERT(output->HasFastElements());
7346
7347 AssertNoAllocation no_allocation;
7348
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007349 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007350 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
7351 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007352 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007353 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007354 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007355 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007356 result = DateParser::Parse(str->ToUC16Vector(), output_array);
7357 }
7358
7359 if (result) {
7360 return *output;
7361 } else {
7362 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007363 }
7364}
7365
7366
7367static Object* Runtime_DateLocalTimezone(Arguments args) {
7368 NoHandleAllocation ha;
7369 ASSERT(args.length() == 1);
7370
7371 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007372 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007373 return Heap::AllocateStringFromUtf8(CStrVector(zone));
7374}
7375
7376
7377static Object* Runtime_DateLocalTimeOffset(Arguments args) {
7378 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007379 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007380
7381 return Heap::NumberFromDouble(OS::LocalTimeOffset());
7382}
7383
7384
7385static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
7386 NoHandleAllocation ha;
7387 ASSERT(args.length() == 1);
7388
7389 CONVERT_DOUBLE_CHECKED(x, args[0]);
7390 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
7391}
7392
7393
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007394static Object* Runtime_GlobalReceiver(Arguments args) {
7395 ASSERT(args.length() == 1);
7396 Object* global = args[0];
7397 if (!global->IsJSGlobalObject()) return Heap::null_value();
7398 return JSGlobalObject::cast(global)->global_receiver();
7399}
7400
7401
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007402static Object* Runtime_CompileString(Arguments args) {
7403 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007404 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007405 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007406 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007407
ager@chromium.org381abbb2009-02-25 13:23:22 +00007408 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007409 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007410 Compiler::ValidationState validate = (is_json->IsTrue())
7411 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007412 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
7413 context,
7414 true,
7415 validate);
7416 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007417 Handle<JSFunction> fun =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007418 Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007419 return *fun;
7420}
7421
7422
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007423static ObjectPair CompileGlobalEval(Handle<String> source,
7424 Handle<Object> receiver) {
7425 // Deal with a normal eval call with a string argument. Compile it
7426 // and return the compiled function bound in the local context.
7427 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
7428 source,
7429 Handle<Context>(Top::context()),
7430 Top::context()->IsGlobalContext(),
7431 Compiler::DONT_VALIDATE_JSON);
7432 if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
7433 Handle<JSFunction> compiled = Factory::NewFunctionFromSharedFunctionInfo(
7434 shared,
7435 Handle<Context>(Top::context()),
7436 NOT_TENURED);
7437 return MakePair(*compiled, *receiver);
7438}
7439
7440
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007441static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
7442 ASSERT(args.length() == 3);
7443 if (!args[0]->IsJSFunction()) {
7444 return MakePair(Top::ThrowIllegalOperation(), NULL);
7445 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007446
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007447 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007448 Handle<JSFunction> callee = args.at<JSFunction>(0);
7449 Handle<Object> receiver; // Will be overwritten.
7450
7451 // Compute the calling context.
7452 Handle<Context> context = Handle<Context>(Top::context());
7453#ifdef DEBUG
7454 // Make sure Top::context() agrees with the old code that traversed
7455 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007456 StackFrameLocator locator;
7457 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007458 ASSERT(Context::cast(frame->context()) == *context);
7459#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007460
7461 // Find where the 'eval' symbol is bound. It is unaliased only if
7462 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007463 int index = -1;
7464 PropertyAttributes attributes = ABSENT;
7465 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007466 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
7467 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007468 // Stop search when eval is found or when the global context is
7469 // reached.
7470 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007471 if (context->is_function_context()) {
7472 context = Handle<Context>(Context::cast(context->closure()->context()));
7473 } else {
7474 context = Handle<Context>(context->previous());
7475 }
7476 }
7477
iposva@chromium.org245aa852009-02-10 00:49:54 +00007478 // If eval could not be resolved, it has been deleted and we need to
7479 // throw a reference error.
7480 if (attributes == ABSENT) {
7481 Handle<Object> name = Factory::eval_symbol();
7482 Handle<Object> reference_error =
7483 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007484 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007485 }
7486
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007487 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007488 // 'eval' is not bound in the global context. Just call the function
7489 // with the given arguments. This is not necessarily the global eval.
7490 if (receiver->IsContext()) {
7491 context = Handle<Context>::cast(receiver);
7492 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007493 } else if (receiver->IsJSContextExtensionObject()) {
7494 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007495 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007496 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007497 }
7498
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007499 // 'eval' is bound in the global context, but it may have been overwritten.
7500 // Compare it to the builtin 'GlobalEval' function to make sure.
7501 if (*callee != Top::global_context()->global_eval_fun() ||
7502 !args[1]->IsString()) {
7503 return MakePair(*callee, Top::context()->global()->global_receiver());
7504 }
7505
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007506 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
7507}
7508
7509
7510static ObjectPair Runtime_ResolvePossiblyDirectEvalNoLookup(Arguments args) {
7511 ASSERT(args.length() == 3);
7512 if (!args[0]->IsJSFunction()) {
7513 return MakePair(Top::ThrowIllegalOperation(), NULL);
7514 }
7515
7516 HandleScope scope;
7517 Handle<JSFunction> callee = args.at<JSFunction>(0);
7518
7519 // 'eval' is bound in the global context, but it may have been overwritten.
7520 // Compare it to the builtin 'GlobalEval' function to make sure.
7521 if (*callee != Top::global_context()->global_eval_fun() ||
7522 !args[1]->IsString()) {
7523 return MakePair(*callee, Top::context()->global()->global_receiver());
7524 }
7525
7526 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007527}
7528
7529
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007530static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
7531 // This utility adjusts the property attributes for newly created Function
7532 // object ("new Function(...)") by changing the map.
7533 // All it does is changing the prototype property to enumerable
7534 // as specified in ECMA262, 15.3.5.2.
7535 HandleScope scope;
7536 ASSERT(args.length() == 1);
7537 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7538 ASSERT(func->map()->instance_type() ==
7539 Top::function_instance_map()->instance_type());
7540 ASSERT(func->map()->instance_size() ==
7541 Top::function_instance_map()->instance_size());
7542 func->set_map(*Top::function_instance_map());
7543 return *func;
7544}
7545
7546
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007547// Push an array unto an array of arrays if it is not already in the
7548// array. Returns true if the element was pushed on the stack and
7549// false otherwise.
7550static Object* Runtime_PushIfAbsent(Arguments args) {
7551 ASSERT(args.length() == 2);
7552 CONVERT_CHECKED(JSArray, array, args[0]);
7553 CONVERT_CHECKED(JSArray, element, args[1]);
7554 RUNTIME_ASSERT(array->HasFastElements());
7555 int length = Smi::cast(array->length())->value();
7556 FixedArray* elements = FixedArray::cast(array->elements());
7557 for (int i = 0; i < length; i++) {
7558 if (elements->get(i) == element) return Heap::false_value();
7559 }
7560 Object* obj = array->SetFastElement(length, element);
7561 if (obj->IsFailure()) return obj;
7562 return Heap::true_value();
7563}
7564
7565
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007566/**
7567 * A simple visitor visits every element of Array's.
7568 * The backend storage can be a fixed array for fast elements case,
7569 * or a dictionary for sparse array. Since Dictionary is a subtype
7570 * of FixedArray, the class can be used by both fast and slow cases.
7571 * The second parameter of the constructor, fast_elements, specifies
7572 * whether the storage is a FixedArray or Dictionary.
7573 *
7574 * An index limit is used to deal with the situation that a result array
7575 * length overflows 32-bit non-negative integer.
7576 */
7577class ArrayConcatVisitor {
7578 public:
7579 ArrayConcatVisitor(Handle<FixedArray> storage,
7580 uint32_t index_limit,
7581 bool fast_elements) :
7582 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007583 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007584
7585 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007586 if (i >= index_limit_ - index_offset_) return;
7587 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007588
7589 if (fast_elements_) {
7590 ASSERT(index < static_cast<uint32_t>(storage_->length()));
7591 storage_->set(index, *elm);
7592
7593 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007594 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
7595 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007596 Factory::DictionaryAtNumberPut(dict, index, elm);
7597 if (!result.is_identical_to(dict))
7598 storage_ = result;
7599 }
7600 }
7601
7602 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007603 if (index_limit_ - index_offset_ < delta) {
7604 index_offset_ = index_limit_;
7605 } else {
7606 index_offset_ += delta;
7607 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007608 }
7609
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007610 Handle<FixedArray> storage() { return storage_; }
7611
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007612 private:
7613 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007614 // Limit on the accepted indices. Elements with indices larger than the
7615 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007616 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007617 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007618 uint32_t index_offset_;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007619 const bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007620};
7621
7622
ager@chromium.org3811b432009-10-28 14:53:37 +00007623template<class ExternalArrayClass, class ElementType>
7624static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
7625 bool elements_are_ints,
7626 bool elements_are_guaranteed_smis,
7627 uint32_t range,
7628 ArrayConcatVisitor* visitor) {
7629 Handle<ExternalArrayClass> array(
7630 ExternalArrayClass::cast(receiver->elements()));
7631 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
7632
7633 if (visitor != NULL) {
7634 if (elements_are_ints) {
7635 if (elements_are_guaranteed_smis) {
7636 for (uint32_t j = 0; j < len; j++) {
7637 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
7638 visitor->visit(j, e);
7639 }
7640 } else {
7641 for (uint32_t j = 0; j < len; j++) {
7642 int64_t val = static_cast<int64_t>(array->get(j));
7643 if (Smi::IsValid(static_cast<intptr_t>(val))) {
7644 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
7645 visitor->visit(j, e);
7646 } else {
7647 Handle<Object> e(
7648 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
7649 visitor->visit(j, e);
7650 }
7651 }
7652 }
7653 } else {
7654 for (uint32_t j = 0; j < len; j++) {
7655 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
7656 visitor->visit(j, e);
7657 }
7658 }
7659 }
7660
7661 return len;
7662}
7663
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007664/**
7665 * A helper function that visits elements of a JSObject. Only elements
7666 * whose index between 0 and range (exclusive) are visited.
7667 *
7668 * If the third parameter, visitor, is not NULL, the visitor is called
7669 * with parameters, 'visitor_index_offset + element index' and the element.
7670 *
7671 * It returns the number of visisted elements.
7672 */
7673static uint32_t IterateElements(Handle<JSObject> receiver,
7674 uint32_t range,
7675 ArrayConcatVisitor* visitor) {
7676 uint32_t num_of_elements = 0;
7677
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007678 switch (receiver->GetElementsKind()) {
7679 case JSObject::FAST_ELEMENTS: {
7680 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
7681 uint32_t len = elements->length();
7682 if (range < len) {
7683 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007684 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007685
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007686 for (uint32_t j = 0; j < len; j++) {
7687 Handle<Object> e(elements->get(j));
7688 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007689 num_of_elements++;
7690 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007691 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007692 }
7693 }
7694 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007695 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007696 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007697 case JSObject::PIXEL_ELEMENTS: {
7698 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
7699 uint32_t len = pixels->length();
7700 if (range < len) {
7701 len = range;
7702 }
7703
7704 for (uint32_t j = 0; j < len; j++) {
7705 num_of_elements++;
7706 if (visitor != NULL) {
7707 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
7708 visitor->visit(j, e);
7709 }
7710 }
7711 break;
7712 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007713 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
7714 num_of_elements =
7715 IterateExternalArrayElements<ExternalByteArray, int8_t>(
7716 receiver, true, true, range, visitor);
7717 break;
7718 }
7719 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7720 num_of_elements =
7721 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
7722 receiver, true, true, range, visitor);
7723 break;
7724 }
7725 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
7726 num_of_elements =
7727 IterateExternalArrayElements<ExternalShortArray, int16_t>(
7728 receiver, true, true, range, visitor);
7729 break;
7730 }
7731 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7732 num_of_elements =
7733 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
7734 receiver, true, true, range, visitor);
7735 break;
7736 }
7737 case JSObject::EXTERNAL_INT_ELEMENTS: {
7738 num_of_elements =
7739 IterateExternalArrayElements<ExternalIntArray, int32_t>(
7740 receiver, true, false, range, visitor);
7741 break;
7742 }
7743 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7744 num_of_elements =
7745 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
7746 receiver, true, false, range, visitor);
7747 break;
7748 }
7749 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
7750 num_of_elements =
7751 IterateExternalArrayElements<ExternalFloatArray, float>(
7752 receiver, false, false, range, visitor);
7753 break;
7754 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007755 case JSObject::DICTIONARY_ELEMENTS: {
7756 Handle<NumberDictionary> dict(receiver->element_dictionary());
7757 uint32_t capacity = dict->Capacity();
7758 for (uint32_t j = 0; j < capacity; j++) {
7759 Handle<Object> k(dict->KeyAt(j));
7760 if (dict->IsKey(*k)) {
7761 ASSERT(k->IsNumber());
7762 uint32_t index = static_cast<uint32_t>(k->Number());
7763 if (index < range) {
7764 num_of_elements++;
7765 if (visitor) {
7766 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
7767 }
7768 }
7769 }
7770 }
7771 break;
7772 }
7773 default:
7774 UNREACHABLE();
7775 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007776 }
7777
7778 return num_of_elements;
7779}
7780
7781
7782/**
7783 * A helper function that visits elements of an Array object, and elements
7784 * on its prototypes.
7785 *
7786 * Elements on prototypes are visited first, and only elements whose indices
7787 * less than Array length are visited.
7788 *
7789 * If a ArrayConcatVisitor object is given, the visitor is called with
7790 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007791 *
7792 * The returned number of elements is an upper bound on the actual number
7793 * of elements added. If the same element occurs in more than one object
7794 * in the array's prototype chain, it will be counted more than once, but
7795 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007796 */
7797static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
7798 ArrayConcatVisitor* visitor) {
7799 uint32_t range = static_cast<uint32_t>(array->length()->Number());
7800 Handle<Object> obj = array;
7801
7802 static const int kEstimatedPrototypes = 3;
7803 List< Handle<JSObject> > objects(kEstimatedPrototypes);
7804
7805 // Visit prototype first. If an element on the prototype is shadowed by
7806 // the inheritor using the same index, the ArrayConcatVisitor visits
7807 // the prototype element before the shadowing element.
7808 // The visitor can simply overwrite the old value by new value using
7809 // the same index. This follows Array::concat semantics.
7810 while (!obj->IsNull()) {
7811 objects.Add(Handle<JSObject>::cast(obj));
7812 obj = Handle<Object>(obj->GetPrototype());
7813 }
7814
7815 uint32_t nof_elements = 0;
7816 for (int i = objects.length() - 1; i >= 0; i--) {
7817 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007818 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007819 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007820
7821 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
7822 nof_elements = JSObject::kMaxElementCount;
7823 } else {
7824 nof_elements += encountered_elements;
7825 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007826 }
7827
7828 return nof_elements;
7829}
7830
7831
7832/**
7833 * A helper function of Runtime_ArrayConcat.
7834 *
7835 * The first argument is an Array of arrays and objects. It is the
7836 * same as the arguments array of Array::concat JS function.
7837 *
7838 * If an argument is an Array object, the function visits array
7839 * elements. If an argument is not an Array object, the function
7840 * visits the object as if it is an one-element array.
7841 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007842 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007843 * non-negative number is used as new length. For example, if one
7844 * array length is 2^32 - 1, second array length is 1, the
7845 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007846 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
7847 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007848 */
7849static uint32_t IterateArguments(Handle<JSArray> arguments,
7850 ArrayConcatVisitor* visitor) {
7851 uint32_t visited_elements = 0;
7852 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7853
7854 for (uint32_t i = 0; i < num_of_args; i++) {
7855 Handle<Object> obj(arguments->GetElement(i));
7856 if (obj->IsJSArray()) {
7857 Handle<JSArray> array = Handle<JSArray>::cast(obj);
7858 uint32_t len = static_cast<uint32_t>(array->length()->Number());
7859 uint32_t nof_elements =
7860 IterateArrayAndPrototypeElements(array, visitor);
7861 // Total elements of array and its prototype chain can be more than
7862 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007863 // the array length number of elements. We use the length as an estimate
7864 // for the actual number of elements added.
7865 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
7866 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
7867 visited_elements = JSArray::kMaxElementCount;
7868 } else {
7869 visited_elements += added_elements;
7870 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007871 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007872 } else {
7873 if (visitor) {
7874 visitor->visit(0, obj);
7875 visitor->increase_index_offset(1);
7876 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007877 if (visited_elements < JSArray::kMaxElementCount) {
7878 visited_elements++;
7879 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007880 }
7881 }
7882 return visited_elements;
7883}
7884
7885
7886/**
7887 * Array::concat implementation.
7888 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007889 * TODO(lrn): Fix non-compliance for very large concatenations and update to
7890 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007891 */
7892static Object* Runtime_ArrayConcat(Arguments args) {
7893 ASSERT(args.length() == 1);
7894 HandleScope handle_scope;
7895
7896 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
7897 Handle<JSArray> arguments(arg_arrays);
7898
7899 // Pass 1: estimate the number of elements of the result
7900 // (it could be more than real numbers if prototype has elements).
7901 uint32_t result_length = 0;
7902 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7903
7904 { AssertNoAllocation nogc;
7905 for (uint32_t i = 0; i < num_of_args; i++) {
7906 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007907 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007908 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007909 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007910 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
7911 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007912 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007913 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007914 if (JSObject::kMaxElementCount - result_length < length_estimate) {
7915 result_length = JSObject::kMaxElementCount;
7916 break;
7917 }
7918 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007919 }
7920 }
7921
7922 // Allocate an empty array, will set length and content later.
7923 Handle<JSArray> result = Factory::NewJSArray(0);
7924
7925 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
7926 // If estimated number of elements is more than half of length, a
7927 // fixed array (fast case) is more time and space-efficient than a
7928 // dictionary.
7929 bool fast_case = (estimate_nof_elements * 2) >= result_length;
7930
7931 Handle<FixedArray> storage;
7932 if (fast_case) {
7933 // The backing storage array must have non-existing elements to
7934 // preserve holes across concat operations.
7935 storage = Factory::NewFixedArrayWithHoles(result_length);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007936 result->set_map(*Factory::GetFastElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007937 } else {
7938 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
7939 uint32_t at_least_space_for = estimate_nof_elements +
7940 (estimate_nof_elements >> 2);
7941 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007942 Factory::NewNumberDictionary(at_least_space_for));
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007943 result->set_map(*Factory::GetSlowElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007944 }
7945
7946 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
7947
7948 ArrayConcatVisitor visitor(storage, result_length, fast_case);
7949
7950 IterateArguments(arguments, &visitor);
7951
7952 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007953 // Please note the storage might have changed in the visitor.
7954 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007955
7956 return *result;
7957}
7958
7959
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007960// This will not allocate (flatten the string), but it may run
7961// very slowly for very deeply nested ConsStrings. For debugging use only.
7962static Object* Runtime_GlobalPrint(Arguments args) {
7963 NoHandleAllocation ha;
7964 ASSERT(args.length() == 1);
7965
7966 CONVERT_CHECKED(String, string, args[0]);
7967 StringInputBuffer buffer(string);
7968 while (buffer.has_more()) {
7969 uint16_t character = buffer.GetNext();
7970 PrintF("%c", character);
7971 }
7972 return string;
7973}
7974
ager@chromium.org5ec48922009-05-05 07:25:34 +00007975// Moves all own elements of an object, that are below a limit, to positions
7976// starting at zero. All undefined values are placed after non-undefined values,
7977// and are followed by non-existing element. Does not change the length
7978// property.
7979// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007980static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007981 ASSERT(args.length() == 2);
7982 CONVERT_CHECKED(JSObject, object, args[0]);
7983 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
7984 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007985}
7986
7987
7988// Move contents of argument 0 (an array) to argument 1 (an array)
7989static Object* Runtime_MoveArrayContents(Arguments args) {
7990 ASSERT(args.length() == 2);
7991 CONVERT_CHECKED(JSArray, from, args[0]);
7992 CONVERT_CHECKED(JSArray, to, args[1]);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007993 HeapObject* new_elements = from->elements();
7994 Object* new_map;
7995 if (new_elements->map() == Heap::fixed_array_map()) {
7996 new_map = to->map()->GetFastElementsMap();
7997 } else {
7998 new_map = to->map()->GetSlowElementsMap();
7999 }
8000 if (new_map->IsFailure()) return new_map;
8001 to->set_map(Map::cast(new_map));
8002 to->set_elements(new_elements);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008003 to->set_length(from->length());
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00008004 Object* obj = from->ResetElements();
8005 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008006 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008007 return to;
8008}
8009
8010
8011// How many elements does this array have?
8012static Object* Runtime_EstimateNumberOfElements(Arguments args) {
8013 ASSERT(args.length() == 1);
8014 CONVERT_CHECKED(JSArray, array, args[0]);
8015 HeapObject* elements = array->elements();
8016 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008017 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008018 } else {
8019 return array->length();
8020 }
8021}
8022
8023
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008024static Object* Runtime_SwapElements(Arguments args) {
8025 HandleScope handle_scope;
8026
8027 ASSERT_EQ(3, args.length());
8028
ager@chromium.orgac091b72010-05-05 07:34:42 +00008029 CONVERT_ARG_CHECKED(JSObject, object, 0);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008030 Handle<Object> key1 = args.at<Object>(1);
8031 Handle<Object> key2 = args.at<Object>(2);
8032
8033 uint32_t index1, index2;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008034 if (!key1->ToArrayIndex(&index1)
8035 || !key2->ToArrayIndex(&index2)) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00008036 return Top::ThrowIllegalOperation();
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008037 }
8038
ager@chromium.orgac091b72010-05-05 07:34:42 +00008039 Handle<JSObject> jsobject = Handle<JSObject>::cast(object);
8040 Handle<Object> tmp1 = GetElement(jsobject, index1);
8041 Handle<Object> tmp2 = GetElement(jsobject, index2);
8042
8043 SetElement(jsobject, index1, tmp2);
8044 SetElement(jsobject, index2, tmp1);
8045
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008046 return Heap::undefined_value();
8047}
8048
8049
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008050// Returns an array that tells you where in the [0, length) interval an array
8051// might have elements. Can either return keys or intervals. Keys can have
8052// gaps in (undefined). Intervals can also span over some undefined keys.
8053static Object* Runtime_GetArrayKeys(Arguments args) {
8054 ASSERT(args.length() == 2);
8055 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00008056 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008057 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008058 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008059 // Create an array and get all the keys into it, then remove all the
8060 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008061 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008062 int keys_length = keys->length();
8063 for (int i = 0; i < keys_length; i++) {
8064 Object* key = keys->get(i);
8065 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008066 if (!key->ToArrayIndex(&index) || index >= length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008067 // Zap invalid keys.
8068 keys->set_undefined(i);
8069 }
8070 }
8071 return *Factory::NewJSArrayWithElements(keys);
8072 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008073 ASSERT(array->HasFastElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008074 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
8075 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008076 single_interval->set(0, Smi::FromInt(-1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008077 uint32_t actual_length =
8078 static_cast<uint32_t>(FixedArray::cast(array->elements())->length());
ager@chromium.org5ec48922009-05-05 07:25:34 +00008079 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008080 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00008081 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008082 single_interval->set(1, *length_object);
8083 return *Factory::NewJSArrayWithElements(single_interval);
8084 }
8085}
8086
8087
8088// DefineAccessor takes an optional final argument which is the
8089// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
8090// to the way accessors are implemented, it is set for both the getter
8091// and setter on the first call to DefineAccessor and ignored on
8092// subsequent calls.
8093static Object* Runtime_DefineAccessor(Arguments args) {
8094 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
8095 // Compute attributes.
8096 PropertyAttributes attributes = NONE;
8097 if (args.length() == 5) {
8098 CONVERT_CHECKED(Smi, attrs, args[4]);
8099 int value = attrs->value();
8100 // Only attribute bits should be set.
8101 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
8102 attributes = static_cast<PropertyAttributes>(value);
8103 }
8104
8105 CONVERT_CHECKED(JSObject, obj, args[0]);
8106 CONVERT_CHECKED(String, name, args[1]);
8107 CONVERT_CHECKED(Smi, flag, args[2]);
8108 CONVERT_CHECKED(JSFunction, fun, args[3]);
8109 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
8110}
8111
8112
8113static Object* Runtime_LookupAccessor(Arguments args) {
8114 ASSERT(args.length() == 3);
8115 CONVERT_CHECKED(JSObject, obj, args[0]);
8116 CONVERT_CHECKED(String, name, args[1]);
8117 CONVERT_CHECKED(Smi, flag, args[2]);
8118 return obj->LookupAccessor(name, flag->value() == 0);
8119}
8120
8121
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008122#ifdef ENABLE_DEBUGGER_SUPPORT
8123static Object* Runtime_DebugBreak(Arguments args) {
8124 ASSERT(args.length() == 0);
8125 return Execution::DebugBreakHelper();
8126}
8127
8128
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008129// Helper functions for wrapping and unwrapping stack frame ids.
8130static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008131 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008132 return Smi::FromInt(id >> 2);
8133}
8134
8135
8136static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
8137 return static_cast<StackFrame::Id>(wrapped->value() << 2);
8138}
8139
8140
8141// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00008142// args[0]: debug event listener function to set or null or undefined for
8143// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008144// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00008145static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008146 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00008147 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
8148 args[0]->IsUndefined() ||
8149 args[0]->IsNull());
8150 Handle<Object> callback = args.at<Object>(0);
8151 Handle<Object> data = args.at<Object>(1);
8152 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008153
8154 return Heap::undefined_value();
8155}
8156
8157
8158static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00008159 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008160 StackGuard::DebugBreak();
8161 return Heap::undefined_value();
8162}
8163
8164
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008165static Object* DebugLookupResultValue(Object* receiver, String* name,
8166 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00008167 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008168 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008169 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008170 case NORMAL:
8171 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008172 if (value->IsTheHole()) {
8173 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008174 }
8175 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008176 case FIELD:
8177 value =
8178 JSObject::cast(
8179 result->holder())->FastPropertyAt(result->GetFieldIndex());
8180 if (value->IsTheHole()) {
8181 return Heap::undefined_value();
8182 }
8183 return value;
8184 case CONSTANT_FUNCTION:
8185 return result->GetConstantFunction();
8186 case CALLBACKS: {
8187 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008188 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008189 value = receiver->GetPropertyWithCallback(
8190 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00008191 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008192 value = Top::pending_exception();
8193 Top::clear_pending_exception();
8194 if (caught_exception != NULL) {
8195 *caught_exception = true;
8196 }
8197 }
8198 return value;
8199 } else {
8200 return Heap::undefined_value();
8201 }
8202 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008203 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008204 case MAP_TRANSITION:
8205 case CONSTANT_TRANSITION:
8206 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008207 return Heap::undefined_value();
8208 default:
8209 UNREACHABLE();
8210 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008211 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008212 return Heap::undefined_value();
8213}
8214
8215
ager@chromium.org32912102009-01-16 10:38:43 +00008216// Get debugger related details for an object property.
8217// args[0]: object holding property
8218// args[1]: name of the property
8219//
8220// The array returned contains the following information:
8221// 0: Property value
8222// 1: Property details
8223// 2: Property value is exception
8224// 3: Getter function if defined
8225// 4: Setter function if defined
8226// Items 2-4 are only filled if the property has either a getter or a setter
8227// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00008228static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008229 HandleScope scope;
8230
8231 ASSERT(args.length() == 2);
8232
8233 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8234 CONVERT_ARG_CHECKED(String, name, 1);
8235
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008236 // Make sure to set the current context to the context before the debugger was
8237 // entered (if the debugger is entered). The reason for switching context here
8238 // is that for some property lookups (accessors and interceptors) callbacks
8239 // into the embedding application can occour, and the embedding application
8240 // could have the assumption that its own global context is the current
8241 // context and not some internal debugger context.
8242 SaveContext save;
8243 if (Debug::InDebugger()) {
8244 Top::set_context(*Debug::debugger_entry()->GetContext());
8245 }
8246
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008247 // Skip the global proxy as it has no properties and always delegates to the
8248 // real global object.
8249 if (obj->IsJSGlobalProxy()) {
8250 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
8251 }
8252
8253
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008254 // Check if the name is trivially convertible to an index and get the element
8255 // if so.
8256 uint32_t index;
8257 if (name->AsArrayIndex(&index)) {
8258 Handle<FixedArray> details = Factory::NewFixedArray(2);
8259 details->set(0, Runtime::GetElementOrCharAt(obj, index));
8260 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
8261 return *Factory::NewJSArrayWithElements(details);
8262 }
8263
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008264 // Find the number of objects making up this.
8265 int length = LocalPrototypeChainLength(*obj);
8266
8267 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008268 Handle<JSObject> jsproto = obj;
8269 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008270 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008271 jsproto->LocalLookup(*name, &result);
8272 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008273 // LookupResult is not GC safe as it holds raw object pointers.
8274 // GC can happen later in this code so put the required fields into
8275 // local variables using handles when required for later use.
8276 PropertyType result_type = result.type();
8277 Handle<Object> result_callback_obj;
8278 if (result_type == CALLBACKS) {
8279 result_callback_obj = Handle<Object>(result.GetCallbackObject());
8280 }
8281 Smi* property_details = result.GetPropertyDetails().AsSmi();
8282 // DebugLookupResultValue can cause GC so details from LookupResult needs
8283 // to be copied to handles before this.
8284 bool caught_exception = false;
8285 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
8286 &caught_exception);
8287 if (raw_value->IsFailure()) return raw_value;
8288 Handle<Object> value(raw_value);
8289
8290 // If the callback object is a fixed array then it contains JavaScript
8291 // getter and/or setter.
8292 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
8293 result_callback_obj->IsFixedArray();
8294 Handle<FixedArray> details =
8295 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
8296 details->set(0, *value);
8297 details->set(1, property_details);
8298 if (hasJavaScriptAccessors) {
8299 details->set(2,
8300 caught_exception ? Heap::true_value()
8301 : Heap::false_value());
8302 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
8303 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
8304 }
8305
8306 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008307 }
8308 if (i < length - 1) {
8309 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
8310 }
8311 }
8312
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008313 return Heap::undefined_value();
8314}
8315
8316
8317static Object* Runtime_DebugGetProperty(Arguments args) {
8318 HandleScope scope;
8319
8320 ASSERT(args.length() == 2);
8321
8322 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8323 CONVERT_ARG_CHECKED(String, name, 1);
8324
8325 LookupResult result;
8326 obj->Lookup(*name, &result);
8327 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008328 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008329 }
8330 return Heap::undefined_value();
8331}
8332
8333
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008334// Return the property type calculated from the property details.
8335// args[0]: smi with property details.
8336static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
8337 ASSERT(args.length() == 1);
8338 CONVERT_CHECKED(Smi, details, args[0]);
8339 PropertyType type = PropertyDetails(details).type();
8340 return Smi::FromInt(static_cast<int>(type));
8341}
8342
8343
8344// Return the property attribute calculated from the property details.
8345// args[0]: smi with property details.
8346static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
8347 ASSERT(args.length() == 1);
8348 CONVERT_CHECKED(Smi, details, args[0]);
8349 PropertyAttributes attributes = PropertyDetails(details).attributes();
8350 return Smi::FromInt(static_cast<int>(attributes));
8351}
8352
8353
8354// Return the property insertion index calculated from the property details.
8355// args[0]: smi with property details.
8356static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
8357 ASSERT(args.length() == 1);
8358 CONVERT_CHECKED(Smi, details, args[0]);
8359 int index = PropertyDetails(details).index();
8360 return Smi::FromInt(index);
8361}
8362
8363
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008364// Return property value from named interceptor.
8365// args[0]: object
8366// args[1]: property name
8367static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
8368 HandleScope scope;
8369 ASSERT(args.length() == 2);
8370 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8371 RUNTIME_ASSERT(obj->HasNamedInterceptor());
8372 CONVERT_ARG_CHECKED(String, name, 1);
8373
8374 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008375 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008376}
8377
8378
8379// Return element value from indexed interceptor.
8380// args[0]: object
8381// args[1]: index
8382static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
8383 HandleScope scope;
8384 ASSERT(args.length() == 2);
8385 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8386 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
8387 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
8388
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008389 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008390}
8391
8392
8393static Object* Runtime_CheckExecutionState(Arguments args) {
8394 ASSERT(args.length() >= 1);
8395 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00008396 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008397 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008398 return Top::Throw(Heap::illegal_execution_state_symbol());
8399 }
8400
8401 return Heap::true_value();
8402}
8403
8404
8405static Object* Runtime_GetFrameCount(Arguments args) {
8406 HandleScope scope;
8407 ASSERT(args.length() == 1);
8408
8409 // Check arguments.
8410 Object* result = Runtime_CheckExecutionState(args);
8411 if (result->IsFailure()) return result;
8412
8413 // Count all frames which are relevant to debugging stack trace.
8414 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008415 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008416 if (id == StackFrame::NO_ID) {
8417 // If there is no JavaScript stack frame count is 0.
8418 return Smi::FromInt(0);
8419 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008420 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
8421 return Smi::FromInt(n);
8422}
8423
8424
8425static const int kFrameDetailsFrameIdIndex = 0;
8426static const int kFrameDetailsReceiverIndex = 1;
8427static const int kFrameDetailsFunctionIndex = 2;
8428static const int kFrameDetailsArgumentCountIndex = 3;
8429static const int kFrameDetailsLocalCountIndex = 4;
8430static const int kFrameDetailsSourcePositionIndex = 5;
8431static const int kFrameDetailsConstructCallIndex = 6;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008432static const int kFrameDetailsAtReturnIndex = 7;
8433static const int kFrameDetailsDebuggerFrameIndex = 8;
8434static const int kFrameDetailsFirstDynamicIndex = 9;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008435
8436// Return an array with frame details
8437// args[0]: number: break id
8438// args[1]: number: frame index
8439//
8440// The array returned contains the following information:
8441// 0: Frame id
8442// 1: Receiver
8443// 2: Function
8444// 3: Argument count
8445// 4: Local count
8446// 5: Source position
8447// 6: Constructor call
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008448// 7: Is at return
8449// 8: Debugger frame
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008450// Arguments name, value
8451// Locals name, value
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008452// Return value if any
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008453static Object* Runtime_GetFrameDetails(Arguments args) {
8454 HandleScope scope;
8455 ASSERT(args.length() == 2);
8456
8457 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008458 Object* check = Runtime_CheckExecutionState(args);
8459 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008460 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8461
8462 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008463 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008464 if (id == StackFrame::NO_ID) {
8465 // If there are no JavaScript stack frames return undefined.
8466 return Heap::undefined_value();
8467 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008468 int count = 0;
8469 JavaScriptFrameIterator it(id);
8470 for (; !it.done(); it.Advance()) {
8471 if (count == index) break;
8472 count++;
8473 }
8474 if (it.done()) return Heap::undefined_value();
8475
8476 // Traverse the saved contexts chain to find the active context for the
8477 // selected frame.
8478 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008479 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008480 save = save->prev();
8481 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008482 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008483
8484 // Get the frame id.
8485 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
8486
8487 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008488 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008489
8490 // Check for constructor frame.
8491 bool constructor = it.frame()->IsConstructor();
8492
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008493 // Get scope info and read from it for local variable information.
8494 Handle<JSFunction> function(JSFunction::cast(it.frame()->function()));
8495 Handle<Object> scope_info(function->shared()->scope_info());
8496 ScopeInfo<> info(*scope_info);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008497
8498 // Get the context.
8499 Handle<Context> context(Context::cast(it.frame()->context()));
8500
8501 // Get the locals names and values into a temporary array.
8502 //
8503 // TODO(1240907): Hide compiler-introduced stack variables
8504 // (e.g. .result)? For users of the debugger, they will probably be
8505 // confusing.
8506 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
8507 for (int i = 0; i < info.NumberOfLocals(); i++) {
8508 // Name of the local.
8509 locals->set(i * 2, *info.LocalName(i));
8510
8511 // Fetch the value of the local - either from the stack or from a
8512 // heap-allocated context.
8513 if (i < info.number_of_stack_slots()) {
8514 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
8515 } else {
8516 Handle<String> name = info.LocalName(i);
8517 // Traverse the context chain to the function context as all local
8518 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008519 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008520 context = Handle<Context>(context->previous());
8521 }
8522 ASSERT(context->is_function_context());
8523 locals->set(i * 2 + 1,
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008524 context->get(ScopeInfo<>::ContextSlotIndex(*scope_info,
8525 *name,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008526 NULL)));
8527 }
8528 }
8529
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008530 // Check whether this frame is positioned at return.
8531 int at_return = (index == 0) ? Debug::IsBreakAtReturn(it.frame()) : false;
8532
8533 // If positioned just before return find the value to be returned and add it
8534 // to the frame information.
8535 Handle<Object> return_value = Factory::undefined_value();
8536 if (at_return) {
8537 StackFrameIterator it2;
8538 Address internal_frame_sp = NULL;
8539 while (!it2.done()) {
8540 if (it2.frame()->is_internal()) {
8541 internal_frame_sp = it2.frame()->sp();
8542 } else {
8543 if (it2.frame()->is_java_script()) {
8544 if (it2.frame()->id() == it.frame()->id()) {
8545 // The internal frame just before the JavaScript frame contains the
8546 // value to return on top. A debug break at return will create an
8547 // internal frame to store the return value (eax/rax/r0) before
8548 // entering the debug break exit frame.
8549 if (internal_frame_sp != NULL) {
8550 return_value =
8551 Handle<Object>(Memory::Object_at(internal_frame_sp));
8552 break;
8553 }
8554 }
8555 }
8556
8557 // Indicate that the previous frame was not an internal frame.
8558 internal_frame_sp = NULL;
8559 }
8560 it2.Advance();
8561 }
8562 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008563
8564 // Now advance to the arguments adapter frame (if any). It contains all
8565 // the provided parameters whereas the function frame always have the number
8566 // of arguments matching the functions parameters. The rest of the
8567 // information (except for what is collected above) is the same.
8568 it.AdvanceToArgumentsFrame();
8569
8570 // Find the number of arguments to fill. At least fill the number of
8571 // parameters for the function and fill more if more parameters are provided.
8572 int argument_count = info.number_of_parameters();
8573 if (argument_count < it.frame()->GetProvidedParametersCount()) {
8574 argument_count = it.frame()->GetProvidedParametersCount();
8575 }
8576
8577 // Calculate the size of the result.
8578 int details_size = kFrameDetailsFirstDynamicIndex +
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008579 2 * (argument_count + info.NumberOfLocals()) +
8580 (at_return ? 1 : 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008581 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8582
8583 // Add the frame id.
8584 details->set(kFrameDetailsFrameIdIndex, *frame_id);
8585
8586 // Add the function (same as in function frame).
8587 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
8588
8589 // Add the arguments count.
8590 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
8591
8592 // Add the locals count
8593 details->set(kFrameDetailsLocalCountIndex,
8594 Smi::FromInt(info.NumberOfLocals()));
8595
8596 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00008597 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008598 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
8599 } else {
8600 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
8601 }
8602
8603 // Add the constructor information.
8604 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
8605
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008606 // Add the at return information.
8607 details->set(kFrameDetailsAtReturnIndex, Heap::ToBoolean(at_return));
8608
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008609 // Add information on whether this frame is invoked in the debugger context.
8610 details->set(kFrameDetailsDebuggerFrameIndex,
8611 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
8612
8613 // Fill the dynamic part.
8614 int details_index = kFrameDetailsFirstDynamicIndex;
8615
8616 // Add arguments name and value.
8617 for (int i = 0; i < argument_count; i++) {
8618 // Name of the argument.
8619 if (i < info.number_of_parameters()) {
8620 details->set(details_index++, *info.parameter_name(i));
8621 } else {
8622 details->set(details_index++, Heap::undefined_value());
8623 }
8624
8625 // Parameter value.
8626 if (i < it.frame()->GetProvidedParametersCount()) {
8627 details->set(details_index++, it.frame()->GetParameter(i));
8628 } else {
8629 details->set(details_index++, Heap::undefined_value());
8630 }
8631 }
8632
8633 // Add locals name and value from the temporary copy from the function frame.
8634 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
8635 details->set(details_index++, locals->get(i));
8636 }
8637
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008638 // Add the value being returned.
8639 if (at_return) {
8640 details->set(details_index++, *return_value);
8641 }
8642
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008643 // Add the receiver (same as in function frame).
8644 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
8645 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
8646 Handle<Object> receiver(it.frame()->receiver());
8647 if (!receiver->IsJSObject()) {
8648 // If the receiver is NOT a JSObject we have hit an optimization
8649 // where a value object is not converted into a wrapped JS objects.
8650 // To hide this optimization from the debugger, we wrap the receiver
8651 // by creating correct wrapper object based on the calling frame's
8652 // global context.
8653 it.Advance();
8654 Handle<Context> calling_frames_global_context(
8655 Context::cast(Context::cast(it.frame()->context())->global_context()));
8656 receiver = Factory::ToObject(receiver, calling_frames_global_context);
8657 }
8658 details->set(kFrameDetailsReceiverIndex, *receiver);
8659
8660 ASSERT_EQ(details_size, details_index);
8661 return *Factory::NewJSArrayWithElements(details);
8662}
8663
8664
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008665// Copy all the context locals into an object used to materialize a scope.
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008666static void CopyContextLocalsToScopeObject(Handle<SharedFunctionInfo> shared,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008667 ScopeInfo<>& scope_info,
8668 Handle<Context> context,
8669 Handle<JSObject> scope_object) {
8670 // Fill all context locals to the context extension.
8671 for (int i = Context::MIN_CONTEXT_SLOTS;
8672 i < scope_info.number_of_context_slots();
8673 i++) {
8674 int context_index =
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008675 ScopeInfo<>::ContextSlotIndex(shared->scope_info(),
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008676 *scope_info.context_slot_name(i),
8677 NULL);
8678
8679 // Don't include the arguments shadow (.arguments) context variable.
8680 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
8681 SetProperty(scope_object,
8682 scope_info.context_slot_name(i),
8683 Handle<Object>(context->get(context_index)), NONE);
8684 }
8685 }
8686}
8687
8688
8689// Create a plain JSObject which materializes the local scope for the specified
8690// frame.
8691static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
8692 Handle<JSFunction> function(JSFunction::cast(frame->function()));
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008693 Handle<SharedFunctionInfo> shared(function->shared());
8694 ScopeInfo<> scope_info(shared->scope_info());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008695
8696 // Allocate and initialize a JSObject with all the arguments, stack locals
8697 // heap locals and extension properties of the debugged function.
8698 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
8699
8700 // First fill all parameters.
8701 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8702 SetProperty(local_scope,
8703 scope_info.parameter_name(i),
8704 Handle<Object>(frame->GetParameter(i)), NONE);
8705 }
8706
8707 // Second fill all stack locals.
8708 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
8709 SetProperty(local_scope,
8710 scope_info.stack_slot_name(i),
8711 Handle<Object>(frame->GetExpression(i)), NONE);
8712 }
8713
8714 // Third fill all context locals.
8715 Handle<Context> frame_context(Context::cast(frame->context()));
8716 Handle<Context> function_context(frame_context->fcontext());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008717 CopyContextLocalsToScopeObject(shared, scope_info,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008718 function_context, local_scope);
8719
8720 // Finally copy any properties from the function context extension. This will
8721 // be variables introduced by eval.
8722 if (function_context->closure() == *function) {
8723 if (function_context->has_extension() &&
8724 !function_context->IsGlobalContext()) {
8725 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008726 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008727 for (int i = 0; i < keys->length(); i++) {
8728 // Names of variables introduced by eval are strings.
8729 ASSERT(keys->get(i)->IsString());
8730 Handle<String> key(String::cast(keys->get(i)));
8731 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
8732 }
8733 }
8734 }
8735 return local_scope;
8736}
8737
8738
8739// Create a plain JSObject which materializes the closure content for the
8740// context.
8741static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
8742 ASSERT(context->is_function_context());
8743
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008744 Handle<SharedFunctionInfo> shared(context->closure()->shared());
8745 ScopeInfo<> scope_info(shared->scope_info());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008746
8747 // Allocate and initialize a JSObject with all the content of theis function
8748 // closure.
8749 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
8750
8751 // Check whether the arguments shadow object exists.
8752 int arguments_shadow_index =
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008753 ScopeInfo<>::ContextSlotIndex(shared->scope_info(),
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008754 Heap::arguments_shadow_symbol(),
8755 NULL);
8756 if (arguments_shadow_index >= 0) {
8757 // In this case all the arguments are available in the arguments shadow
8758 // object.
8759 Handle<JSObject> arguments_shadow(
8760 JSObject::cast(context->get(arguments_shadow_index)));
8761 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8762 SetProperty(closure_scope,
8763 scope_info.parameter_name(i),
8764 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
8765 }
8766 }
8767
8768 // Fill all context locals to the context extension.
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008769 CopyContextLocalsToScopeObject(shared, scope_info, context, closure_scope);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008770
8771 // Finally copy any properties from the function context extension. This will
8772 // be variables introduced by eval.
8773 if (context->has_extension()) {
8774 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008775 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008776 for (int i = 0; i < keys->length(); i++) {
8777 // Names of variables introduced by eval are strings.
8778 ASSERT(keys->get(i)->IsString());
8779 Handle<String> key(String::cast(keys->get(i)));
8780 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
8781 }
8782 }
8783
8784 return closure_scope;
8785}
8786
8787
8788// Iterate over the actual scopes visible from a stack frame. All scopes are
8789// backed by an actual context except the local scope, which is inserted
8790// "artifically" in the context chain.
8791class ScopeIterator {
8792 public:
8793 enum ScopeType {
8794 ScopeTypeGlobal = 0,
8795 ScopeTypeLocal,
8796 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00008797 ScopeTypeClosure,
8798 // Every catch block contains an implicit with block (its parameter is
8799 // a JSContextExtensionObject) that extends current scope with a variable
8800 // holding exception object. Such with blocks are treated as scopes of their
8801 // own type.
8802 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008803 };
8804
8805 explicit ScopeIterator(JavaScriptFrame* frame)
8806 : frame_(frame),
8807 function_(JSFunction::cast(frame->function())),
8808 context_(Context::cast(frame->context())),
8809 local_done_(false),
8810 at_local_(false) {
8811
8812 // Check whether the first scope is actually a local scope.
8813 if (context_->IsGlobalContext()) {
8814 // If there is a stack slot for .result then this local scope has been
8815 // created for evaluating top level code and it is not a real local scope.
8816 // Checking for the existence of .result seems fragile, but the scope info
8817 // saved with the code object does not otherwise have that information.
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008818 int index = ScopeInfo<>::StackSlotIndex(function_->shared()->scope_info(),
8819 Heap::result_symbol());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008820 at_local_ = index < 0;
8821 } else if (context_->is_function_context()) {
8822 at_local_ = true;
8823 }
8824 }
8825
8826 // More scopes?
8827 bool Done() { return context_.is_null(); }
8828
8829 // Move to the next scope.
8830 void Next() {
8831 // If at a local scope mark the local scope as passed.
8832 if (at_local_) {
8833 at_local_ = false;
8834 local_done_ = true;
8835
8836 // If the current context is not associated with the local scope the
8837 // current context is the next real scope, so don't move to the next
8838 // context in this case.
8839 if (context_->closure() != *function_) {
8840 return;
8841 }
8842 }
8843
8844 // The global scope is always the last in the chain.
8845 if (context_->IsGlobalContext()) {
8846 context_ = Handle<Context>();
8847 return;
8848 }
8849
8850 // Move to the next context.
8851 if (context_->is_function_context()) {
8852 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
8853 } else {
8854 context_ = Handle<Context>(context_->previous());
8855 }
8856
8857 // If passing the local scope indicate that the current scope is now the
8858 // local scope.
8859 if (!local_done_ &&
8860 (context_->IsGlobalContext() || (context_->is_function_context()))) {
8861 at_local_ = true;
8862 }
8863 }
8864
8865 // Return the type of the current scope.
8866 int Type() {
8867 if (at_local_) {
8868 return ScopeTypeLocal;
8869 }
8870 if (context_->IsGlobalContext()) {
8871 ASSERT(context_->global()->IsGlobalObject());
8872 return ScopeTypeGlobal;
8873 }
8874 if (context_->is_function_context()) {
8875 return ScopeTypeClosure;
8876 }
8877 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00008878 // Current scope is either an explicit with statement or a with statement
8879 // implicitely generated for a catch block.
8880 // If the extension object here is a JSContextExtensionObject then
8881 // current with statement is one frome a catch block otherwise it's a
8882 // regular with statement.
8883 if (context_->extension()->IsJSContextExtensionObject()) {
8884 return ScopeTypeCatch;
8885 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008886 return ScopeTypeWith;
8887 }
8888
8889 // Return the JavaScript object with the content of the current scope.
8890 Handle<JSObject> ScopeObject() {
8891 switch (Type()) {
8892 case ScopeIterator::ScopeTypeGlobal:
8893 return Handle<JSObject>(CurrentContext()->global());
8894 break;
8895 case ScopeIterator::ScopeTypeLocal:
8896 // Materialize the content of the local scope into a JSObject.
8897 return MaterializeLocalScope(frame_);
8898 break;
8899 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00008900 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008901 // Return the with object.
8902 return Handle<JSObject>(CurrentContext()->extension());
8903 break;
8904 case ScopeIterator::ScopeTypeClosure:
8905 // Materialize the content of the closure scope into a JSObject.
8906 return MaterializeClosure(CurrentContext());
8907 break;
8908 }
8909 UNREACHABLE();
8910 return Handle<JSObject>();
8911 }
8912
8913 // Return the context for this scope. For the local context there might not
8914 // be an actual context.
8915 Handle<Context> CurrentContext() {
8916 if (at_local_ && context_->closure() != *function_) {
8917 return Handle<Context>();
8918 }
8919 return context_;
8920 }
8921
8922#ifdef DEBUG
8923 // Debug print of the content of the current scope.
8924 void DebugPrint() {
8925 switch (Type()) {
8926 case ScopeIterator::ScopeTypeGlobal:
8927 PrintF("Global:\n");
8928 CurrentContext()->Print();
8929 break;
8930
8931 case ScopeIterator::ScopeTypeLocal: {
8932 PrintF("Local:\n");
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008933 ScopeInfo<> scope_info(function_->shared()->scope_info());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008934 scope_info.Print();
8935 if (!CurrentContext().is_null()) {
8936 CurrentContext()->Print();
8937 if (CurrentContext()->has_extension()) {
8938 Handle<JSObject> extension =
8939 Handle<JSObject>(CurrentContext()->extension());
8940 if (extension->IsJSContextExtensionObject()) {
8941 extension->Print();
8942 }
8943 }
8944 }
8945 break;
8946 }
8947
8948 case ScopeIterator::ScopeTypeWith: {
8949 PrintF("With:\n");
8950 Handle<JSObject> extension =
8951 Handle<JSObject>(CurrentContext()->extension());
8952 extension->Print();
8953 break;
8954 }
8955
ager@chromium.orga1645e22009-09-09 19:27:10 +00008956 case ScopeIterator::ScopeTypeCatch: {
8957 PrintF("Catch:\n");
8958 Handle<JSObject> extension =
8959 Handle<JSObject>(CurrentContext()->extension());
8960 extension->Print();
8961 break;
8962 }
8963
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008964 case ScopeIterator::ScopeTypeClosure: {
8965 PrintF("Closure:\n");
8966 CurrentContext()->Print();
8967 if (CurrentContext()->has_extension()) {
8968 Handle<JSObject> extension =
8969 Handle<JSObject>(CurrentContext()->extension());
8970 if (extension->IsJSContextExtensionObject()) {
8971 extension->Print();
8972 }
8973 }
8974 break;
8975 }
8976
8977 default:
8978 UNREACHABLE();
8979 }
8980 PrintF("\n");
8981 }
8982#endif
8983
8984 private:
8985 JavaScriptFrame* frame_;
8986 Handle<JSFunction> function_;
8987 Handle<Context> context_;
8988 bool local_done_;
8989 bool at_local_;
8990
8991 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
8992};
8993
8994
8995static Object* Runtime_GetScopeCount(Arguments args) {
8996 HandleScope scope;
8997 ASSERT(args.length() == 2);
8998
8999 // Check arguments.
9000 Object* check = Runtime_CheckExecutionState(args);
9001 if (check->IsFailure()) return check;
9002 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9003
9004 // Get the frame where the debugging is performed.
9005 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9006 JavaScriptFrameIterator it(id);
9007 JavaScriptFrame* frame = it.frame();
9008
9009 // Count the visible scopes.
9010 int n = 0;
9011 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
9012 n++;
9013 }
9014
9015 return Smi::FromInt(n);
9016}
9017
9018
9019static const int kScopeDetailsTypeIndex = 0;
9020static const int kScopeDetailsObjectIndex = 1;
9021static const int kScopeDetailsSize = 2;
9022
9023// Return an array with scope details
9024// args[0]: number: break id
9025// args[1]: number: frame index
9026// args[2]: number: scope index
9027//
9028// The array returned contains the following information:
9029// 0: Scope type
9030// 1: Scope object
9031static Object* Runtime_GetScopeDetails(Arguments args) {
9032 HandleScope scope;
9033 ASSERT(args.length() == 3);
9034
9035 // Check arguments.
9036 Object* check = Runtime_CheckExecutionState(args);
9037 if (check->IsFailure()) return check;
9038 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9039 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
9040
9041 // Get the frame where the debugging is performed.
9042 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9043 JavaScriptFrameIterator frame_it(id);
9044 JavaScriptFrame* frame = frame_it.frame();
9045
9046 // Find the requested scope.
9047 int n = 0;
9048 ScopeIterator it(frame);
9049 for (; !it.Done() && n < index; it.Next()) {
9050 n++;
9051 }
9052 if (it.Done()) {
9053 return Heap::undefined_value();
9054 }
9055
9056 // Calculate the size of the result.
9057 int details_size = kScopeDetailsSize;
9058 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
9059
9060 // Fill in scope details.
9061 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
9062 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
9063
9064 return *Factory::NewJSArrayWithElements(details);
9065}
9066
9067
9068static Object* Runtime_DebugPrintScopes(Arguments args) {
9069 HandleScope scope;
9070 ASSERT(args.length() == 0);
9071
9072#ifdef DEBUG
9073 // Print the scopes for the top frame.
9074 StackFrameLocator locator;
9075 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
9076 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
9077 it.DebugPrint();
9078 }
9079#endif
9080 return Heap::undefined_value();
9081}
9082
9083
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009084static Object* Runtime_GetCFrames(Arguments args) {
9085 HandleScope scope;
9086 ASSERT(args.length() == 1);
9087 Object* result = Runtime_CheckExecutionState(args);
9088 if (result->IsFailure()) return result;
9089
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00009090#if V8_HOST_ARCH_64_BIT
9091 UNIMPLEMENTED();
9092 return Heap::undefined_value();
9093#else
9094
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009095 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009096 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
9097 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009098 if (frames_count == OS::kStackWalkError) {
9099 return Heap::undefined_value();
9100 }
9101
9102 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
9103 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
9104 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
9105 for (int i = 0; i < frames_count; i++) {
9106 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
9107 frame_value->SetProperty(
9108 *address_str,
9109 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
9110 NONE);
9111
9112 // Get the stack walk text for this frame.
9113 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009114 int frame_text_length = StrLength(frames[i].text);
9115 if (frame_text_length > 0) {
9116 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009117 frame_text = Factory::NewStringFromAscii(str);
9118 }
9119
9120 if (!frame_text.is_null()) {
9121 frame_value->SetProperty(*text_str, *frame_text, NONE);
9122 }
9123
9124 frames_array->set(i, *frame_value);
9125 }
9126 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00009127#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009128}
9129
9130
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00009131static Object* Runtime_GetThreadCount(Arguments args) {
9132 HandleScope scope;
9133 ASSERT(args.length() == 1);
9134
9135 // Check arguments.
9136 Object* result = Runtime_CheckExecutionState(args);
9137 if (result->IsFailure()) return result;
9138
9139 // Count all archived V8 threads.
9140 int n = 0;
9141 for (ThreadState* thread = ThreadState::FirstInUse();
9142 thread != NULL;
9143 thread = thread->Next()) {
9144 n++;
9145 }
9146
9147 // Total number of threads is current thread and archived threads.
9148 return Smi::FromInt(n + 1);
9149}
9150
9151
9152static const int kThreadDetailsCurrentThreadIndex = 0;
9153static const int kThreadDetailsThreadIdIndex = 1;
9154static const int kThreadDetailsSize = 2;
9155
9156// Return an array with thread details
9157// args[0]: number: break id
9158// args[1]: number: thread index
9159//
9160// The array returned contains the following information:
9161// 0: Is current thread?
9162// 1: Thread id
9163static Object* Runtime_GetThreadDetails(Arguments args) {
9164 HandleScope scope;
9165 ASSERT(args.length() == 2);
9166
9167 // Check arguments.
9168 Object* check = Runtime_CheckExecutionState(args);
9169 if (check->IsFailure()) return check;
9170 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
9171
9172 // Allocate array for result.
9173 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
9174
9175 // Thread index 0 is current thread.
9176 if (index == 0) {
9177 // Fill the details.
9178 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
9179 details->set(kThreadDetailsThreadIdIndex,
9180 Smi::FromInt(ThreadManager::CurrentId()));
9181 } else {
9182 // Find the thread with the requested index.
9183 int n = 1;
9184 ThreadState* thread = ThreadState::FirstInUse();
9185 while (index != n && thread != NULL) {
9186 thread = thread->Next();
9187 n++;
9188 }
9189 if (thread == NULL) {
9190 return Heap::undefined_value();
9191 }
9192
9193 // Fill the details.
9194 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
9195 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
9196 }
9197
9198 // Convert to JS array and return.
9199 return *Factory::NewJSArrayWithElements(details);
9200}
9201
9202
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009203static Object* Runtime_GetBreakLocations(Arguments args) {
9204 HandleScope scope;
9205 ASSERT(args.length() == 1);
9206
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009207 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9208 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009209 // Find the number of break points
9210 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
9211 if (break_locations->IsUndefined()) return Heap::undefined_value();
9212 // Return array as JS array
9213 return *Factory::NewJSArrayWithElements(
9214 Handle<FixedArray>::cast(break_locations));
9215}
9216
9217
9218// Set a break point in a function
9219// args[0]: function
9220// args[1]: number: break source position (within the function source)
9221// args[2]: number: break point object
9222static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
9223 HandleScope scope;
9224 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009225 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9226 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009227 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9228 RUNTIME_ASSERT(source_position >= 0);
9229 Handle<Object> break_point_object_arg = args.at<Object>(2);
9230
9231 // Set break point.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009232 Debug::SetBreakPoint(shared, break_point_object_arg, &source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009233
lrn@chromium.org32d961d2010-06-30 09:09:34 +00009234 return Smi::FromInt(source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009235}
9236
9237
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009238Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
9239 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009240 // Iterate the heap looking for SharedFunctionInfo generated from the
9241 // script. The inner most SharedFunctionInfo containing the source position
9242 // for the requested break point is found.
9243 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
9244 // which is found is not compiled it is compiled and the heap is iterated
9245 // again as the compilation might create inner functions from the newly
9246 // compiled function and the actual requested break point might be in one of
9247 // these functions.
9248 bool done = false;
9249 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00009250 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009251 Handle<SharedFunctionInfo> target;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009252 while (!done) {
9253 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009254 for (HeapObject* obj = iterator.next();
9255 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009256 if (obj->IsSharedFunctionInfo()) {
9257 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
9258 if (shared->script() == *script) {
9259 // If the SharedFunctionInfo found has the requested script data and
9260 // contains the source position it is a candidate.
9261 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00009262 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009263 start_position = shared->start_position();
9264 }
9265 if (start_position <= position &&
9266 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00009267 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009268 // candidate this is the new candidate.
9269 if (target.is_null()) {
9270 target_start_position = start_position;
9271 target = shared;
9272 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00009273 if (target_start_position == start_position &&
9274 shared->end_position() == target->end_position()) {
9275 // If a top-level function contain only one function
9276 // declartion the source for the top-level and the function is
9277 // the same. In that case prefer the non top-level function.
9278 if (!shared->is_toplevel()) {
9279 target_start_position = start_position;
9280 target = shared;
9281 }
9282 } else if (target_start_position <= start_position &&
9283 shared->end_position() <= target->end_position()) {
9284 // This containment check includes equality as a function inside
9285 // a top-level function can share either start or end position
9286 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009287 target_start_position = start_position;
9288 target = shared;
9289 }
9290 }
9291 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009292 }
9293 }
9294 }
9295
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009296 if (target.is_null()) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009297 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009298 }
9299
9300 // If the candidate found is compiled we are done. NOTE: when lazy
9301 // compilation of inner functions is introduced some additional checking
9302 // needs to be done here to compile inner functions.
9303 done = target->is_compiled();
9304 if (!done) {
9305 // If the candidate is not compiled compile it to reveal any inner
9306 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009307 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009308 }
9309 }
9310
9311 return *target;
9312}
9313
9314
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009315// Changes the state of a break point in a script and returns source position
9316// where break point was set. NOTE: Regarding performance see the NOTE for
9317// GetScriptFromScriptData.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009318// args[0]: script to set break point in
9319// args[1]: number: break source position (within the script source)
9320// args[2]: number: break point object
9321static Object* Runtime_SetScriptBreakPoint(Arguments args) {
9322 HandleScope scope;
9323 ASSERT(args.length() == 3);
9324 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
9325 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9326 RUNTIME_ASSERT(source_position >= 0);
9327 Handle<Object> break_point_object_arg = args.at<Object>(2);
9328
9329 // Get the script from the script wrapper.
9330 RUNTIME_ASSERT(wrapper->value()->IsScript());
9331 Handle<Script> script(Script::cast(wrapper->value()));
9332
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009333 Object* result = Runtime::FindSharedFunctionInfoInScript(
9334 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009335 if (!result->IsUndefined()) {
9336 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
9337 // Find position within function. The script position might be before the
9338 // source position of the first function.
9339 int position;
9340 if (shared->start_position() > source_position) {
9341 position = 0;
9342 } else {
9343 position = source_position - shared->start_position();
9344 }
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009345 Debug::SetBreakPoint(shared, break_point_object_arg, &position);
9346 position += shared->start_position();
9347 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009348 }
9349 return Heap::undefined_value();
9350}
9351
9352
9353// Clear a break point
9354// args[0]: number: break point object
9355static Object* Runtime_ClearBreakPoint(Arguments args) {
9356 HandleScope scope;
9357 ASSERT(args.length() == 1);
9358 Handle<Object> break_point_object_arg = args.at<Object>(0);
9359
9360 // Clear break point.
9361 Debug::ClearBreakPoint(break_point_object_arg);
9362
9363 return Heap::undefined_value();
9364}
9365
9366
9367// Change the state of break on exceptions
9368// args[0]: boolean indicating uncaught exceptions
9369// args[1]: boolean indicating on/off
9370static Object* Runtime_ChangeBreakOnException(Arguments args) {
9371 HandleScope scope;
9372 ASSERT(args.length() == 2);
9373 ASSERT(args[0]->IsNumber());
9374 ASSERT(args[1]->IsBoolean());
9375
9376 // Update break point state
9377 ExceptionBreakType type =
9378 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
9379 bool enable = args[1]->ToBoolean()->IsTrue();
9380 Debug::ChangeBreakOnException(type, enable);
9381 return Heap::undefined_value();
9382}
9383
9384
9385// Prepare for stepping
9386// args[0]: break id for checking execution state
9387// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00009388// args[2]: number of times to perform the step, for step out it is the number
9389// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009390static Object* Runtime_PrepareStep(Arguments args) {
9391 HandleScope scope;
9392 ASSERT(args.length() == 3);
9393 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009394 Object* check = Runtime_CheckExecutionState(args);
9395 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009396 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
9397 return Top::Throw(Heap::illegal_argument_symbol());
9398 }
9399
9400 // Get the step action and check validity.
9401 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
9402 if (step_action != StepIn &&
9403 step_action != StepNext &&
9404 step_action != StepOut &&
9405 step_action != StepInMin &&
9406 step_action != StepMin) {
9407 return Top::Throw(Heap::illegal_argument_symbol());
9408 }
9409
9410 // Get the number of steps.
9411 int step_count = NumberToInt32(args[2]);
9412 if (step_count < 1) {
9413 return Top::Throw(Heap::illegal_argument_symbol());
9414 }
9415
ager@chromium.orga1645e22009-09-09 19:27:10 +00009416 // Clear all current stepping setup.
9417 Debug::ClearStepping();
9418
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009419 // Prepare step.
9420 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
9421 return Heap::undefined_value();
9422}
9423
9424
9425// Clear all stepping set by PrepareStep.
9426static Object* Runtime_ClearStepping(Arguments args) {
9427 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009428 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009429 Debug::ClearStepping();
9430 return Heap::undefined_value();
9431}
9432
9433
9434// Creates a copy of the with context chain. The copy of the context chain is
9435// is linked to the function context supplied.
9436static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
9437 Handle<Context> function_context) {
9438 // At the bottom of the chain. Return the function context to link to.
9439 if (context_chain->is_function_context()) {
9440 return function_context;
9441 }
9442
9443 // Recursively copy the with contexts.
9444 Handle<Context> previous(context_chain->previous());
9445 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
9446 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009447 CopyWithContextChain(function_context, previous),
9448 extension,
9449 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009450}
9451
9452
9453// Helper function to find or create the arguments object for
9454// Runtime_DebugEvaluate.
9455static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
9456 Handle<JSFunction> function,
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009457 Handle<Object> scope_info,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009458 const ScopeInfo<>* sinfo,
9459 Handle<Context> function_context) {
9460 // Try to find the value of 'arguments' to pass as parameter. If it is not
9461 // found (that is the debugged function does not reference 'arguments' and
9462 // does not support eval) then create an 'arguments' object.
9463 int index;
9464 if (sinfo->number_of_stack_slots() > 0) {
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009465 index = ScopeInfo<>::StackSlotIndex(*scope_info, Heap::arguments_symbol());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009466 if (index != -1) {
9467 return Handle<Object>(frame->GetExpression(index));
9468 }
9469 }
9470
9471 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009472 index = ScopeInfo<>::ContextSlotIndex(*scope_info, Heap::arguments_symbol(),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009473 NULL);
9474 if (index != -1) {
9475 return Handle<Object>(function_context->get(index));
9476 }
9477 }
9478
9479 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009480 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
9481 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009482
9483 AssertNoAllocation no_gc;
9484 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009485 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009486 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009487 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009488 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009489 return arguments;
9490}
9491
9492
9493// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00009494// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009495// extension part has all the parameters and locals of the function on the
9496// stack frame. A function which calls eval with the code to evaluate is then
9497// compiled in this context and called in this context. As this context
9498// replaces the context of the function on the stack frame a new (empty)
9499// function is created as well to be used as the closure for the context.
9500// This function and the context acts as replacements for the function on the
9501// stack frame presenting the same view of the values of parameters and
9502// local variables as if the piece of JavaScript was evaluated at the point
9503// where the function on the stack frame is currently stopped.
9504static Object* Runtime_DebugEvaluate(Arguments args) {
9505 HandleScope scope;
9506
9507 // Check the execution state and decode arguments frame and source to be
9508 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009509 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009510 Object* check_result = Runtime_CheckExecutionState(args);
9511 if (check_result->IsFailure()) return check_result;
9512 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9513 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009514 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
9515
9516 // Handle the processing of break.
9517 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009518
9519 // Get the frame where the debugging is performed.
9520 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9521 JavaScriptFrameIterator it(id);
9522 JavaScriptFrame* frame = it.frame();
9523 Handle<JSFunction> function(JSFunction::cast(frame->function()));
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009524 Handle<Object> scope_info(function->shared()->scope_info());
9525 ScopeInfo<> sinfo(*scope_info);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009526
9527 // Traverse the saved contexts chain to find the active context for the
9528 // selected frame.
9529 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009530 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009531 save = save->prev();
9532 }
9533 ASSERT(save != NULL);
9534 SaveContext savex;
9535 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009536
9537 // Create the (empty) function replacing the function on the stack frame for
9538 // the purpose of evaluating in the context created below. It is important
9539 // that this function does not describe any parameters and local variables
9540 // in the context. If it does then this will cause problems with the lookup
9541 // in Context::Lookup, where context slots for parameters and local variables
9542 // are looked at before the extension object.
9543 Handle<JSFunction> go_between =
9544 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
9545 go_between->set_context(function->context());
9546#ifdef DEBUG
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009547 ScopeInfo<> go_between_sinfo(go_between->shared()->scope_info());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009548 ASSERT(go_between_sinfo.number_of_parameters() == 0);
9549 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
9550#endif
9551
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009552 // Materialize the content of the local scope into a JSObject.
9553 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009554
9555 // Allocate a new context for the debug evaluation and set the extension
9556 // object build.
9557 Handle<Context> context =
9558 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009559 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009560 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009561 Handle<Context> frame_context(Context::cast(frame->context()));
9562 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009563 context = CopyWithContextChain(frame_context, context);
9564
9565 // Wrap the evaluation statement in a new function compiled in the newly
9566 // created context. The function has one parameter which has to be called
9567 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00009568 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009569 // function(arguments,__source__) {return eval(__source__);}
9570 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00009571 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009572 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009573 Handle<String> function_source =
9574 Factory::NewStringFromAscii(Vector<const char>(source_str,
9575 source_str_length));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009576 Handle<SharedFunctionInfo> shared =
ager@chromium.org381abbb2009-02-25 13:23:22 +00009577 Compiler::CompileEval(function_source,
9578 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00009579 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00009580 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009581 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009582 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009583 Factory::NewFunctionFromSharedFunctionInfo(shared, context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009584
9585 // Invoke the result of the compilation to get the evaluation function.
9586 bool has_pending_exception;
9587 Handle<Object> receiver(frame->receiver());
9588 Handle<Object> evaluation_function =
9589 Execution::Call(compiled_function, receiver, 0, NULL,
9590 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009591 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009592
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009593 Handle<Object> arguments = GetArgumentsObject(frame, function, scope_info,
9594 &sinfo, function_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009595
9596 // Invoke the evaluation function and return the result.
9597 const int argc = 2;
9598 Object** argv[argc] = { arguments.location(),
9599 Handle<Object>::cast(source).location() };
9600 Handle<Object> result =
9601 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
9602 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009603 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009604
9605 // Skip the global proxy as it has no properties and always delegates to the
9606 // real global object.
9607 if (result->IsJSGlobalProxy()) {
9608 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
9609 }
9610
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009611 return *result;
9612}
9613
9614
9615static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
9616 HandleScope scope;
9617
9618 // Check the execution state and decode arguments frame and source to be
9619 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009620 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009621 Object* check_result = Runtime_CheckExecutionState(args);
9622 if (check_result->IsFailure()) return check_result;
9623 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009624 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
9625
9626 // Handle the processing of break.
9627 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009628
9629 // Enter the top context from before the debugger was invoked.
9630 SaveContext save;
9631 SaveContext* top = &save;
9632 while (top != NULL && *top->context() == *Debug::debug_context()) {
9633 top = top->prev();
9634 }
9635 if (top != NULL) {
9636 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009637 }
9638
9639 // Get the global context now set to the top context from before the
9640 // debugger was invoked.
9641 Handle<Context> context = Top::global_context();
9642
9643 // Compile the source to be evaluated.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009644 Handle<SharedFunctionInfo> shared =
9645 Compiler::CompileEval(source,
9646 context,
9647 true,
9648 Compiler::DONT_VALIDATE_JSON);
9649 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009650 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009651 Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
9652 context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009653
9654 // Invoke the result of the compilation to get the evaluation function.
9655 bool has_pending_exception;
9656 Handle<Object> receiver = Top::global();
9657 Handle<Object> result =
9658 Execution::Call(compiled_function, receiver, 0, NULL,
9659 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009660 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009661 return *result;
9662}
9663
9664
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009665static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
9666 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009667 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009668
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009669 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009670 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009671
9672 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009673 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00009674 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
9675 // Get the script wrapper in a local handle before calling GetScriptWrapper,
9676 // because using
9677 // instances->set(i, *GetScriptWrapper(script))
9678 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
9679 // already have deferenced the instances handle.
9680 Handle<JSValue> wrapper = GetScriptWrapper(script);
9681 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009682 }
9683
9684 // Return result as a JS array.
9685 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
9686 Handle<JSArray>::cast(result)->SetContent(*instances);
9687 return *result;
9688}
9689
9690
9691// Helper function used by Runtime_DebugReferencedBy below.
9692static int DebugReferencedBy(JSObject* target,
9693 Object* instance_filter, int max_references,
9694 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009695 JSFunction* arguments_function) {
9696 NoHandleAllocation ha;
9697 AssertNoAllocation no_alloc;
9698
9699 // Iterate the heap.
9700 int count = 0;
9701 JSObject* last = NULL;
9702 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009703 HeapObject* heap_obj = NULL;
9704 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009705 (max_references == 0 || count < max_references)) {
9706 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009707 if (heap_obj->IsJSObject()) {
9708 // Skip context extension objects and argument arrays as these are
9709 // checked in the context of functions using them.
9710 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00009711 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009712 obj->map()->constructor() == arguments_function) {
9713 continue;
9714 }
9715
9716 // Check if the JS object has a reference to the object looked for.
9717 if (obj->ReferencesObject(target)) {
9718 // Check instance filter if supplied. This is normally used to avoid
9719 // references from mirror objects (see Runtime_IsInPrototypeChain).
9720 if (!instance_filter->IsUndefined()) {
9721 Object* V = obj;
9722 while (true) {
9723 Object* prototype = V->GetPrototype();
9724 if (prototype->IsNull()) {
9725 break;
9726 }
9727 if (instance_filter == prototype) {
9728 obj = NULL; // Don't add this object.
9729 break;
9730 }
9731 V = prototype;
9732 }
9733 }
9734
9735 if (obj != NULL) {
9736 // Valid reference found add to instance array if supplied an update
9737 // count.
9738 if (instances != NULL && count < instances_size) {
9739 instances->set(count, obj);
9740 }
9741 last = obj;
9742 count++;
9743 }
9744 }
9745 }
9746 }
9747
9748 // Check for circular reference only. This can happen when the object is only
9749 // referenced from mirrors and has a circular reference in which case the
9750 // object is not really alive and would have been garbage collected if not
9751 // referenced from the mirror.
9752 if (count == 1 && last == target) {
9753 count = 0;
9754 }
9755
9756 // Return the number of referencing objects found.
9757 return count;
9758}
9759
9760
9761// Scan the heap for objects with direct references to an object
9762// args[0]: the object to find references to
9763// args[1]: constructor function for instances to exclude (Mirror)
9764// args[2]: the the maximum number of objects to return
9765static Object* Runtime_DebugReferencedBy(Arguments args) {
9766 ASSERT(args.length() == 3);
9767
9768 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009769 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009770
9771 // Check parameters.
9772 CONVERT_CHECKED(JSObject, target, args[0]);
9773 Object* instance_filter = args[1];
9774 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
9775 instance_filter->IsJSObject());
9776 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
9777 RUNTIME_ASSERT(max_references >= 0);
9778
9779 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009780 JSObject* arguments_boilerplate =
9781 Top::context()->global_context()->arguments_boilerplate();
9782 JSFunction* arguments_function =
9783 JSFunction::cast(arguments_boilerplate->map()->constructor());
9784
9785 // Get the number of referencing objects.
9786 int count;
9787 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009788 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009789
9790 // Allocate an array to hold the result.
9791 Object* object = Heap::AllocateFixedArray(count);
9792 if (object->IsFailure()) return object;
9793 FixedArray* instances = FixedArray::cast(object);
9794
9795 // Fill the referencing objects.
9796 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009797 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009798
9799 // Return result as JS array.
9800 Object* result =
9801 Heap::AllocateJSObject(
9802 Top::context()->global_context()->array_function());
9803 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9804 return result;
9805}
9806
9807
9808// Helper function used by Runtime_DebugConstructedBy below.
9809static int DebugConstructedBy(JSFunction* constructor, int max_references,
9810 FixedArray* instances, int instances_size) {
9811 AssertNoAllocation no_alloc;
9812
9813 // Iterate the heap.
9814 int count = 0;
9815 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009816 HeapObject* heap_obj = NULL;
9817 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009818 (max_references == 0 || count < max_references)) {
9819 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009820 if (heap_obj->IsJSObject()) {
9821 JSObject* obj = JSObject::cast(heap_obj);
9822 if (obj->map()->constructor() == constructor) {
9823 // Valid reference found add to instance array if supplied an update
9824 // count.
9825 if (instances != NULL && count < instances_size) {
9826 instances->set(count, obj);
9827 }
9828 count++;
9829 }
9830 }
9831 }
9832
9833 // Return the number of referencing objects found.
9834 return count;
9835}
9836
9837
9838// Scan the heap for objects constructed by a specific function.
9839// args[0]: the constructor to find instances of
9840// args[1]: the the maximum number of objects to return
9841static Object* Runtime_DebugConstructedBy(Arguments args) {
9842 ASSERT(args.length() == 2);
9843
9844 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009845 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009846
9847 // Check parameters.
9848 CONVERT_CHECKED(JSFunction, constructor, args[0]);
9849 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
9850 RUNTIME_ASSERT(max_references >= 0);
9851
9852 // Get the number of referencing objects.
9853 int count;
9854 count = DebugConstructedBy(constructor, max_references, NULL, 0);
9855
9856 // Allocate an array to hold the result.
9857 Object* object = Heap::AllocateFixedArray(count);
9858 if (object->IsFailure()) return object;
9859 FixedArray* instances = FixedArray::cast(object);
9860
9861 // Fill the referencing objects.
9862 count = DebugConstructedBy(constructor, max_references, instances, count);
9863
9864 // Return result as JS array.
9865 Object* result =
9866 Heap::AllocateJSObject(
9867 Top::context()->global_context()->array_function());
9868 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9869 return result;
9870}
9871
9872
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009873// Find the effective prototype object as returned by __proto__.
9874// args[0]: the object to find the prototype for.
9875static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009876 ASSERT(args.length() == 1);
9877
9878 CONVERT_CHECKED(JSObject, obj, args[0]);
9879
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009880 // Use the __proto__ accessor.
9881 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009882}
9883
9884
9885static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009886 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009887 CPU::DebugBreak();
9888 return Heap::undefined_value();
9889}
9890
9891
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009892static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009893#ifdef DEBUG
9894 HandleScope scope;
9895 ASSERT(args.length() == 1);
9896 // Get the function and make sure it is compiled.
9897 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009898 Handle<SharedFunctionInfo> shared(func->shared());
9899 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009900 return Failure::Exception();
9901 }
9902 func->code()->PrintLn();
9903#endif // DEBUG
9904 return Heap::undefined_value();
9905}
ager@chromium.org9085a012009-05-11 19:22:57 +00009906
9907
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009908static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
9909#ifdef DEBUG
9910 HandleScope scope;
9911 ASSERT(args.length() == 1);
9912 // Get the function and make sure it is compiled.
9913 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009914 Handle<SharedFunctionInfo> shared(func->shared());
9915 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009916 return Failure::Exception();
9917 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009918 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009919#endif // DEBUG
9920 return Heap::undefined_value();
9921}
9922
9923
ager@chromium.org9085a012009-05-11 19:22:57 +00009924static Object* Runtime_FunctionGetInferredName(Arguments args) {
9925 NoHandleAllocation ha;
9926 ASSERT(args.length() == 1);
9927
9928 CONVERT_CHECKED(JSFunction, f, args[0]);
9929 return f->shared()->inferred_name();
9930}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009931
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009932
9933static int FindSharedFunctionInfosForScript(Script* script,
9934 FixedArray* buffer) {
9935 AssertNoAllocation no_allocations;
9936
9937 int counter = 0;
9938 int buffer_size = buffer->length();
9939 HeapIterator iterator;
9940 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
9941 ASSERT(obj != NULL);
9942 if (!obj->IsSharedFunctionInfo()) {
9943 continue;
9944 }
9945 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
9946 if (shared->script() != script) {
9947 continue;
9948 }
9949 if (counter < buffer_size) {
9950 buffer->set(counter, shared);
9951 }
9952 counter++;
9953 }
9954 return counter;
9955}
9956
9957// For a script finds all SharedFunctionInfo's in the heap that points
9958// to this script. Returns JSArray of SharedFunctionInfo wrapped
9959// in OpaqueReferences.
9960static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
9961 Arguments args) {
9962 ASSERT(args.length() == 1);
9963 HandleScope scope;
9964 CONVERT_CHECKED(JSValue, script_value, args[0]);
9965
9966 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
9967
9968 const int kBufferSize = 32;
9969
9970 Handle<FixedArray> array;
9971 array = Factory::NewFixedArray(kBufferSize);
9972 int number = FindSharedFunctionInfosForScript(*script, *array);
9973 if (number > kBufferSize) {
9974 array = Factory::NewFixedArray(number);
9975 FindSharedFunctionInfosForScript(*script, *array);
9976 }
9977
9978 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
9979 result->set_length(Smi::FromInt(number));
9980
9981 LiveEdit::WrapSharedFunctionInfos(result);
9982
9983 return *result;
9984}
9985
9986// For a script calculates compilation information about all its functions.
9987// The script source is explicitly specified by the second argument.
9988// The source of the actual script is not used, however it is important that
9989// all generated code keeps references to this particular instance of script.
9990// Returns a JSArray of compilation infos. The array is ordered so that
9991// each function with all its descendant is always stored in a continues range
9992// with the function itself going first. The root function is a script function.
9993static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
9994 ASSERT(args.length() == 2);
9995 HandleScope scope;
9996 CONVERT_CHECKED(JSValue, script, args[0]);
9997 CONVERT_ARG_CHECKED(String, source, 1);
9998 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
9999
10000 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
10001
10002 if (Top::has_pending_exception()) {
10003 return Failure::Exception();
10004 }
10005
10006 return result;
10007}
10008
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010009// Changes the source of the script to a new_source.
10010// If old_script_name is provided (i.e. is a String), also creates a copy of
10011// the script with its original source and sends notification to debugger.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010012static Object* Runtime_LiveEditReplaceScript(Arguments args) {
10013 ASSERT(args.length() == 3);
10014 HandleScope scope;
10015 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
10016 CONVERT_ARG_CHECKED(String, new_source, 1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010017 Handle<Object> old_script_name(args[2]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010018
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010019 CONVERT_CHECKED(Script, original_script_pointer,
10020 original_script_value->value());
10021 Handle<Script> original_script(original_script_pointer);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010022
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010023 Object* old_script = LiveEdit::ChangeScriptSource(original_script,
10024 new_source,
10025 old_script_name);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010026
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010027 if (old_script->IsScript()) {
10028 Handle<Script> script_handle(Script::cast(old_script));
10029 return *(GetScriptWrapper(script_handle));
10030 } else {
10031 return Heap::null_value();
10032 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010033}
10034
10035// Replaces code of SharedFunctionInfo with a new one.
10036static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
10037 ASSERT(args.length() == 2);
10038 HandleScope scope;
10039 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
10040 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
10041
ager@chromium.orgac091b72010-05-05 07:34:42 +000010042 return LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010043}
10044
10045// Connects SharedFunctionInfo to another script.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010046static Object* Runtime_LiveEditFunctionSetScript(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010047 ASSERT(args.length() == 2);
10048 HandleScope scope;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010049 Handle<Object> function_object(args[0]);
10050 Handle<Object> script_object(args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010051
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010052 if (function_object->IsJSValue()) {
10053 Handle<JSValue> function_wrapper = Handle<JSValue>::cast(function_object);
10054 if (script_object->IsJSValue()) {
10055 CONVERT_CHECKED(Script, script, JSValue::cast(*script_object)->value());
10056 script_object = Handle<Object>(script);
10057 }
10058
10059 LiveEdit::SetFunctionScript(function_wrapper, script_object);
10060 } else {
10061 // Just ignore this. We may not have a SharedFunctionInfo for some functions
10062 // and we check it in this function.
10063 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010064
10065 return Heap::undefined_value();
10066}
10067
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010068
10069// In a code of a parent function replaces original function as embedded object
10070// with a substitution one.
10071static Object* Runtime_LiveEditReplaceRefToNestedFunction(Arguments args) {
10072 ASSERT(args.length() == 3);
10073 HandleScope scope;
10074
10075 CONVERT_ARG_CHECKED(JSValue, parent_wrapper, 0);
10076 CONVERT_ARG_CHECKED(JSValue, orig_wrapper, 1);
10077 CONVERT_ARG_CHECKED(JSValue, subst_wrapper, 2);
10078
10079 LiveEdit::ReplaceRefToNestedFunction(parent_wrapper, orig_wrapper,
10080 subst_wrapper);
10081
10082 return Heap::undefined_value();
10083}
10084
10085
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010086// Updates positions of a shared function info (first parameter) according
10087// to script source change. Text change is described in second parameter as
10088// array of groups of 3 numbers:
10089// (change_begin, change_end, change_end_new_position).
10090// Each group describes a change in text; groups are sorted by change_begin.
10091static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
10092 ASSERT(args.length() == 2);
10093 HandleScope scope;
10094 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
10095 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
10096
ager@chromium.orgac091b72010-05-05 07:34:42 +000010097 return LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010098}
10099
10100
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010101// For array of SharedFunctionInfo's (each wrapped in JSValue)
10102// checks that none of them have activations on stacks (of any thread).
10103// Returns array of the same length with corresponding results of
10104// LiveEdit::FunctionPatchabilityStatus type.
ager@chromium.org357bf652010-04-12 11:30:10 +000010105static Object* Runtime_LiveEditCheckAndDropActivations(Arguments args) {
10106 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010107 HandleScope scope;
10108 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +000010109 CONVERT_BOOLEAN_CHECKED(do_drop, args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010110
ager@chromium.org357bf652010-04-12 11:30:10 +000010111 return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010112}
10113
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010114// Compares 2 strings line-by-line and returns diff in form of JSArray of
fschneider@chromium.org013f3e12010-04-26 13:27:52 +000010115// triplets (pos1, pos1_end, pos2_end) describing list of diff chunks.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010116static Object* Runtime_LiveEditCompareStringsLinewise(Arguments args) {
10117 ASSERT(args.length() == 2);
10118 HandleScope scope;
10119 CONVERT_ARG_CHECKED(String, s1, 0);
10120 CONVERT_ARG_CHECKED(String, s2, 1);
10121
10122 return *LiveEdit::CompareStringsLinewise(s1, s2);
10123}
10124
10125
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010126
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010127// A testing entry. Returns statement position which is the closest to
10128// source_position.
10129static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
10130 ASSERT(args.length() == 2);
10131 HandleScope scope;
10132 CONVERT_ARG_CHECKED(JSFunction, function, 0);
10133 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
10134
10135 Handle<Code> code(function->code());
10136
10137 RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
10138 int closest_pc = 0;
10139 int distance = kMaxInt;
10140 while (!it.done()) {
10141 int statement_position = static_cast<int>(it.rinfo()->data());
10142 // Check if this break point is closer that what was previously found.
10143 if (source_position <= statement_position &&
10144 statement_position - source_position < distance) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +000010145 closest_pc =
10146 static_cast<int>(it.rinfo()->pc() - code->instruction_start());
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010147 distance = statement_position - source_position;
10148 // Check whether we can't get any closer.
10149 if (distance == 0) break;
10150 }
10151 it.next();
10152 }
10153
10154 return Smi::FromInt(closest_pc);
10155}
10156
10157
ager@chromium.org357bf652010-04-12 11:30:10 +000010158// Calls specified function with or without entering the debugger.
10159// This is used in unit tests to run code as if debugger is entered or simply
10160// to have a stack with C++ frame in the middle.
10161static Object* Runtime_ExecuteInDebugContext(Arguments args) {
10162 ASSERT(args.length() == 2);
10163 HandleScope scope;
10164 CONVERT_ARG_CHECKED(JSFunction, function, 0);
10165 CONVERT_BOOLEAN_CHECKED(without_debugger, args[1]);
10166
10167 Handle<Object> result;
10168 bool pending_exception;
10169 {
10170 if (without_debugger) {
10171 result = Execution::Call(function, Top::global(), 0, NULL,
10172 &pending_exception);
10173 } else {
10174 EnterDebugger enter_debugger;
10175 result = Execution::Call(function, Top::global(), 0, NULL,
10176 &pending_exception);
10177 }
10178 }
10179 if (!pending_exception) {
10180 return *result;
10181 } else {
10182 return Failure::Exception();
10183 }
10184}
10185
10186
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010187#endif // ENABLE_DEBUGGER_SUPPORT
10188
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010189#ifdef ENABLE_LOGGING_AND_PROFILING
10190
10191static Object* Runtime_ProfilerResume(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::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010198 return Heap::undefined_value();
10199}
10200
10201
10202static Object* Runtime_ProfilerPause(Arguments args) {
10203 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010204 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010205
10206 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010207 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10208 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010209 return Heap::undefined_value();
10210}
10211
10212#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010213
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010214// Finds the script object from the script data. NOTE: This operation uses
10215// heap traversal to find the function generated for the source position
10216// for the requested break point. For lazily compiled functions several heap
10217// traversals might be required rendering this operation as a rather slow
10218// operation. However for setting break points which is normally done through
10219// some kind of user interaction the performance is not crucial.
10220static Handle<Object> Runtime_GetScriptFromScriptName(
10221 Handle<String> script_name) {
10222 // Scan the heap for Script objects to find the script with the requested
10223 // script data.
10224 Handle<Script> script;
10225 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010226 HeapObject* obj = NULL;
10227 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010228 // If a script is found check if it has the script data requested.
10229 if (obj->IsScript()) {
10230 if (Script::cast(obj)->name()->IsString()) {
10231 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
10232 script = Handle<Script>(Script::cast(obj));
10233 }
10234 }
10235 }
10236 }
10237
10238 // If no script with the requested script data is found return undefined.
10239 if (script.is_null()) return Factory::undefined_value();
10240
10241 // Return the script found.
10242 return GetScriptWrapper(script);
10243}
10244
10245
10246// Get the script object from script data. NOTE: Regarding performance
10247// see the NOTE for GetScriptFromScriptData.
10248// args[0]: script data for the script to find the source for
10249static Object* Runtime_GetScript(Arguments args) {
10250 HandleScope scope;
10251
10252 ASSERT(args.length() == 1);
10253
10254 CONVERT_CHECKED(String, script_name, args[0]);
10255
10256 // Find the requested script.
10257 Handle<Object> result =
10258 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
10259 return *result;
10260}
10261
10262
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010263// Determines whether the given stack frame should be displayed in
10264// a stack trace. The caller is the error constructor that asked
10265// for the stack trace to be collected. The first time a construct
10266// call to this function is encountered it is skipped. The seen_caller
10267// in/out parameter is used to remember if the caller has been seen
10268// yet.
10269static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
10270 bool* seen_caller) {
10271 // Only display JS frames.
10272 if (!raw_frame->is_java_script())
10273 return false;
10274 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
10275 Object* raw_fun = frame->function();
10276 // Not sure when this can happen but skip it just in case.
10277 if (!raw_fun->IsJSFunction())
10278 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010279 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010280 *seen_caller = true;
10281 return false;
10282 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010283 // Skip all frames until we've seen the caller. Also, skip the most
10284 // obvious builtin calls. Some builtin calls (such as Number.ADD
10285 // which is invoked using 'call') are very difficult to recognize
10286 // so we're leaving them in for now.
10287 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010288}
10289
10290
10291// Collect the raw data for a stack trace. Returns an array of three
10292// element segments each containing a receiver, function and native
10293// code offset.
10294static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010295 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010296 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010297 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
10298
10299 HandleScope scope;
10300
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000010301 limit = Max(limit, 0); // Ensure that limit is not negative.
10302 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010303 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010304
10305 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010306 // If the caller parameter is a function we skip frames until we're
10307 // under it before starting to collect.
10308 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010309 int cursor = 0;
10310 int frames_seen = 0;
10311 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010312 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010313 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010314 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010315 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010316 Object* recv = frame->receiver();
10317 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010318 Address pc = frame->pc();
10319 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010320 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010321 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010322 if (cursor + 2 < elements->length()) {
10323 elements->set(cursor++, recv);
10324 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010325 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010326 } else {
10327 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010328 Handle<Object> recv_handle(recv);
10329 Handle<Object> fun_handle(fun);
10330 SetElement(result, cursor++, recv_handle);
10331 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010332 SetElement(result, cursor++, Handle<Smi>(offset));
10333 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010334 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010335 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010336 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010337
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010338 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010339 return *result;
10340}
10341
10342
ager@chromium.org3811b432009-10-28 14:53:37 +000010343// Returns V8 version as a string.
10344static Object* Runtime_GetV8Version(Arguments args) {
10345 ASSERT_EQ(args.length(), 0);
10346
10347 NoHandleAllocation ha;
10348
10349 const char* version_string = v8::V8::GetVersion();
10350
10351 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
10352}
10353
10354
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010355static Object* Runtime_Abort(Arguments args) {
10356 ASSERT(args.length() == 2);
10357 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
10358 Smi::cast(args[1])->value());
10359 Top::PrintStack();
10360 OS::Abort();
10361 UNREACHABLE();
10362 return NULL;
10363}
10364
10365
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010366static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
10367 ASSERT(args.length() == 0);
10368 HandleScope::DeleteExtensions();
10369 return Heap::undefined_value();
10370}
10371
10372
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010373static Object* CacheMiss(FixedArray* cache_obj, int index, Object* key_obj) {
10374 ASSERT(index % 2 == 0); // index of the key
10375 ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
10376 ASSERT(index < cache_obj->length());
10377
10378 HandleScope scope;
10379
10380 Handle<FixedArray> cache(cache_obj);
10381 Handle<Object> key(key_obj);
10382 Handle<JSFunction> factory(JSFunction::cast(
10383 cache->get(JSFunctionResultCache::kFactoryIndex)));
10384 // TODO(antonm): consider passing a receiver when constructing a cache.
10385 Handle<Object> receiver(Top::global_context()->global());
10386
10387 Handle<Object> value;
10388 {
10389 // This handle is nor shared, nor used later, so it's safe.
10390 Object** argv[] = { key.location() };
10391 bool pending_exception = false;
10392 value = Execution::Call(factory,
10393 receiver,
10394 1,
10395 argv,
10396 &pending_exception);
10397 if (pending_exception) return Failure::Exception();
10398 }
10399
10400 cache->set(index, *key);
10401 cache->set(index + 1, *value);
10402 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(index));
10403
10404 return *value;
10405}
10406
10407
10408static Object* Runtime_GetFromCache(Arguments args) {
10409 // This is only called from codegen, so checks might be more lax.
10410 CONVERT_CHECKED(FixedArray, cache, args[0]);
10411 Object* key = args[1];
10412
10413 const int finger_index =
10414 Smi::cast(cache->get(JSFunctionResultCache::kFingerIndex))->value();
10415
10416 Object* o = cache->get(finger_index);
10417 if (o == key) {
10418 // The fastest case: hit the same place again.
10419 return cache->get(finger_index + 1);
10420 }
10421
10422 for (int i = finger_index - 2;
10423 i >= JSFunctionResultCache::kEntriesIndex;
10424 i -= 2) {
10425 o = cache->get(i);
10426 if (o == key) {
10427 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10428 return cache->get(i + 1);
10429 }
10430 }
10431
10432 const int size =
10433 Smi::cast(cache->get(JSFunctionResultCache::kCacheSizeIndex))->value();
10434 ASSERT(size <= cache->length());
10435
10436 for (int i = size - 2; i > finger_index; i -= 2) {
10437 o = cache->get(i);
10438 if (o == key) {
10439 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10440 return cache->get(i + 1);
10441 }
10442 }
10443
10444 // Cache miss. If we have spare room, put new data into it, otherwise
10445 // evict post finger entry which must be least recently used.
10446 if (size < cache->length()) {
10447 cache->set(JSFunctionResultCache::kCacheSizeIndex, Smi::FromInt(size + 2));
10448 return CacheMiss(cache, size, key);
10449 } else {
antonm@chromium.org397e23c2010-04-21 12:00:05 +000010450 int target_index = finger_index + JSFunctionResultCache::kEntrySize;
10451 if (target_index == cache->length()) {
10452 target_index = JSFunctionResultCache::kEntriesIndex;
10453 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010454 return CacheMiss(cache, target_index, key);
10455 }
10456}
10457
kasper.lund44510672008-07-25 07:37:58 +000010458#ifdef DEBUG
10459// ListNatives is ONLY used by the fuzz-natives.js in debug mode
10460// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010461static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +000010462 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010463 HandleScope scope;
10464 Handle<JSArray> result = Factory::NewJSArray(0);
10465 int index = 0;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010466 bool inline_runtime_functions = false;
ager@chromium.orga1645e22009-09-09 19:27:10 +000010467#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010468 { \
10469 HandleScope inner; \
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010470 Handle<String> name; \
10471 /* Inline runtime functions have an underscore in front of the name. */ \
10472 if (inline_runtime_functions) { \
10473 name = Factory::NewStringFromAscii( \
10474 Vector<const char>("_" #Name, StrLength("_" #Name))); \
10475 } else { \
10476 name = Factory::NewStringFromAscii( \
10477 Vector<const char>(#Name, StrLength(#Name))); \
10478 } \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010479 Handle<JSArray> pair = Factory::NewJSArray(0); \
10480 SetElement(pair, 0, name); \
10481 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
10482 SetElement(result, index++, pair); \
10483 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010484 inline_runtime_functions = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010485 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010486 inline_runtime_functions = true;
10487 INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010488#undef ADD_ENTRY
10489 return *result;
10490}
kasper.lund44510672008-07-25 07:37:58 +000010491#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010492
10493
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010494static Object* Runtime_Log(Arguments args) {
10495 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +000010496 CONVERT_CHECKED(String, format, args[0]);
10497 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010498 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010499 Logger::LogRuntime(chars, elms);
10500 return Heap::undefined_value();
10501}
10502
10503
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010504static Object* Runtime_IS_VAR(Arguments args) {
10505 UNREACHABLE(); // implemented as macro in the parser
10506 return NULL;
10507}
10508
10509
10510// ----------------------------------------------------------------------------
10511// Implementation of Runtime
10512
ager@chromium.orga1645e22009-09-09 19:27:10 +000010513#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010514 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +000010515 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010516
10517static Runtime::Function Runtime_functions[] = {
10518 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010519 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010520};
10521
10522#undef F
10523
10524
10525Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
10526 ASSERT(0 <= fid && fid < kNofFunctions);
10527 return &Runtime_functions[fid];
10528}
10529
10530
10531Runtime::Function* Runtime::FunctionForName(const char* name) {
10532 for (Function* f = Runtime_functions; f->name != NULL; f++) {
10533 if (strcmp(f->name, name) == 0) {
10534 return f;
10535 }
10536 }
10537 return NULL;
10538}
10539
10540
10541void Runtime::PerformGC(Object* result) {
10542 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010543 if (failure->IsRetryAfterGC()) {
10544 // Try to do a garbage collection; ignore it if it fails. The C
10545 // entry stub will throw an out-of-memory exception in that case.
10546 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
10547 } else {
10548 // Handle last resort GC and make sure to allow future allocations
10549 // to grow the heap without causing GCs (if possible).
10550 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +000010551 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010552 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010553}
10554
10555
10556} } // namespace v8::internal