blob: fa881eb203bc2a1e1d6693b403d6d433efcbf2a9 [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.orgb5737492010-07-15 09:29:43 +00006872 int length = function->shared()->scope_info()->NumberOfContextSlots();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006873 Object* result = Heap::AllocateFunctionContext(length, function);
6874 if (result->IsFailure()) return result;
6875
6876 Top::set_context(Context::cast(result));
6877
kasper.lund7276f142008-07-30 08:49:36 +00006878 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006879}
6880
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006881static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006882 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006883 Object* js_object = object;
6884 if (!js_object->IsJSObject()) {
6885 js_object = js_object->ToObject();
6886 if (js_object->IsFailure()) {
6887 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006888 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006889 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006890 Handle<Object> result =
6891 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
6892 return Top::Throw(*result);
6893 }
6894 }
6895
6896 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006897 Heap::AllocateWithContext(Top::context(),
6898 JSObject::cast(js_object),
6899 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006900 if (result->IsFailure()) return result;
6901
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006902 Context* context = Context::cast(result);
6903 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006904
kasper.lund7276f142008-07-30 08:49:36 +00006905 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006906}
6907
6908
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006909static Object* Runtime_PushContext(Arguments args) {
6910 NoHandleAllocation ha;
6911 ASSERT(args.length() == 1);
6912 return PushContextHelper(args[0], false);
6913}
6914
6915
6916static Object* Runtime_PushCatchContext(Arguments args) {
6917 NoHandleAllocation ha;
6918 ASSERT(args.length() == 1);
6919 return PushContextHelper(args[0], true);
6920}
6921
6922
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006923static Object* Runtime_LookupContext(Arguments args) {
6924 HandleScope scope;
6925 ASSERT(args.length() == 2);
6926
6927 CONVERT_ARG_CHECKED(Context, context, 0);
6928 CONVERT_ARG_CHECKED(String, name, 1);
6929
6930 int index;
6931 PropertyAttributes attributes;
6932 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006933 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006934 context->Lookup(name, flags, &index, &attributes);
6935
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006936 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006937 ASSERT(holder->IsJSObject());
6938 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006939 }
6940
6941 // No intermediate context found. Use global object by default.
6942 return Top::context()->global();
6943}
6944
6945
ager@chromium.orga1645e22009-09-09 19:27:10 +00006946// A mechanism to return a pair of Object pointers in registers (if possible).
6947// How this is achieved is calling convention-dependent.
6948// All currently supported x86 compiles uses calling conventions that are cdecl
6949// variants where a 64-bit value is returned in two 32-bit registers
6950// (edx:eax on ia32, r1:r0 on ARM).
6951// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
6952// In Win64 calling convention, a struct of two pointers is returned in memory,
6953// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006954#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006955struct ObjectPair {
6956 Object* x;
6957 Object* y;
6958};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006959
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006960static inline ObjectPair MakePair(Object* x, Object* y) {
6961 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006962 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
6963 // In Win64 they are assigned to a hidden first argument.
6964 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006965}
6966#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006967typedef uint64_t ObjectPair;
6968static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006969 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006970 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006971}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006972#endif
6973
6974
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006975static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006976 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
6977 USE(attributes);
6978 return x->IsTheHole() ? Heap::undefined_value() : x;
6979}
6980
6981
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006982static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
6983 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006984 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006985 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006986 JSFunction* context_extension_function =
6987 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006988 // If the holder isn't a context extension object, we just return it
6989 // as the receiver. This allows arguments objects to be used as
6990 // receivers, but only if they are put in the context scope chain
6991 // explicitly via a with-statement.
6992 Object* constructor = holder->map()->constructor();
6993 if (constructor != context_extension_function) return holder;
6994 // Fall back to using the global object as the receiver if the
6995 // property turns out to be a local variable allocated in a context
6996 // extension object - introduced via eval.
6997 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006998}
6999
7000
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007001static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007002 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00007003 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007004
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007005 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00007006 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007007 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007008 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007009 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007010
7011 int index;
7012 PropertyAttributes attributes;
7013 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007014 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007015 context->Lookup(name, flags, &index, &attributes);
7016
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007017 // If the index is non-negative, the slot has been found in a local
7018 // variable or a parameter. Read it from the context object or the
7019 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007020 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007021 // If the "property" we were looking for is a local variable or an
7022 // argument in a context, the receiver is the global object; see
7023 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
7024 JSObject* receiver = Top::context()->global()->global_receiver();
7025 Object* value = (holder->IsContext())
7026 ? Context::cast(*holder)->get(index)
7027 : JSObject::cast(*holder)->GetElement(index);
7028 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007029 }
7030
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007031 // If the holder is found, we read the property from it.
7032 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007033 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007034 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007035 JSObject* receiver;
7036 if (object->IsGlobalObject()) {
7037 receiver = GlobalObject::cast(object)->global_receiver();
7038 } else if (context->is_exception_holder(*holder)) {
7039 receiver = Top::context()->global()->global_receiver();
7040 } else {
7041 receiver = ComputeReceiverForNonGlobal(object);
7042 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007043 // No need to unhole the value here. This is taken care of by the
7044 // GetProperty function.
7045 Object* value = object->GetProperty(*name);
7046 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007047 }
7048
7049 if (throw_error) {
7050 // The property doesn't exist - throw exception.
7051 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007052 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007053 return MakePair(Top::Throw(*reference_error), NULL);
7054 } else {
7055 // The property doesn't exist - return undefined
7056 return MakePair(Heap::undefined_value(), Heap::undefined_value());
7057 }
7058}
7059
7060
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007061static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007062 return LoadContextSlotHelper(args, true);
7063}
7064
7065
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007066static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007067 return LoadContextSlotHelper(args, false);
7068}
7069
7070
7071static Object* Runtime_StoreContextSlot(Arguments args) {
7072 HandleScope scope;
7073 ASSERT(args.length() == 3);
7074
7075 Handle<Object> value(args[0]);
7076 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007077 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007078
7079 int index;
7080 PropertyAttributes attributes;
7081 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007082 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007083 context->Lookup(name, flags, &index, &attributes);
7084
7085 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007086 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007087 // Ignore if read_only variable.
7088 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007089 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007090 }
7091 } else {
7092 ASSERT((attributes & READ_ONLY) == 0);
7093 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007094 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007095 USE(result);
7096 ASSERT(!result->IsFailure());
7097 }
7098 return *value;
7099 }
7100
7101 // Slow case: The property is not in a FixedArray context.
7102 // It is either in an JSObject extension context or it was not found.
7103 Handle<JSObject> context_ext;
7104
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007105 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007106 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007107 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007108 } else {
7109 // The property was not found. It needs to be stored in the global context.
7110 ASSERT(attributes == ABSENT);
7111 attributes = NONE;
7112 context_ext = Handle<JSObject>(Top::context()->global());
7113 }
7114
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007115 // Set the property, but ignore if read_only variable on the context
7116 // extension object itself.
7117 if ((attributes & READ_ONLY) == 0 ||
7118 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007119 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
7120 if (set.is_null()) {
7121 // Failure::Exception is converted to a null handle in the
7122 // handle-based methods such as SetProperty. We therefore need
7123 // to convert null handles back to exceptions.
7124 ASSERT(Top::has_pending_exception());
7125 return Failure::Exception();
7126 }
7127 }
7128 return *value;
7129}
7130
7131
7132static Object* Runtime_Throw(Arguments args) {
7133 HandleScope scope;
7134 ASSERT(args.length() == 1);
7135
7136 return Top::Throw(args[0]);
7137}
7138
7139
7140static Object* Runtime_ReThrow(Arguments args) {
7141 HandleScope scope;
7142 ASSERT(args.length() == 1);
7143
7144 return Top::ReThrow(args[0]);
7145}
7146
7147
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007148static Object* Runtime_PromoteScheduledException(Arguments args) {
7149 ASSERT_EQ(0, args.length());
7150 return Top::PromoteScheduledException();
7151}
7152
7153
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007154static Object* Runtime_ThrowReferenceError(Arguments args) {
7155 HandleScope scope;
7156 ASSERT(args.length() == 1);
7157
7158 Handle<Object> name(args[0]);
7159 Handle<Object> reference_error =
7160 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
7161 return Top::Throw(*reference_error);
7162}
7163
7164
7165static Object* Runtime_StackOverflow(Arguments args) {
7166 NoHandleAllocation na;
7167 return Top::StackOverflow();
7168}
7169
7170
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007171static Object* Runtime_StackGuard(Arguments args) {
7172 ASSERT(args.length() == 1);
7173
7174 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007175 if (StackGuard::IsStackOverflow()) {
7176 return Runtime_StackOverflow(args);
7177 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007178
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007179 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007180}
7181
7182
7183// NOTE: These PrintXXX functions are defined for all builds (not just
7184// DEBUG builds) because we may want to be able to trace function
7185// calls in all modes.
7186static void PrintString(String* str) {
7187 // not uncommon to have empty strings
7188 if (str->length() > 0) {
7189 SmartPointer<char> s =
7190 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
7191 PrintF("%s", *s);
7192 }
7193}
7194
7195
7196static void PrintObject(Object* obj) {
7197 if (obj->IsSmi()) {
7198 PrintF("%d", Smi::cast(obj)->value());
7199 } else if (obj->IsString() || obj->IsSymbol()) {
7200 PrintString(String::cast(obj));
7201 } else if (obj->IsNumber()) {
7202 PrintF("%g", obj->Number());
7203 } else if (obj->IsFailure()) {
7204 PrintF("<failure>");
7205 } else if (obj->IsUndefined()) {
7206 PrintF("<undefined>");
7207 } else if (obj->IsNull()) {
7208 PrintF("<null>");
7209 } else if (obj->IsTrue()) {
7210 PrintF("<true>");
7211 } else if (obj->IsFalse()) {
7212 PrintF("<false>");
7213 } else {
7214 PrintF("%p", obj);
7215 }
7216}
7217
7218
7219static int StackSize() {
7220 int n = 0;
7221 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
7222 return n;
7223}
7224
7225
7226static void PrintTransition(Object* result) {
7227 // indentation
7228 { const int nmax = 80;
7229 int n = StackSize();
7230 if (n <= nmax)
7231 PrintF("%4d:%*s", n, n, "");
7232 else
7233 PrintF("%4d:%*s", n, nmax, "...");
7234 }
7235
7236 if (result == NULL) {
7237 // constructor calls
7238 JavaScriptFrameIterator it;
7239 JavaScriptFrame* frame = it.frame();
7240 if (frame->IsConstructor()) PrintF("new ");
7241 // function name
7242 Object* fun = frame->function();
7243 if (fun->IsJSFunction()) {
7244 PrintObject(JSFunction::cast(fun)->shared()->name());
7245 } else {
7246 PrintObject(fun);
7247 }
7248 // function arguments
7249 // (we are intentionally only printing the actually
7250 // supplied parameters, not all parameters required)
7251 PrintF("(this=");
7252 PrintObject(frame->receiver());
7253 const int length = frame->GetProvidedParametersCount();
7254 for (int i = 0; i < length; i++) {
7255 PrintF(", ");
7256 PrintObject(frame->GetParameter(i));
7257 }
7258 PrintF(") {\n");
7259
7260 } else {
7261 // function result
7262 PrintF("} -> ");
7263 PrintObject(result);
7264 PrintF("\n");
7265 }
7266}
7267
7268
7269static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007270 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007271 NoHandleAllocation ha;
7272 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007273 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007274}
7275
7276
7277static Object* Runtime_TraceExit(Arguments args) {
7278 NoHandleAllocation ha;
7279 PrintTransition(args[0]);
7280 return args[0]; // return TOS
7281}
7282
7283
7284static Object* Runtime_DebugPrint(Arguments args) {
7285 NoHandleAllocation ha;
7286 ASSERT(args.length() == 1);
7287
7288#ifdef DEBUG
7289 if (args[0]->IsString()) {
7290 // If we have a string, assume it's a code "marker"
7291 // and print some interesting cpu debugging info.
7292 JavaScriptFrameIterator it;
7293 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007294 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
7295 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007296 } else {
7297 PrintF("DebugPrint: ");
7298 }
7299 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007300 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007301 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007302 HeapObject::cast(args[0])->map()->Print();
7303 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007304#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007305 // ShortPrint is available in release mode. Print is not.
7306 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007307#endif
7308 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00007309 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007310
7311 return args[0]; // return TOS
7312}
7313
7314
7315static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007316 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007317 NoHandleAllocation ha;
7318 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007319 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007320}
7321
7322
mads.s.ager31e71382008-08-13 09:32:07 +00007323static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007324 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007325 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007326
7327 // According to ECMA-262, section 15.9.1, page 117, the precision of
7328 // the number in a Date object representing a particular instant in
7329 // time is milliseconds. Therefore, we floor the result of getting
7330 // the OS time.
7331 double millis = floor(OS::TimeCurrentMillis());
7332 return Heap::NumberFromDouble(millis);
7333}
7334
7335
7336static Object* Runtime_DateParseString(Arguments args) {
7337 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007338 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007339
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007340 CONVERT_ARG_CHECKED(String, str, 0);
7341 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007342
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007343 CONVERT_ARG_CHECKED(JSArray, output, 1);
7344 RUNTIME_ASSERT(output->HasFastElements());
7345
7346 AssertNoAllocation no_allocation;
7347
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007348 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007349 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
7350 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007351 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007352 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007353 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007354 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007355 result = DateParser::Parse(str->ToUC16Vector(), output_array);
7356 }
7357
7358 if (result) {
7359 return *output;
7360 } else {
7361 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007362 }
7363}
7364
7365
7366static Object* Runtime_DateLocalTimezone(Arguments args) {
7367 NoHandleAllocation ha;
7368 ASSERT(args.length() == 1);
7369
7370 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007371 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007372 return Heap::AllocateStringFromUtf8(CStrVector(zone));
7373}
7374
7375
7376static Object* Runtime_DateLocalTimeOffset(Arguments args) {
7377 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007378 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007379
7380 return Heap::NumberFromDouble(OS::LocalTimeOffset());
7381}
7382
7383
7384static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
7385 NoHandleAllocation ha;
7386 ASSERT(args.length() == 1);
7387
7388 CONVERT_DOUBLE_CHECKED(x, args[0]);
7389 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
7390}
7391
7392
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007393static Object* Runtime_GlobalReceiver(Arguments args) {
7394 ASSERT(args.length() == 1);
7395 Object* global = args[0];
7396 if (!global->IsJSGlobalObject()) return Heap::null_value();
7397 return JSGlobalObject::cast(global)->global_receiver();
7398}
7399
7400
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007401static Object* Runtime_CompileString(Arguments args) {
7402 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007403 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007404 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007405 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007406
ager@chromium.org381abbb2009-02-25 13:23:22 +00007407 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007408 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007409 Compiler::ValidationState validate = (is_json->IsTrue())
7410 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007411 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
7412 context,
7413 true,
7414 validate);
7415 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007416 Handle<JSFunction> fun =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007417 Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007418 return *fun;
7419}
7420
7421
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007422static ObjectPair CompileGlobalEval(Handle<String> source,
7423 Handle<Object> receiver) {
7424 // Deal with a normal eval call with a string argument. Compile it
7425 // and return the compiled function bound in the local context.
7426 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
7427 source,
7428 Handle<Context>(Top::context()),
7429 Top::context()->IsGlobalContext(),
7430 Compiler::DONT_VALIDATE_JSON);
7431 if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
7432 Handle<JSFunction> compiled = Factory::NewFunctionFromSharedFunctionInfo(
7433 shared,
7434 Handle<Context>(Top::context()),
7435 NOT_TENURED);
7436 return MakePair(*compiled, *receiver);
7437}
7438
7439
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007440static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
7441 ASSERT(args.length() == 3);
7442 if (!args[0]->IsJSFunction()) {
7443 return MakePair(Top::ThrowIllegalOperation(), NULL);
7444 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007445
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007446 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007447 Handle<JSFunction> callee = args.at<JSFunction>(0);
7448 Handle<Object> receiver; // Will be overwritten.
7449
7450 // Compute the calling context.
7451 Handle<Context> context = Handle<Context>(Top::context());
7452#ifdef DEBUG
7453 // Make sure Top::context() agrees with the old code that traversed
7454 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007455 StackFrameLocator locator;
7456 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007457 ASSERT(Context::cast(frame->context()) == *context);
7458#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007459
7460 // Find where the 'eval' symbol is bound. It is unaliased only if
7461 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007462 int index = -1;
7463 PropertyAttributes attributes = ABSENT;
7464 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007465 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
7466 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007467 // Stop search when eval is found or when the global context is
7468 // reached.
7469 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007470 if (context->is_function_context()) {
7471 context = Handle<Context>(Context::cast(context->closure()->context()));
7472 } else {
7473 context = Handle<Context>(context->previous());
7474 }
7475 }
7476
iposva@chromium.org245aa852009-02-10 00:49:54 +00007477 // If eval could not be resolved, it has been deleted and we need to
7478 // throw a reference error.
7479 if (attributes == ABSENT) {
7480 Handle<Object> name = Factory::eval_symbol();
7481 Handle<Object> reference_error =
7482 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007483 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007484 }
7485
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007486 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007487 // 'eval' is not bound in the global context. Just call the function
7488 // with the given arguments. This is not necessarily the global eval.
7489 if (receiver->IsContext()) {
7490 context = Handle<Context>::cast(receiver);
7491 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007492 } else if (receiver->IsJSContextExtensionObject()) {
7493 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007494 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007495 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007496 }
7497
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007498 // 'eval' is bound in the global context, but it may have been overwritten.
7499 // Compare it to the builtin 'GlobalEval' function to make sure.
7500 if (*callee != Top::global_context()->global_eval_fun() ||
7501 !args[1]->IsString()) {
7502 return MakePair(*callee, Top::context()->global()->global_receiver());
7503 }
7504
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007505 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
7506}
7507
7508
7509static ObjectPair Runtime_ResolvePossiblyDirectEvalNoLookup(Arguments args) {
7510 ASSERT(args.length() == 3);
7511 if (!args[0]->IsJSFunction()) {
7512 return MakePair(Top::ThrowIllegalOperation(), NULL);
7513 }
7514
7515 HandleScope scope;
7516 Handle<JSFunction> callee = args.at<JSFunction>(0);
7517
7518 // 'eval' is bound in the global context, but it may have been overwritten.
7519 // Compare it to the builtin 'GlobalEval' function to make sure.
7520 if (*callee != Top::global_context()->global_eval_fun() ||
7521 !args[1]->IsString()) {
7522 return MakePair(*callee, Top::context()->global()->global_receiver());
7523 }
7524
7525 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007526}
7527
7528
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007529static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
7530 // This utility adjusts the property attributes for newly created Function
7531 // object ("new Function(...)") by changing the map.
7532 // All it does is changing the prototype property to enumerable
7533 // as specified in ECMA262, 15.3.5.2.
7534 HandleScope scope;
7535 ASSERT(args.length() == 1);
7536 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7537 ASSERT(func->map()->instance_type() ==
7538 Top::function_instance_map()->instance_type());
7539 ASSERT(func->map()->instance_size() ==
7540 Top::function_instance_map()->instance_size());
7541 func->set_map(*Top::function_instance_map());
7542 return *func;
7543}
7544
7545
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007546// Push an array unto an array of arrays if it is not already in the
7547// array. Returns true if the element was pushed on the stack and
7548// false otherwise.
7549static Object* Runtime_PushIfAbsent(Arguments args) {
7550 ASSERT(args.length() == 2);
7551 CONVERT_CHECKED(JSArray, array, args[0]);
7552 CONVERT_CHECKED(JSArray, element, args[1]);
7553 RUNTIME_ASSERT(array->HasFastElements());
7554 int length = Smi::cast(array->length())->value();
7555 FixedArray* elements = FixedArray::cast(array->elements());
7556 for (int i = 0; i < length; i++) {
7557 if (elements->get(i) == element) return Heap::false_value();
7558 }
7559 Object* obj = array->SetFastElement(length, element);
7560 if (obj->IsFailure()) return obj;
7561 return Heap::true_value();
7562}
7563
7564
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007565/**
7566 * A simple visitor visits every element of Array's.
7567 * The backend storage can be a fixed array for fast elements case,
7568 * or a dictionary for sparse array. Since Dictionary is a subtype
7569 * of FixedArray, the class can be used by both fast and slow cases.
7570 * The second parameter of the constructor, fast_elements, specifies
7571 * whether the storage is a FixedArray or Dictionary.
7572 *
7573 * An index limit is used to deal with the situation that a result array
7574 * length overflows 32-bit non-negative integer.
7575 */
7576class ArrayConcatVisitor {
7577 public:
7578 ArrayConcatVisitor(Handle<FixedArray> storage,
7579 uint32_t index_limit,
7580 bool fast_elements) :
7581 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007582 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007583
7584 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007585 if (i >= index_limit_ - index_offset_) return;
7586 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007587
7588 if (fast_elements_) {
7589 ASSERT(index < static_cast<uint32_t>(storage_->length()));
7590 storage_->set(index, *elm);
7591
7592 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007593 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
7594 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007595 Factory::DictionaryAtNumberPut(dict, index, elm);
7596 if (!result.is_identical_to(dict))
7597 storage_ = result;
7598 }
7599 }
7600
7601 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007602 if (index_limit_ - index_offset_ < delta) {
7603 index_offset_ = index_limit_;
7604 } else {
7605 index_offset_ += delta;
7606 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007607 }
7608
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007609 Handle<FixedArray> storage() { return storage_; }
7610
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007611 private:
7612 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007613 // Limit on the accepted indices. Elements with indices larger than the
7614 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007615 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007616 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007617 uint32_t index_offset_;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007618 const bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007619};
7620
7621
ager@chromium.org3811b432009-10-28 14:53:37 +00007622template<class ExternalArrayClass, class ElementType>
7623static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
7624 bool elements_are_ints,
7625 bool elements_are_guaranteed_smis,
7626 uint32_t range,
7627 ArrayConcatVisitor* visitor) {
7628 Handle<ExternalArrayClass> array(
7629 ExternalArrayClass::cast(receiver->elements()));
7630 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
7631
7632 if (visitor != NULL) {
7633 if (elements_are_ints) {
7634 if (elements_are_guaranteed_smis) {
7635 for (uint32_t j = 0; j < len; j++) {
7636 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
7637 visitor->visit(j, e);
7638 }
7639 } else {
7640 for (uint32_t j = 0; j < len; j++) {
7641 int64_t val = static_cast<int64_t>(array->get(j));
7642 if (Smi::IsValid(static_cast<intptr_t>(val))) {
7643 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
7644 visitor->visit(j, e);
7645 } else {
7646 Handle<Object> e(
7647 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
7648 visitor->visit(j, e);
7649 }
7650 }
7651 }
7652 } else {
7653 for (uint32_t j = 0; j < len; j++) {
7654 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
7655 visitor->visit(j, e);
7656 }
7657 }
7658 }
7659
7660 return len;
7661}
7662
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007663/**
7664 * A helper function that visits elements of a JSObject. Only elements
7665 * whose index between 0 and range (exclusive) are visited.
7666 *
7667 * If the third parameter, visitor, is not NULL, the visitor is called
7668 * with parameters, 'visitor_index_offset + element index' and the element.
7669 *
7670 * It returns the number of visisted elements.
7671 */
7672static uint32_t IterateElements(Handle<JSObject> receiver,
7673 uint32_t range,
7674 ArrayConcatVisitor* visitor) {
7675 uint32_t num_of_elements = 0;
7676
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007677 switch (receiver->GetElementsKind()) {
7678 case JSObject::FAST_ELEMENTS: {
7679 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
7680 uint32_t len = elements->length();
7681 if (range < len) {
7682 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007683 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007684
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007685 for (uint32_t j = 0; j < len; j++) {
7686 Handle<Object> e(elements->get(j));
7687 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007688 num_of_elements++;
7689 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007690 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007691 }
7692 }
7693 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007694 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007695 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007696 case JSObject::PIXEL_ELEMENTS: {
7697 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
7698 uint32_t len = pixels->length();
7699 if (range < len) {
7700 len = range;
7701 }
7702
7703 for (uint32_t j = 0; j < len; j++) {
7704 num_of_elements++;
7705 if (visitor != NULL) {
7706 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
7707 visitor->visit(j, e);
7708 }
7709 }
7710 break;
7711 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007712 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
7713 num_of_elements =
7714 IterateExternalArrayElements<ExternalByteArray, int8_t>(
7715 receiver, true, true, range, visitor);
7716 break;
7717 }
7718 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7719 num_of_elements =
7720 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
7721 receiver, true, true, range, visitor);
7722 break;
7723 }
7724 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
7725 num_of_elements =
7726 IterateExternalArrayElements<ExternalShortArray, int16_t>(
7727 receiver, true, true, range, visitor);
7728 break;
7729 }
7730 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7731 num_of_elements =
7732 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
7733 receiver, true, true, range, visitor);
7734 break;
7735 }
7736 case JSObject::EXTERNAL_INT_ELEMENTS: {
7737 num_of_elements =
7738 IterateExternalArrayElements<ExternalIntArray, int32_t>(
7739 receiver, true, false, range, visitor);
7740 break;
7741 }
7742 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7743 num_of_elements =
7744 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
7745 receiver, true, false, range, visitor);
7746 break;
7747 }
7748 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
7749 num_of_elements =
7750 IterateExternalArrayElements<ExternalFloatArray, float>(
7751 receiver, false, false, range, visitor);
7752 break;
7753 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007754 case JSObject::DICTIONARY_ELEMENTS: {
7755 Handle<NumberDictionary> dict(receiver->element_dictionary());
7756 uint32_t capacity = dict->Capacity();
7757 for (uint32_t j = 0; j < capacity; j++) {
7758 Handle<Object> k(dict->KeyAt(j));
7759 if (dict->IsKey(*k)) {
7760 ASSERT(k->IsNumber());
7761 uint32_t index = static_cast<uint32_t>(k->Number());
7762 if (index < range) {
7763 num_of_elements++;
7764 if (visitor) {
7765 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
7766 }
7767 }
7768 }
7769 }
7770 break;
7771 }
7772 default:
7773 UNREACHABLE();
7774 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007775 }
7776
7777 return num_of_elements;
7778}
7779
7780
7781/**
7782 * A helper function that visits elements of an Array object, and elements
7783 * on its prototypes.
7784 *
7785 * Elements on prototypes are visited first, and only elements whose indices
7786 * less than Array length are visited.
7787 *
7788 * If a ArrayConcatVisitor object is given, the visitor is called with
7789 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007790 *
7791 * The returned number of elements is an upper bound on the actual number
7792 * of elements added. If the same element occurs in more than one object
7793 * in the array's prototype chain, it will be counted more than once, but
7794 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007795 */
7796static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
7797 ArrayConcatVisitor* visitor) {
7798 uint32_t range = static_cast<uint32_t>(array->length()->Number());
7799 Handle<Object> obj = array;
7800
7801 static const int kEstimatedPrototypes = 3;
7802 List< Handle<JSObject> > objects(kEstimatedPrototypes);
7803
7804 // Visit prototype first. If an element on the prototype is shadowed by
7805 // the inheritor using the same index, the ArrayConcatVisitor visits
7806 // the prototype element before the shadowing element.
7807 // The visitor can simply overwrite the old value by new value using
7808 // the same index. This follows Array::concat semantics.
7809 while (!obj->IsNull()) {
7810 objects.Add(Handle<JSObject>::cast(obj));
7811 obj = Handle<Object>(obj->GetPrototype());
7812 }
7813
7814 uint32_t nof_elements = 0;
7815 for (int i = objects.length() - 1; i >= 0; i--) {
7816 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007817 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007818 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007819
7820 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
7821 nof_elements = JSObject::kMaxElementCount;
7822 } else {
7823 nof_elements += encountered_elements;
7824 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007825 }
7826
7827 return nof_elements;
7828}
7829
7830
7831/**
7832 * A helper function of Runtime_ArrayConcat.
7833 *
7834 * The first argument is an Array of arrays and objects. It is the
7835 * same as the arguments array of Array::concat JS function.
7836 *
7837 * If an argument is an Array object, the function visits array
7838 * elements. If an argument is not an Array object, the function
7839 * visits the object as if it is an one-element array.
7840 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007841 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007842 * non-negative number is used as new length. For example, if one
7843 * array length is 2^32 - 1, second array length is 1, the
7844 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007845 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
7846 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007847 */
7848static uint32_t IterateArguments(Handle<JSArray> arguments,
7849 ArrayConcatVisitor* visitor) {
7850 uint32_t visited_elements = 0;
7851 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7852
7853 for (uint32_t i = 0; i < num_of_args; i++) {
7854 Handle<Object> obj(arguments->GetElement(i));
7855 if (obj->IsJSArray()) {
7856 Handle<JSArray> array = Handle<JSArray>::cast(obj);
7857 uint32_t len = static_cast<uint32_t>(array->length()->Number());
7858 uint32_t nof_elements =
7859 IterateArrayAndPrototypeElements(array, visitor);
7860 // Total elements of array and its prototype chain can be more than
7861 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007862 // the array length number of elements. We use the length as an estimate
7863 // for the actual number of elements added.
7864 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
7865 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
7866 visited_elements = JSArray::kMaxElementCount;
7867 } else {
7868 visited_elements += added_elements;
7869 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007870 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007871 } else {
7872 if (visitor) {
7873 visitor->visit(0, obj);
7874 visitor->increase_index_offset(1);
7875 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007876 if (visited_elements < JSArray::kMaxElementCount) {
7877 visited_elements++;
7878 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007879 }
7880 }
7881 return visited_elements;
7882}
7883
7884
7885/**
7886 * Array::concat implementation.
7887 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007888 * TODO(lrn): Fix non-compliance for very large concatenations and update to
7889 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007890 */
7891static Object* Runtime_ArrayConcat(Arguments args) {
7892 ASSERT(args.length() == 1);
7893 HandleScope handle_scope;
7894
7895 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
7896 Handle<JSArray> arguments(arg_arrays);
7897
7898 // Pass 1: estimate the number of elements of the result
7899 // (it could be more than real numbers if prototype has elements).
7900 uint32_t result_length = 0;
7901 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7902
7903 { AssertNoAllocation nogc;
7904 for (uint32_t i = 0; i < num_of_args; i++) {
7905 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007906 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007907 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007908 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007909 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
7910 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007911 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007912 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007913 if (JSObject::kMaxElementCount - result_length < length_estimate) {
7914 result_length = JSObject::kMaxElementCount;
7915 break;
7916 }
7917 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007918 }
7919 }
7920
7921 // Allocate an empty array, will set length and content later.
7922 Handle<JSArray> result = Factory::NewJSArray(0);
7923
7924 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
7925 // If estimated number of elements is more than half of length, a
7926 // fixed array (fast case) is more time and space-efficient than a
7927 // dictionary.
7928 bool fast_case = (estimate_nof_elements * 2) >= result_length;
7929
7930 Handle<FixedArray> storage;
7931 if (fast_case) {
7932 // The backing storage array must have non-existing elements to
7933 // preserve holes across concat operations.
7934 storage = Factory::NewFixedArrayWithHoles(result_length);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007935 result->set_map(*Factory::GetFastElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007936 } else {
7937 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
7938 uint32_t at_least_space_for = estimate_nof_elements +
7939 (estimate_nof_elements >> 2);
7940 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007941 Factory::NewNumberDictionary(at_least_space_for));
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007942 result->set_map(*Factory::GetSlowElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007943 }
7944
7945 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
7946
7947 ArrayConcatVisitor visitor(storage, result_length, fast_case);
7948
7949 IterateArguments(arguments, &visitor);
7950
7951 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007952 // Please note the storage might have changed in the visitor.
7953 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007954
7955 return *result;
7956}
7957
7958
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007959// This will not allocate (flatten the string), but it may run
7960// very slowly for very deeply nested ConsStrings. For debugging use only.
7961static Object* Runtime_GlobalPrint(Arguments args) {
7962 NoHandleAllocation ha;
7963 ASSERT(args.length() == 1);
7964
7965 CONVERT_CHECKED(String, string, args[0]);
7966 StringInputBuffer buffer(string);
7967 while (buffer.has_more()) {
7968 uint16_t character = buffer.GetNext();
7969 PrintF("%c", character);
7970 }
7971 return string;
7972}
7973
ager@chromium.org5ec48922009-05-05 07:25:34 +00007974// Moves all own elements of an object, that are below a limit, to positions
7975// starting at zero. All undefined values are placed after non-undefined values,
7976// and are followed by non-existing element. Does not change the length
7977// property.
7978// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007979static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007980 ASSERT(args.length() == 2);
7981 CONVERT_CHECKED(JSObject, object, args[0]);
7982 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
7983 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007984}
7985
7986
7987// Move contents of argument 0 (an array) to argument 1 (an array)
7988static Object* Runtime_MoveArrayContents(Arguments args) {
7989 ASSERT(args.length() == 2);
7990 CONVERT_CHECKED(JSArray, from, args[0]);
7991 CONVERT_CHECKED(JSArray, to, args[1]);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007992 HeapObject* new_elements = from->elements();
7993 Object* new_map;
7994 if (new_elements->map() == Heap::fixed_array_map()) {
7995 new_map = to->map()->GetFastElementsMap();
7996 } else {
7997 new_map = to->map()->GetSlowElementsMap();
7998 }
7999 if (new_map->IsFailure()) return new_map;
8000 to->set_map(Map::cast(new_map));
8001 to->set_elements(new_elements);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008002 to->set_length(from->length());
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00008003 Object* obj = from->ResetElements();
8004 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008005 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008006 return to;
8007}
8008
8009
8010// How many elements does this array have?
8011static Object* Runtime_EstimateNumberOfElements(Arguments args) {
8012 ASSERT(args.length() == 1);
8013 CONVERT_CHECKED(JSArray, array, args[0]);
8014 HeapObject* elements = array->elements();
8015 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008016 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008017 } else {
8018 return array->length();
8019 }
8020}
8021
8022
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008023static Object* Runtime_SwapElements(Arguments args) {
8024 HandleScope handle_scope;
8025
8026 ASSERT_EQ(3, args.length());
8027
ager@chromium.orgac091b72010-05-05 07:34:42 +00008028 CONVERT_ARG_CHECKED(JSObject, object, 0);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008029 Handle<Object> key1 = args.at<Object>(1);
8030 Handle<Object> key2 = args.at<Object>(2);
8031
8032 uint32_t index1, index2;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008033 if (!key1->ToArrayIndex(&index1)
8034 || !key2->ToArrayIndex(&index2)) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00008035 return Top::ThrowIllegalOperation();
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008036 }
8037
ager@chromium.orgac091b72010-05-05 07:34:42 +00008038 Handle<JSObject> jsobject = Handle<JSObject>::cast(object);
8039 Handle<Object> tmp1 = GetElement(jsobject, index1);
8040 Handle<Object> tmp2 = GetElement(jsobject, index2);
8041
8042 SetElement(jsobject, index1, tmp2);
8043 SetElement(jsobject, index2, tmp1);
8044
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008045 return Heap::undefined_value();
8046}
8047
8048
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008049// Returns an array that tells you where in the [0, length) interval an array
8050// might have elements. Can either return keys or intervals. Keys can have
8051// gaps in (undefined). Intervals can also span over some undefined keys.
8052static Object* Runtime_GetArrayKeys(Arguments args) {
8053 ASSERT(args.length() == 2);
8054 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00008055 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008056 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008057 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008058 // Create an array and get all the keys into it, then remove all the
8059 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008060 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008061 int keys_length = keys->length();
8062 for (int i = 0; i < keys_length; i++) {
8063 Object* key = keys->get(i);
8064 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008065 if (!key->ToArrayIndex(&index) || index >= length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008066 // Zap invalid keys.
8067 keys->set_undefined(i);
8068 }
8069 }
8070 return *Factory::NewJSArrayWithElements(keys);
8071 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008072 ASSERT(array->HasFastElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008073 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
8074 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008075 single_interval->set(0, Smi::FromInt(-1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008076 uint32_t actual_length =
8077 static_cast<uint32_t>(FixedArray::cast(array->elements())->length());
ager@chromium.org5ec48922009-05-05 07:25:34 +00008078 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008079 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00008080 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008081 single_interval->set(1, *length_object);
8082 return *Factory::NewJSArrayWithElements(single_interval);
8083 }
8084}
8085
8086
8087// DefineAccessor takes an optional final argument which is the
8088// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
8089// to the way accessors are implemented, it is set for both the getter
8090// and setter on the first call to DefineAccessor and ignored on
8091// subsequent calls.
8092static Object* Runtime_DefineAccessor(Arguments args) {
8093 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
8094 // Compute attributes.
8095 PropertyAttributes attributes = NONE;
8096 if (args.length() == 5) {
8097 CONVERT_CHECKED(Smi, attrs, args[4]);
8098 int value = attrs->value();
8099 // Only attribute bits should be set.
8100 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
8101 attributes = static_cast<PropertyAttributes>(value);
8102 }
8103
8104 CONVERT_CHECKED(JSObject, obj, args[0]);
8105 CONVERT_CHECKED(String, name, args[1]);
8106 CONVERT_CHECKED(Smi, flag, args[2]);
8107 CONVERT_CHECKED(JSFunction, fun, args[3]);
8108 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
8109}
8110
8111
8112static Object* Runtime_LookupAccessor(Arguments args) {
8113 ASSERT(args.length() == 3);
8114 CONVERT_CHECKED(JSObject, obj, args[0]);
8115 CONVERT_CHECKED(String, name, args[1]);
8116 CONVERT_CHECKED(Smi, flag, args[2]);
8117 return obj->LookupAccessor(name, flag->value() == 0);
8118}
8119
8120
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008121#ifdef ENABLE_DEBUGGER_SUPPORT
8122static Object* Runtime_DebugBreak(Arguments args) {
8123 ASSERT(args.length() == 0);
8124 return Execution::DebugBreakHelper();
8125}
8126
8127
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008128// Helper functions for wrapping and unwrapping stack frame ids.
8129static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008130 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008131 return Smi::FromInt(id >> 2);
8132}
8133
8134
8135static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
8136 return static_cast<StackFrame::Id>(wrapped->value() << 2);
8137}
8138
8139
8140// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00008141// args[0]: debug event listener function to set or null or undefined for
8142// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008143// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00008144static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008145 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00008146 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
8147 args[0]->IsUndefined() ||
8148 args[0]->IsNull());
8149 Handle<Object> callback = args.at<Object>(0);
8150 Handle<Object> data = args.at<Object>(1);
8151 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008152
8153 return Heap::undefined_value();
8154}
8155
8156
8157static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00008158 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008159 StackGuard::DebugBreak();
8160 return Heap::undefined_value();
8161}
8162
8163
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008164static Object* DebugLookupResultValue(Object* receiver, String* name,
8165 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00008166 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008167 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008168 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008169 case NORMAL:
8170 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008171 if (value->IsTheHole()) {
8172 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008173 }
8174 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008175 case FIELD:
8176 value =
8177 JSObject::cast(
8178 result->holder())->FastPropertyAt(result->GetFieldIndex());
8179 if (value->IsTheHole()) {
8180 return Heap::undefined_value();
8181 }
8182 return value;
8183 case CONSTANT_FUNCTION:
8184 return result->GetConstantFunction();
8185 case CALLBACKS: {
8186 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008187 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008188 value = receiver->GetPropertyWithCallback(
8189 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00008190 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008191 value = Top::pending_exception();
8192 Top::clear_pending_exception();
8193 if (caught_exception != NULL) {
8194 *caught_exception = true;
8195 }
8196 }
8197 return value;
8198 } else {
8199 return Heap::undefined_value();
8200 }
8201 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008202 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008203 case MAP_TRANSITION:
8204 case CONSTANT_TRANSITION:
8205 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008206 return Heap::undefined_value();
8207 default:
8208 UNREACHABLE();
8209 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008210 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008211 return Heap::undefined_value();
8212}
8213
8214
ager@chromium.org32912102009-01-16 10:38:43 +00008215// Get debugger related details for an object property.
8216// args[0]: object holding property
8217// args[1]: name of the property
8218//
8219// The array returned contains the following information:
8220// 0: Property value
8221// 1: Property details
8222// 2: Property value is exception
8223// 3: Getter function if defined
8224// 4: Setter function if defined
8225// Items 2-4 are only filled if the property has either a getter or a setter
8226// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00008227static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008228 HandleScope scope;
8229
8230 ASSERT(args.length() == 2);
8231
8232 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8233 CONVERT_ARG_CHECKED(String, name, 1);
8234
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008235 // Make sure to set the current context to the context before the debugger was
8236 // entered (if the debugger is entered). The reason for switching context here
8237 // is that for some property lookups (accessors and interceptors) callbacks
8238 // into the embedding application can occour, and the embedding application
8239 // could have the assumption that its own global context is the current
8240 // context and not some internal debugger context.
8241 SaveContext save;
8242 if (Debug::InDebugger()) {
8243 Top::set_context(*Debug::debugger_entry()->GetContext());
8244 }
8245
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008246 // Skip the global proxy as it has no properties and always delegates to the
8247 // real global object.
8248 if (obj->IsJSGlobalProxy()) {
8249 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
8250 }
8251
8252
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008253 // Check if the name is trivially convertible to an index and get the element
8254 // if so.
8255 uint32_t index;
8256 if (name->AsArrayIndex(&index)) {
8257 Handle<FixedArray> details = Factory::NewFixedArray(2);
8258 details->set(0, Runtime::GetElementOrCharAt(obj, index));
8259 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
8260 return *Factory::NewJSArrayWithElements(details);
8261 }
8262
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008263 // Find the number of objects making up this.
8264 int length = LocalPrototypeChainLength(*obj);
8265
8266 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008267 Handle<JSObject> jsproto = obj;
8268 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008269 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008270 jsproto->LocalLookup(*name, &result);
8271 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008272 // LookupResult is not GC safe as it holds raw object pointers.
8273 // GC can happen later in this code so put the required fields into
8274 // local variables using handles when required for later use.
8275 PropertyType result_type = result.type();
8276 Handle<Object> result_callback_obj;
8277 if (result_type == CALLBACKS) {
8278 result_callback_obj = Handle<Object>(result.GetCallbackObject());
8279 }
8280 Smi* property_details = result.GetPropertyDetails().AsSmi();
8281 // DebugLookupResultValue can cause GC so details from LookupResult needs
8282 // to be copied to handles before this.
8283 bool caught_exception = false;
8284 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
8285 &caught_exception);
8286 if (raw_value->IsFailure()) return raw_value;
8287 Handle<Object> value(raw_value);
8288
8289 // If the callback object is a fixed array then it contains JavaScript
8290 // getter and/or setter.
8291 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
8292 result_callback_obj->IsFixedArray();
8293 Handle<FixedArray> details =
8294 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
8295 details->set(0, *value);
8296 details->set(1, property_details);
8297 if (hasJavaScriptAccessors) {
8298 details->set(2,
8299 caught_exception ? Heap::true_value()
8300 : Heap::false_value());
8301 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
8302 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
8303 }
8304
8305 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008306 }
8307 if (i < length - 1) {
8308 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
8309 }
8310 }
8311
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008312 return Heap::undefined_value();
8313}
8314
8315
8316static Object* Runtime_DebugGetProperty(Arguments args) {
8317 HandleScope scope;
8318
8319 ASSERT(args.length() == 2);
8320
8321 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8322 CONVERT_ARG_CHECKED(String, name, 1);
8323
8324 LookupResult result;
8325 obj->Lookup(*name, &result);
8326 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008327 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008328 }
8329 return Heap::undefined_value();
8330}
8331
8332
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008333// Return the property type calculated from the property details.
8334// args[0]: smi with property details.
8335static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
8336 ASSERT(args.length() == 1);
8337 CONVERT_CHECKED(Smi, details, args[0]);
8338 PropertyType type = PropertyDetails(details).type();
8339 return Smi::FromInt(static_cast<int>(type));
8340}
8341
8342
8343// Return the property attribute calculated from the property details.
8344// args[0]: smi with property details.
8345static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
8346 ASSERT(args.length() == 1);
8347 CONVERT_CHECKED(Smi, details, args[0]);
8348 PropertyAttributes attributes = PropertyDetails(details).attributes();
8349 return Smi::FromInt(static_cast<int>(attributes));
8350}
8351
8352
8353// Return the property insertion index calculated from the property details.
8354// args[0]: smi with property details.
8355static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
8356 ASSERT(args.length() == 1);
8357 CONVERT_CHECKED(Smi, details, args[0]);
8358 int index = PropertyDetails(details).index();
8359 return Smi::FromInt(index);
8360}
8361
8362
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008363// Return property value from named interceptor.
8364// args[0]: object
8365// args[1]: property name
8366static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
8367 HandleScope scope;
8368 ASSERT(args.length() == 2);
8369 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8370 RUNTIME_ASSERT(obj->HasNamedInterceptor());
8371 CONVERT_ARG_CHECKED(String, name, 1);
8372
8373 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008374 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008375}
8376
8377
8378// Return element value from indexed interceptor.
8379// args[0]: object
8380// args[1]: index
8381static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
8382 HandleScope scope;
8383 ASSERT(args.length() == 2);
8384 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8385 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
8386 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
8387
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008388 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008389}
8390
8391
8392static Object* Runtime_CheckExecutionState(Arguments args) {
8393 ASSERT(args.length() >= 1);
8394 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00008395 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008396 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008397 return Top::Throw(Heap::illegal_execution_state_symbol());
8398 }
8399
8400 return Heap::true_value();
8401}
8402
8403
8404static Object* Runtime_GetFrameCount(Arguments args) {
8405 HandleScope scope;
8406 ASSERT(args.length() == 1);
8407
8408 // Check arguments.
8409 Object* result = Runtime_CheckExecutionState(args);
8410 if (result->IsFailure()) return result;
8411
8412 // Count all frames which are relevant to debugging stack trace.
8413 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008414 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008415 if (id == StackFrame::NO_ID) {
8416 // If there is no JavaScript stack frame count is 0.
8417 return Smi::FromInt(0);
8418 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008419 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
8420 return Smi::FromInt(n);
8421}
8422
8423
8424static const int kFrameDetailsFrameIdIndex = 0;
8425static const int kFrameDetailsReceiverIndex = 1;
8426static const int kFrameDetailsFunctionIndex = 2;
8427static const int kFrameDetailsArgumentCountIndex = 3;
8428static const int kFrameDetailsLocalCountIndex = 4;
8429static const int kFrameDetailsSourcePositionIndex = 5;
8430static const int kFrameDetailsConstructCallIndex = 6;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008431static const int kFrameDetailsAtReturnIndex = 7;
8432static const int kFrameDetailsDebuggerFrameIndex = 8;
8433static const int kFrameDetailsFirstDynamicIndex = 9;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008434
8435// Return an array with frame details
8436// args[0]: number: break id
8437// args[1]: number: frame index
8438//
8439// The array returned contains the following information:
8440// 0: Frame id
8441// 1: Receiver
8442// 2: Function
8443// 3: Argument count
8444// 4: Local count
8445// 5: Source position
8446// 6: Constructor call
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008447// 7: Is at return
8448// 8: Debugger frame
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008449// Arguments name, value
8450// Locals name, value
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008451// Return value if any
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008452static Object* Runtime_GetFrameDetails(Arguments args) {
8453 HandleScope scope;
8454 ASSERT(args.length() == 2);
8455
8456 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008457 Object* check = Runtime_CheckExecutionState(args);
8458 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008459 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8460
8461 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008462 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008463 if (id == StackFrame::NO_ID) {
8464 // If there are no JavaScript stack frames return undefined.
8465 return Heap::undefined_value();
8466 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008467 int count = 0;
8468 JavaScriptFrameIterator it(id);
8469 for (; !it.done(); it.Advance()) {
8470 if (count == index) break;
8471 count++;
8472 }
8473 if (it.done()) return Heap::undefined_value();
8474
8475 // Traverse the saved contexts chain to find the active context for the
8476 // selected frame.
8477 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008478 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008479 save = save->prev();
8480 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008481 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008482
8483 // Get the frame id.
8484 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
8485
8486 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008487 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008488
8489 // Check for constructor frame.
8490 bool constructor = it.frame()->IsConstructor();
8491
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008492 // Get scope info and read from it for local variable information.
8493 Handle<JSFunction> function(JSFunction::cast(it.frame()->function()));
ager@chromium.orgb5737492010-07-15 09:29:43 +00008494 Handle<SerializedScopeInfo> scope_info(function->shared()->scope_info());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008495 ScopeInfo<> info(*scope_info);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008496
8497 // Get the context.
8498 Handle<Context> context(Context::cast(it.frame()->context()));
8499
8500 // Get the locals names and values into a temporary array.
8501 //
8502 // TODO(1240907): Hide compiler-introduced stack variables
8503 // (e.g. .result)? For users of the debugger, they will probably be
8504 // confusing.
8505 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
8506 for (int i = 0; i < info.NumberOfLocals(); i++) {
8507 // Name of the local.
8508 locals->set(i * 2, *info.LocalName(i));
8509
8510 // Fetch the value of the local - either from the stack or from a
8511 // heap-allocated context.
8512 if (i < info.number_of_stack_slots()) {
8513 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
8514 } else {
8515 Handle<String> name = info.LocalName(i);
8516 // Traverse the context chain to the function context as all local
8517 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008518 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008519 context = Handle<Context>(context->previous());
8520 }
8521 ASSERT(context->is_function_context());
8522 locals->set(i * 2 + 1,
ager@chromium.orgb5737492010-07-15 09:29:43 +00008523 context->get(scope_info->ContextSlotIndex(*name, NULL)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008524 }
8525 }
8526
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008527 // Check whether this frame is positioned at return.
8528 int at_return = (index == 0) ? Debug::IsBreakAtReturn(it.frame()) : false;
8529
8530 // If positioned just before return find the value to be returned and add it
8531 // to the frame information.
8532 Handle<Object> return_value = Factory::undefined_value();
8533 if (at_return) {
8534 StackFrameIterator it2;
8535 Address internal_frame_sp = NULL;
8536 while (!it2.done()) {
8537 if (it2.frame()->is_internal()) {
8538 internal_frame_sp = it2.frame()->sp();
8539 } else {
8540 if (it2.frame()->is_java_script()) {
8541 if (it2.frame()->id() == it.frame()->id()) {
8542 // The internal frame just before the JavaScript frame contains the
8543 // value to return on top. A debug break at return will create an
8544 // internal frame to store the return value (eax/rax/r0) before
8545 // entering the debug break exit frame.
8546 if (internal_frame_sp != NULL) {
8547 return_value =
8548 Handle<Object>(Memory::Object_at(internal_frame_sp));
8549 break;
8550 }
8551 }
8552 }
8553
8554 // Indicate that the previous frame was not an internal frame.
8555 internal_frame_sp = NULL;
8556 }
8557 it2.Advance();
8558 }
8559 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008560
8561 // Now advance to the arguments adapter frame (if any). It contains all
8562 // the provided parameters whereas the function frame always have the number
8563 // of arguments matching the functions parameters. The rest of the
8564 // information (except for what is collected above) is the same.
8565 it.AdvanceToArgumentsFrame();
8566
8567 // Find the number of arguments to fill. At least fill the number of
8568 // parameters for the function and fill more if more parameters are provided.
8569 int argument_count = info.number_of_parameters();
8570 if (argument_count < it.frame()->GetProvidedParametersCount()) {
8571 argument_count = it.frame()->GetProvidedParametersCount();
8572 }
8573
8574 // Calculate the size of the result.
8575 int details_size = kFrameDetailsFirstDynamicIndex +
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008576 2 * (argument_count + info.NumberOfLocals()) +
8577 (at_return ? 1 : 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008578 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8579
8580 // Add the frame id.
8581 details->set(kFrameDetailsFrameIdIndex, *frame_id);
8582
8583 // Add the function (same as in function frame).
8584 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
8585
8586 // Add the arguments count.
8587 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
8588
8589 // Add the locals count
8590 details->set(kFrameDetailsLocalCountIndex,
8591 Smi::FromInt(info.NumberOfLocals()));
8592
8593 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00008594 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008595 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
8596 } else {
8597 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
8598 }
8599
8600 // Add the constructor information.
8601 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
8602
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008603 // Add the at return information.
8604 details->set(kFrameDetailsAtReturnIndex, Heap::ToBoolean(at_return));
8605
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008606 // Add information on whether this frame is invoked in the debugger context.
8607 details->set(kFrameDetailsDebuggerFrameIndex,
8608 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
8609
8610 // Fill the dynamic part.
8611 int details_index = kFrameDetailsFirstDynamicIndex;
8612
8613 // Add arguments name and value.
8614 for (int i = 0; i < argument_count; i++) {
8615 // Name of the argument.
8616 if (i < info.number_of_parameters()) {
8617 details->set(details_index++, *info.parameter_name(i));
8618 } else {
8619 details->set(details_index++, Heap::undefined_value());
8620 }
8621
8622 // Parameter value.
8623 if (i < it.frame()->GetProvidedParametersCount()) {
8624 details->set(details_index++, it.frame()->GetParameter(i));
8625 } else {
8626 details->set(details_index++, Heap::undefined_value());
8627 }
8628 }
8629
8630 // Add locals name and value from the temporary copy from the function frame.
8631 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
8632 details->set(details_index++, locals->get(i));
8633 }
8634
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008635 // Add the value being returned.
8636 if (at_return) {
8637 details->set(details_index++, *return_value);
8638 }
8639
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008640 // Add the receiver (same as in function frame).
8641 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
8642 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
8643 Handle<Object> receiver(it.frame()->receiver());
8644 if (!receiver->IsJSObject()) {
8645 // If the receiver is NOT a JSObject we have hit an optimization
8646 // where a value object is not converted into a wrapped JS objects.
8647 // To hide this optimization from the debugger, we wrap the receiver
8648 // by creating correct wrapper object based on the calling frame's
8649 // global context.
8650 it.Advance();
8651 Handle<Context> calling_frames_global_context(
8652 Context::cast(Context::cast(it.frame()->context())->global_context()));
8653 receiver = Factory::ToObject(receiver, calling_frames_global_context);
8654 }
8655 details->set(kFrameDetailsReceiverIndex, *receiver);
8656
8657 ASSERT_EQ(details_size, details_index);
8658 return *Factory::NewJSArrayWithElements(details);
8659}
8660
8661
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008662// Copy all the context locals into an object used to materialize a scope.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008663static void CopyContextLocalsToScopeObject(
8664 Handle<SerializedScopeInfo> serialized_scope_info,
8665 ScopeInfo<>& scope_info,
8666 Handle<Context> context,
8667 Handle<JSObject> scope_object) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008668 // Fill all context locals to the context extension.
8669 for (int i = Context::MIN_CONTEXT_SLOTS;
8670 i < scope_info.number_of_context_slots();
8671 i++) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00008672 int context_index = serialized_scope_info->ContextSlotIndex(
8673 *scope_info.context_slot_name(i), NULL);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008674
8675 // Don't include the arguments shadow (.arguments) context variable.
8676 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
8677 SetProperty(scope_object,
8678 scope_info.context_slot_name(i),
8679 Handle<Object>(context->get(context_index)), NONE);
8680 }
8681 }
8682}
8683
8684
8685// Create a plain JSObject which materializes the local scope for the specified
8686// frame.
8687static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
8688 Handle<JSFunction> function(JSFunction::cast(frame->function()));
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008689 Handle<SharedFunctionInfo> shared(function->shared());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008690 Handle<SerializedScopeInfo> serialized_scope_info(shared->scope_info());
8691 ScopeInfo<> scope_info(*serialized_scope_info);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008692
8693 // Allocate and initialize a JSObject with all the arguments, stack locals
8694 // heap locals and extension properties of the debugged function.
8695 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
8696
8697 // First fill all parameters.
8698 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8699 SetProperty(local_scope,
8700 scope_info.parameter_name(i),
8701 Handle<Object>(frame->GetParameter(i)), NONE);
8702 }
8703
8704 // Second fill all stack locals.
8705 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
8706 SetProperty(local_scope,
8707 scope_info.stack_slot_name(i),
8708 Handle<Object>(frame->GetExpression(i)), NONE);
8709 }
8710
8711 // Third fill all context locals.
8712 Handle<Context> frame_context(Context::cast(frame->context()));
8713 Handle<Context> function_context(frame_context->fcontext());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008714 CopyContextLocalsToScopeObject(serialized_scope_info, scope_info,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008715 function_context, local_scope);
8716
8717 // Finally copy any properties from the function context extension. This will
8718 // be variables introduced by eval.
8719 if (function_context->closure() == *function) {
8720 if (function_context->has_extension() &&
8721 !function_context->IsGlobalContext()) {
8722 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008723 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008724 for (int i = 0; i < keys->length(); i++) {
8725 // Names of variables introduced by eval are strings.
8726 ASSERT(keys->get(i)->IsString());
8727 Handle<String> key(String::cast(keys->get(i)));
8728 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
8729 }
8730 }
8731 }
8732 return local_scope;
8733}
8734
8735
8736// Create a plain JSObject which materializes the closure content for the
8737// context.
8738static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
8739 ASSERT(context->is_function_context());
8740
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008741 Handle<SharedFunctionInfo> shared(context->closure()->shared());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008742 Handle<SerializedScopeInfo> serialized_scope_info(shared->scope_info());
8743 ScopeInfo<> scope_info(*serialized_scope_info);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008744
8745 // Allocate and initialize a JSObject with all the content of theis function
8746 // closure.
8747 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
8748
8749 // Check whether the arguments shadow object exists.
8750 int arguments_shadow_index =
ager@chromium.orgb5737492010-07-15 09:29:43 +00008751 shared->scope_info()->ContextSlotIndex(Heap::arguments_shadow_symbol(),
8752 NULL);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008753 if (arguments_shadow_index >= 0) {
8754 // In this case all the arguments are available in the arguments shadow
8755 // object.
8756 Handle<JSObject> arguments_shadow(
8757 JSObject::cast(context->get(arguments_shadow_index)));
8758 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8759 SetProperty(closure_scope,
8760 scope_info.parameter_name(i),
8761 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
8762 }
8763 }
8764
8765 // Fill all context locals to the context extension.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008766 CopyContextLocalsToScopeObject(serialized_scope_info, scope_info,
8767 context, closure_scope);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008768
8769 // Finally copy any properties from the function context extension. This will
8770 // be variables introduced by eval.
8771 if (context->has_extension()) {
8772 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008773 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008774 for (int i = 0; i < keys->length(); i++) {
8775 // Names of variables introduced by eval are strings.
8776 ASSERT(keys->get(i)->IsString());
8777 Handle<String> key(String::cast(keys->get(i)));
8778 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
8779 }
8780 }
8781
8782 return closure_scope;
8783}
8784
8785
8786// Iterate over the actual scopes visible from a stack frame. All scopes are
8787// backed by an actual context except the local scope, which is inserted
8788// "artifically" in the context chain.
8789class ScopeIterator {
8790 public:
8791 enum ScopeType {
8792 ScopeTypeGlobal = 0,
8793 ScopeTypeLocal,
8794 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00008795 ScopeTypeClosure,
8796 // Every catch block contains an implicit with block (its parameter is
8797 // a JSContextExtensionObject) that extends current scope with a variable
8798 // holding exception object. Such with blocks are treated as scopes of their
8799 // own type.
8800 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008801 };
8802
8803 explicit ScopeIterator(JavaScriptFrame* frame)
8804 : frame_(frame),
8805 function_(JSFunction::cast(frame->function())),
8806 context_(Context::cast(frame->context())),
8807 local_done_(false),
8808 at_local_(false) {
8809
8810 // Check whether the first scope is actually a local scope.
8811 if (context_->IsGlobalContext()) {
8812 // If there is a stack slot for .result then this local scope has been
8813 // created for evaluating top level code and it is not a real local scope.
8814 // Checking for the existence of .result seems fragile, but the scope info
8815 // saved with the code object does not otherwise have that information.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008816 int index = function_->shared()->scope_info()->
8817 StackSlotIndex(Heap::result_symbol());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008818 at_local_ = index < 0;
8819 } else if (context_->is_function_context()) {
8820 at_local_ = true;
8821 }
8822 }
8823
8824 // More scopes?
8825 bool Done() { return context_.is_null(); }
8826
8827 // Move to the next scope.
8828 void Next() {
8829 // If at a local scope mark the local scope as passed.
8830 if (at_local_) {
8831 at_local_ = false;
8832 local_done_ = true;
8833
8834 // If the current context is not associated with the local scope the
8835 // current context is the next real scope, so don't move to the next
8836 // context in this case.
8837 if (context_->closure() != *function_) {
8838 return;
8839 }
8840 }
8841
8842 // The global scope is always the last in the chain.
8843 if (context_->IsGlobalContext()) {
8844 context_ = Handle<Context>();
8845 return;
8846 }
8847
8848 // Move to the next context.
8849 if (context_->is_function_context()) {
8850 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
8851 } else {
8852 context_ = Handle<Context>(context_->previous());
8853 }
8854
8855 // If passing the local scope indicate that the current scope is now the
8856 // local scope.
8857 if (!local_done_ &&
8858 (context_->IsGlobalContext() || (context_->is_function_context()))) {
8859 at_local_ = true;
8860 }
8861 }
8862
8863 // Return the type of the current scope.
8864 int Type() {
8865 if (at_local_) {
8866 return ScopeTypeLocal;
8867 }
8868 if (context_->IsGlobalContext()) {
8869 ASSERT(context_->global()->IsGlobalObject());
8870 return ScopeTypeGlobal;
8871 }
8872 if (context_->is_function_context()) {
8873 return ScopeTypeClosure;
8874 }
8875 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00008876 // Current scope is either an explicit with statement or a with statement
8877 // implicitely generated for a catch block.
8878 // If the extension object here is a JSContextExtensionObject then
8879 // current with statement is one frome a catch block otherwise it's a
8880 // regular with statement.
8881 if (context_->extension()->IsJSContextExtensionObject()) {
8882 return ScopeTypeCatch;
8883 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008884 return ScopeTypeWith;
8885 }
8886
8887 // Return the JavaScript object with the content of the current scope.
8888 Handle<JSObject> ScopeObject() {
8889 switch (Type()) {
8890 case ScopeIterator::ScopeTypeGlobal:
8891 return Handle<JSObject>(CurrentContext()->global());
8892 break;
8893 case ScopeIterator::ScopeTypeLocal:
8894 // Materialize the content of the local scope into a JSObject.
8895 return MaterializeLocalScope(frame_);
8896 break;
8897 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00008898 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008899 // Return the with object.
8900 return Handle<JSObject>(CurrentContext()->extension());
8901 break;
8902 case ScopeIterator::ScopeTypeClosure:
8903 // Materialize the content of the closure scope into a JSObject.
8904 return MaterializeClosure(CurrentContext());
8905 break;
8906 }
8907 UNREACHABLE();
8908 return Handle<JSObject>();
8909 }
8910
8911 // Return the context for this scope. For the local context there might not
8912 // be an actual context.
8913 Handle<Context> CurrentContext() {
8914 if (at_local_ && context_->closure() != *function_) {
8915 return Handle<Context>();
8916 }
8917 return context_;
8918 }
8919
8920#ifdef DEBUG
8921 // Debug print of the content of the current scope.
8922 void DebugPrint() {
8923 switch (Type()) {
8924 case ScopeIterator::ScopeTypeGlobal:
8925 PrintF("Global:\n");
8926 CurrentContext()->Print();
8927 break;
8928
8929 case ScopeIterator::ScopeTypeLocal: {
8930 PrintF("Local:\n");
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008931 ScopeInfo<> scope_info(function_->shared()->scope_info());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008932 scope_info.Print();
8933 if (!CurrentContext().is_null()) {
8934 CurrentContext()->Print();
8935 if (CurrentContext()->has_extension()) {
8936 Handle<JSObject> extension =
8937 Handle<JSObject>(CurrentContext()->extension());
8938 if (extension->IsJSContextExtensionObject()) {
8939 extension->Print();
8940 }
8941 }
8942 }
8943 break;
8944 }
8945
8946 case ScopeIterator::ScopeTypeWith: {
8947 PrintF("With:\n");
8948 Handle<JSObject> extension =
8949 Handle<JSObject>(CurrentContext()->extension());
8950 extension->Print();
8951 break;
8952 }
8953
ager@chromium.orga1645e22009-09-09 19:27:10 +00008954 case ScopeIterator::ScopeTypeCatch: {
8955 PrintF("Catch:\n");
8956 Handle<JSObject> extension =
8957 Handle<JSObject>(CurrentContext()->extension());
8958 extension->Print();
8959 break;
8960 }
8961
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008962 case ScopeIterator::ScopeTypeClosure: {
8963 PrintF("Closure:\n");
8964 CurrentContext()->Print();
8965 if (CurrentContext()->has_extension()) {
8966 Handle<JSObject> extension =
8967 Handle<JSObject>(CurrentContext()->extension());
8968 if (extension->IsJSContextExtensionObject()) {
8969 extension->Print();
8970 }
8971 }
8972 break;
8973 }
8974
8975 default:
8976 UNREACHABLE();
8977 }
8978 PrintF("\n");
8979 }
8980#endif
8981
8982 private:
8983 JavaScriptFrame* frame_;
8984 Handle<JSFunction> function_;
8985 Handle<Context> context_;
8986 bool local_done_;
8987 bool at_local_;
8988
8989 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
8990};
8991
8992
8993static Object* Runtime_GetScopeCount(Arguments args) {
8994 HandleScope scope;
8995 ASSERT(args.length() == 2);
8996
8997 // Check arguments.
8998 Object* check = Runtime_CheckExecutionState(args);
8999 if (check->IsFailure()) return check;
9000 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9001
9002 // Get the frame where the debugging is performed.
9003 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9004 JavaScriptFrameIterator it(id);
9005 JavaScriptFrame* frame = it.frame();
9006
9007 // Count the visible scopes.
9008 int n = 0;
9009 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
9010 n++;
9011 }
9012
9013 return Smi::FromInt(n);
9014}
9015
9016
9017static const int kScopeDetailsTypeIndex = 0;
9018static const int kScopeDetailsObjectIndex = 1;
9019static const int kScopeDetailsSize = 2;
9020
9021// Return an array with scope details
9022// args[0]: number: break id
9023// args[1]: number: frame index
9024// args[2]: number: scope index
9025//
9026// The array returned contains the following information:
9027// 0: Scope type
9028// 1: Scope object
9029static Object* Runtime_GetScopeDetails(Arguments args) {
9030 HandleScope scope;
9031 ASSERT(args.length() == 3);
9032
9033 // Check arguments.
9034 Object* check = Runtime_CheckExecutionState(args);
9035 if (check->IsFailure()) return check;
9036 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9037 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
9038
9039 // Get the frame where the debugging is performed.
9040 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9041 JavaScriptFrameIterator frame_it(id);
9042 JavaScriptFrame* frame = frame_it.frame();
9043
9044 // Find the requested scope.
9045 int n = 0;
9046 ScopeIterator it(frame);
9047 for (; !it.Done() && n < index; it.Next()) {
9048 n++;
9049 }
9050 if (it.Done()) {
9051 return Heap::undefined_value();
9052 }
9053
9054 // Calculate the size of the result.
9055 int details_size = kScopeDetailsSize;
9056 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
9057
9058 // Fill in scope details.
9059 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
9060 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
9061
9062 return *Factory::NewJSArrayWithElements(details);
9063}
9064
9065
9066static Object* Runtime_DebugPrintScopes(Arguments args) {
9067 HandleScope scope;
9068 ASSERT(args.length() == 0);
9069
9070#ifdef DEBUG
9071 // Print the scopes for the top frame.
9072 StackFrameLocator locator;
9073 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
9074 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
9075 it.DebugPrint();
9076 }
9077#endif
9078 return Heap::undefined_value();
9079}
9080
9081
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009082static Object* Runtime_GetCFrames(Arguments args) {
9083 HandleScope scope;
9084 ASSERT(args.length() == 1);
9085 Object* result = Runtime_CheckExecutionState(args);
9086 if (result->IsFailure()) return result;
9087
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00009088#if V8_HOST_ARCH_64_BIT
9089 UNIMPLEMENTED();
9090 return Heap::undefined_value();
9091#else
9092
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009093 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009094 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
9095 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009096 if (frames_count == OS::kStackWalkError) {
9097 return Heap::undefined_value();
9098 }
9099
9100 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
9101 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
9102 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
9103 for (int i = 0; i < frames_count; i++) {
9104 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
9105 frame_value->SetProperty(
9106 *address_str,
9107 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
9108 NONE);
9109
9110 // Get the stack walk text for this frame.
9111 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009112 int frame_text_length = StrLength(frames[i].text);
9113 if (frame_text_length > 0) {
9114 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009115 frame_text = Factory::NewStringFromAscii(str);
9116 }
9117
9118 if (!frame_text.is_null()) {
9119 frame_value->SetProperty(*text_str, *frame_text, NONE);
9120 }
9121
9122 frames_array->set(i, *frame_value);
9123 }
9124 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00009125#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009126}
9127
9128
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00009129static Object* Runtime_GetThreadCount(Arguments args) {
9130 HandleScope scope;
9131 ASSERT(args.length() == 1);
9132
9133 // Check arguments.
9134 Object* result = Runtime_CheckExecutionState(args);
9135 if (result->IsFailure()) return result;
9136
9137 // Count all archived V8 threads.
9138 int n = 0;
9139 for (ThreadState* thread = ThreadState::FirstInUse();
9140 thread != NULL;
9141 thread = thread->Next()) {
9142 n++;
9143 }
9144
9145 // Total number of threads is current thread and archived threads.
9146 return Smi::FromInt(n + 1);
9147}
9148
9149
9150static const int kThreadDetailsCurrentThreadIndex = 0;
9151static const int kThreadDetailsThreadIdIndex = 1;
9152static const int kThreadDetailsSize = 2;
9153
9154// Return an array with thread details
9155// args[0]: number: break id
9156// args[1]: number: thread index
9157//
9158// The array returned contains the following information:
9159// 0: Is current thread?
9160// 1: Thread id
9161static Object* Runtime_GetThreadDetails(Arguments args) {
9162 HandleScope scope;
9163 ASSERT(args.length() == 2);
9164
9165 // Check arguments.
9166 Object* check = Runtime_CheckExecutionState(args);
9167 if (check->IsFailure()) return check;
9168 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
9169
9170 // Allocate array for result.
9171 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
9172
9173 // Thread index 0 is current thread.
9174 if (index == 0) {
9175 // Fill the details.
9176 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
9177 details->set(kThreadDetailsThreadIdIndex,
9178 Smi::FromInt(ThreadManager::CurrentId()));
9179 } else {
9180 // Find the thread with the requested index.
9181 int n = 1;
9182 ThreadState* thread = ThreadState::FirstInUse();
9183 while (index != n && thread != NULL) {
9184 thread = thread->Next();
9185 n++;
9186 }
9187 if (thread == NULL) {
9188 return Heap::undefined_value();
9189 }
9190
9191 // Fill the details.
9192 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
9193 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
9194 }
9195
9196 // Convert to JS array and return.
9197 return *Factory::NewJSArrayWithElements(details);
9198}
9199
9200
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009201static Object* Runtime_GetBreakLocations(Arguments args) {
9202 HandleScope scope;
9203 ASSERT(args.length() == 1);
9204
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009205 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9206 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009207 // Find the number of break points
9208 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
9209 if (break_locations->IsUndefined()) return Heap::undefined_value();
9210 // Return array as JS array
9211 return *Factory::NewJSArrayWithElements(
9212 Handle<FixedArray>::cast(break_locations));
9213}
9214
9215
9216// Set a break point in a function
9217// args[0]: function
9218// args[1]: number: break source position (within the function source)
9219// args[2]: number: break point object
9220static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
9221 HandleScope scope;
9222 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009223 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9224 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009225 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9226 RUNTIME_ASSERT(source_position >= 0);
9227 Handle<Object> break_point_object_arg = args.at<Object>(2);
9228
9229 // Set break point.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009230 Debug::SetBreakPoint(shared, break_point_object_arg, &source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009231
lrn@chromium.org32d961d2010-06-30 09:09:34 +00009232 return Smi::FromInt(source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009233}
9234
9235
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009236Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
9237 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009238 // Iterate the heap looking for SharedFunctionInfo generated from the
9239 // script. The inner most SharedFunctionInfo containing the source position
9240 // for the requested break point is found.
9241 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
9242 // which is found is not compiled it is compiled and the heap is iterated
9243 // again as the compilation might create inner functions from the newly
9244 // compiled function and the actual requested break point might be in one of
9245 // these functions.
9246 bool done = false;
9247 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00009248 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009249 Handle<SharedFunctionInfo> target;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009250 while (!done) {
9251 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009252 for (HeapObject* obj = iterator.next();
9253 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009254 if (obj->IsSharedFunctionInfo()) {
9255 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
9256 if (shared->script() == *script) {
9257 // If the SharedFunctionInfo found has the requested script data and
9258 // contains the source position it is a candidate.
9259 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00009260 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009261 start_position = shared->start_position();
9262 }
9263 if (start_position <= position &&
9264 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00009265 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009266 // candidate this is the new candidate.
9267 if (target.is_null()) {
9268 target_start_position = start_position;
9269 target = shared;
9270 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00009271 if (target_start_position == start_position &&
9272 shared->end_position() == target->end_position()) {
9273 // If a top-level function contain only one function
9274 // declartion the source for the top-level and the function is
9275 // the same. In that case prefer the non top-level function.
9276 if (!shared->is_toplevel()) {
9277 target_start_position = start_position;
9278 target = shared;
9279 }
9280 } else if (target_start_position <= start_position &&
9281 shared->end_position() <= target->end_position()) {
9282 // This containment check includes equality as a function inside
9283 // a top-level function can share either start or end position
9284 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009285 target_start_position = start_position;
9286 target = shared;
9287 }
9288 }
9289 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009290 }
9291 }
9292 }
9293
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009294 if (target.is_null()) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009295 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009296 }
9297
9298 // If the candidate found is compiled we are done. NOTE: when lazy
9299 // compilation of inner functions is introduced some additional checking
9300 // needs to be done here to compile inner functions.
9301 done = target->is_compiled();
9302 if (!done) {
9303 // If the candidate is not compiled compile it to reveal any inner
9304 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009305 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009306 }
9307 }
9308
9309 return *target;
9310}
9311
9312
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009313// Changes the state of a break point in a script and returns source position
9314// where break point was set. NOTE: Regarding performance see the NOTE for
9315// GetScriptFromScriptData.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009316// args[0]: script to set break point in
9317// args[1]: number: break source position (within the script source)
9318// args[2]: number: break point object
9319static Object* Runtime_SetScriptBreakPoint(Arguments args) {
9320 HandleScope scope;
9321 ASSERT(args.length() == 3);
9322 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
9323 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9324 RUNTIME_ASSERT(source_position >= 0);
9325 Handle<Object> break_point_object_arg = args.at<Object>(2);
9326
9327 // Get the script from the script wrapper.
9328 RUNTIME_ASSERT(wrapper->value()->IsScript());
9329 Handle<Script> script(Script::cast(wrapper->value()));
9330
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009331 Object* result = Runtime::FindSharedFunctionInfoInScript(
9332 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009333 if (!result->IsUndefined()) {
9334 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
9335 // Find position within function. The script position might be before the
9336 // source position of the first function.
9337 int position;
9338 if (shared->start_position() > source_position) {
9339 position = 0;
9340 } else {
9341 position = source_position - shared->start_position();
9342 }
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009343 Debug::SetBreakPoint(shared, break_point_object_arg, &position);
9344 position += shared->start_position();
9345 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009346 }
9347 return Heap::undefined_value();
9348}
9349
9350
9351// Clear a break point
9352// args[0]: number: break point object
9353static Object* Runtime_ClearBreakPoint(Arguments args) {
9354 HandleScope scope;
9355 ASSERT(args.length() == 1);
9356 Handle<Object> break_point_object_arg = args.at<Object>(0);
9357
9358 // Clear break point.
9359 Debug::ClearBreakPoint(break_point_object_arg);
9360
9361 return Heap::undefined_value();
9362}
9363
9364
9365// Change the state of break on exceptions
9366// args[0]: boolean indicating uncaught exceptions
9367// args[1]: boolean indicating on/off
9368static Object* Runtime_ChangeBreakOnException(Arguments args) {
9369 HandleScope scope;
9370 ASSERT(args.length() == 2);
9371 ASSERT(args[0]->IsNumber());
9372 ASSERT(args[1]->IsBoolean());
9373
9374 // Update break point state
9375 ExceptionBreakType type =
9376 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
9377 bool enable = args[1]->ToBoolean()->IsTrue();
9378 Debug::ChangeBreakOnException(type, enable);
9379 return Heap::undefined_value();
9380}
9381
9382
9383// Prepare for stepping
9384// args[0]: break id for checking execution state
9385// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00009386// args[2]: number of times to perform the step, for step out it is the number
9387// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009388static Object* Runtime_PrepareStep(Arguments args) {
9389 HandleScope scope;
9390 ASSERT(args.length() == 3);
9391 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009392 Object* check = Runtime_CheckExecutionState(args);
9393 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009394 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
9395 return Top::Throw(Heap::illegal_argument_symbol());
9396 }
9397
9398 // Get the step action and check validity.
9399 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
9400 if (step_action != StepIn &&
9401 step_action != StepNext &&
9402 step_action != StepOut &&
9403 step_action != StepInMin &&
9404 step_action != StepMin) {
9405 return Top::Throw(Heap::illegal_argument_symbol());
9406 }
9407
9408 // Get the number of steps.
9409 int step_count = NumberToInt32(args[2]);
9410 if (step_count < 1) {
9411 return Top::Throw(Heap::illegal_argument_symbol());
9412 }
9413
ager@chromium.orga1645e22009-09-09 19:27:10 +00009414 // Clear all current stepping setup.
9415 Debug::ClearStepping();
9416
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009417 // Prepare step.
9418 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
9419 return Heap::undefined_value();
9420}
9421
9422
9423// Clear all stepping set by PrepareStep.
9424static Object* Runtime_ClearStepping(Arguments args) {
9425 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009426 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009427 Debug::ClearStepping();
9428 return Heap::undefined_value();
9429}
9430
9431
9432// Creates a copy of the with context chain. The copy of the context chain is
9433// is linked to the function context supplied.
9434static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
9435 Handle<Context> function_context) {
9436 // At the bottom of the chain. Return the function context to link to.
9437 if (context_chain->is_function_context()) {
9438 return function_context;
9439 }
9440
9441 // Recursively copy the with contexts.
9442 Handle<Context> previous(context_chain->previous());
9443 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
9444 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009445 CopyWithContextChain(function_context, previous),
9446 extension,
9447 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009448}
9449
9450
9451// Helper function to find or create the arguments object for
9452// Runtime_DebugEvaluate.
9453static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
9454 Handle<JSFunction> function,
ager@chromium.orgb5737492010-07-15 09:29:43 +00009455 Handle<SerializedScopeInfo> scope_info,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009456 const ScopeInfo<>* sinfo,
9457 Handle<Context> function_context) {
9458 // Try to find the value of 'arguments' to pass as parameter. If it is not
9459 // found (that is the debugged function does not reference 'arguments' and
9460 // does not support eval) then create an 'arguments' object.
9461 int index;
9462 if (sinfo->number_of_stack_slots() > 0) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00009463 index = scope_info->StackSlotIndex(Heap::arguments_symbol());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009464 if (index != -1) {
9465 return Handle<Object>(frame->GetExpression(index));
9466 }
9467 }
9468
9469 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00009470 index = scope_info->ContextSlotIndex(Heap::arguments_symbol(), NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009471 if (index != -1) {
9472 return Handle<Object>(function_context->get(index));
9473 }
9474 }
9475
9476 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009477 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
9478 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009479
9480 AssertNoAllocation no_gc;
9481 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009482 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009483 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009484 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009485 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009486 return arguments;
9487}
9488
9489
9490// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00009491// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009492// extension part has all the parameters and locals of the function on the
9493// stack frame. A function which calls eval with the code to evaluate is then
9494// compiled in this context and called in this context. As this context
9495// replaces the context of the function on the stack frame a new (empty)
9496// function is created as well to be used as the closure for the context.
9497// This function and the context acts as replacements for the function on the
9498// stack frame presenting the same view of the values of parameters and
9499// local variables as if the piece of JavaScript was evaluated at the point
9500// where the function on the stack frame is currently stopped.
9501static Object* Runtime_DebugEvaluate(Arguments args) {
9502 HandleScope scope;
9503
9504 // Check the execution state and decode arguments frame and source to be
9505 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009506 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009507 Object* check_result = Runtime_CheckExecutionState(args);
9508 if (check_result->IsFailure()) return check_result;
9509 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9510 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009511 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
9512
9513 // Handle the processing of break.
9514 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009515
9516 // Get the frame where the debugging is performed.
9517 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9518 JavaScriptFrameIterator it(id);
9519 JavaScriptFrame* frame = it.frame();
9520 Handle<JSFunction> function(JSFunction::cast(frame->function()));
ager@chromium.orgb5737492010-07-15 09:29:43 +00009521 Handle<SerializedScopeInfo> scope_info(function->shared()->scope_info());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009522 ScopeInfo<> sinfo(*scope_info);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009523
9524 // Traverse the saved contexts chain to find the active context for the
9525 // selected frame.
9526 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009527 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009528 save = save->prev();
9529 }
9530 ASSERT(save != NULL);
9531 SaveContext savex;
9532 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009533
9534 // Create the (empty) function replacing the function on the stack frame for
9535 // the purpose of evaluating in the context created below. It is important
9536 // that this function does not describe any parameters and local variables
9537 // in the context. If it does then this will cause problems with the lookup
9538 // in Context::Lookup, where context slots for parameters and local variables
9539 // are looked at before the extension object.
9540 Handle<JSFunction> go_between =
9541 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
9542 go_between->set_context(function->context());
9543#ifdef DEBUG
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009544 ScopeInfo<> go_between_sinfo(go_between->shared()->scope_info());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009545 ASSERT(go_between_sinfo.number_of_parameters() == 0);
9546 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
9547#endif
9548
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009549 // Materialize the content of the local scope into a JSObject.
9550 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009551
9552 // Allocate a new context for the debug evaluation and set the extension
9553 // object build.
9554 Handle<Context> context =
9555 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009556 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009557 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009558 Handle<Context> frame_context(Context::cast(frame->context()));
9559 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009560 context = CopyWithContextChain(frame_context, context);
9561
9562 // Wrap the evaluation statement in a new function compiled in the newly
9563 // created context. The function has one parameter which has to be called
9564 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00009565 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009566 // function(arguments,__source__) {return eval(__source__);}
9567 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00009568 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009569 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009570 Handle<String> function_source =
9571 Factory::NewStringFromAscii(Vector<const char>(source_str,
9572 source_str_length));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009573 Handle<SharedFunctionInfo> shared =
ager@chromium.org381abbb2009-02-25 13:23:22 +00009574 Compiler::CompileEval(function_source,
9575 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00009576 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00009577 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009578 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009579 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009580 Factory::NewFunctionFromSharedFunctionInfo(shared, context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009581
9582 // Invoke the result of the compilation to get the evaluation function.
9583 bool has_pending_exception;
9584 Handle<Object> receiver(frame->receiver());
9585 Handle<Object> evaluation_function =
9586 Execution::Call(compiled_function, receiver, 0, NULL,
9587 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009588 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009589
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009590 Handle<Object> arguments = GetArgumentsObject(frame, function, scope_info,
9591 &sinfo, function_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009592
9593 // Invoke the evaluation function and return the result.
9594 const int argc = 2;
9595 Object** argv[argc] = { arguments.location(),
9596 Handle<Object>::cast(source).location() };
9597 Handle<Object> result =
9598 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
9599 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009600 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009601
9602 // Skip the global proxy as it has no properties and always delegates to the
9603 // real global object.
9604 if (result->IsJSGlobalProxy()) {
9605 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
9606 }
9607
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009608 return *result;
9609}
9610
9611
9612static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
9613 HandleScope scope;
9614
9615 // Check the execution state and decode arguments frame and source to be
9616 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009617 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009618 Object* check_result = Runtime_CheckExecutionState(args);
9619 if (check_result->IsFailure()) return check_result;
9620 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009621 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
9622
9623 // Handle the processing of break.
9624 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009625
9626 // Enter the top context from before the debugger was invoked.
9627 SaveContext save;
9628 SaveContext* top = &save;
9629 while (top != NULL && *top->context() == *Debug::debug_context()) {
9630 top = top->prev();
9631 }
9632 if (top != NULL) {
9633 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009634 }
9635
9636 // Get the global context now set to the top context from before the
9637 // debugger was invoked.
9638 Handle<Context> context = Top::global_context();
9639
9640 // Compile the source to be evaluated.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009641 Handle<SharedFunctionInfo> shared =
9642 Compiler::CompileEval(source,
9643 context,
9644 true,
9645 Compiler::DONT_VALIDATE_JSON);
9646 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009647 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009648 Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
9649 context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009650
9651 // Invoke the result of the compilation to get the evaluation function.
9652 bool has_pending_exception;
9653 Handle<Object> receiver = Top::global();
9654 Handle<Object> result =
9655 Execution::Call(compiled_function, receiver, 0, NULL,
9656 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009657 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009658 return *result;
9659}
9660
9661
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009662static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
9663 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009664 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009665
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009666 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009667 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009668
9669 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009670 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00009671 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
9672 // Get the script wrapper in a local handle before calling GetScriptWrapper,
9673 // because using
9674 // instances->set(i, *GetScriptWrapper(script))
9675 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
9676 // already have deferenced the instances handle.
9677 Handle<JSValue> wrapper = GetScriptWrapper(script);
9678 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009679 }
9680
9681 // Return result as a JS array.
9682 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
9683 Handle<JSArray>::cast(result)->SetContent(*instances);
9684 return *result;
9685}
9686
9687
9688// Helper function used by Runtime_DebugReferencedBy below.
9689static int DebugReferencedBy(JSObject* target,
9690 Object* instance_filter, int max_references,
9691 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009692 JSFunction* arguments_function) {
9693 NoHandleAllocation ha;
9694 AssertNoAllocation no_alloc;
9695
9696 // Iterate the heap.
9697 int count = 0;
9698 JSObject* last = NULL;
9699 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009700 HeapObject* heap_obj = NULL;
9701 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009702 (max_references == 0 || count < max_references)) {
9703 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009704 if (heap_obj->IsJSObject()) {
9705 // Skip context extension objects and argument arrays as these are
9706 // checked in the context of functions using them.
9707 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00009708 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009709 obj->map()->constructor() == arguments_function) {
9710 continue;
9711 }
9712
9713 // Check if the JS object has a reference to the object looked for.
9714 if (obj->ReferencesObject(target)) {
9715 // Check instance filter if supplied. This is normally used to avoid
9716 // references from mirror objects (see Runtime_IsInPrototypeChain).
9717 if (!instance_filter->IsUndefined()) {
9718 Object* V = obj;
9719 while (true) {
9720 Object* prototype = V->GetPrototype();
9721 if (prototype->IsNull()) {
9722 break;
9723 }
9724 if (instance_filter == prototype) {
9725 obj = NULL; // Don't add this object.
9726 break;
9727 }
9728 V = prototype;
9729 }
9730 }
9731
9732 if (obj != NULL) {
9733 // Valid reference found add to instance array if supplied an update
9734 // count.
9735 if (instances != NULL && count < instances_size) {
9736 instances->set(count, obj);
9737 }
9738 last = obj;
9739 count++;
9740 }
9741 }
9742 }
9743 }
9744
9745 // Check for circular reference only. This can happen when the object is only
9746 // referenced from mirrors and has a circular reference in which case the
9747 // object is not really alive and would have been garbage collected if not
9748 // referenced from the mirror.
9749 if (count == 1 && last == target) {
9750 count = 0;
9751 }
9752
9753 // Return the number of referencing objects found.
9754 return count;
9755}
9756
9757
9758// Scan the heap for objects with direct references to an object
9759// args[0]: the object to find references to
9760// args[1]: constructor function for instances to exclude (Mirror)
9761// args[2]: the the maximum number of objects to return
9762static Object* Runtime_DebugReferencedBy(Arguments args) {
9763 ASSERT(args.length() == 3);
9764
9765 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009766 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009767
9768 // Check parameters.
9769 CONVERT_CHECKED(JSObject, target, args[0]);
9770 Object* instance_filter = args[1];
9771 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
9772 instance_filter->IsJSObject());
9773 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
9774 RUNTIME_ASSERT(max_references >= 0);
9775
9776 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009777 JSObject* arguments_boilerplate =
9778 Top::context()->global_context()->arguments_boilerplate();
9779 JSFunction* arguments_function =
9780 JSFunction::cast(arguments_boilerplate->map()->constructor());
9781
9782 // Get the number of referencing objects.
9783 int count;
9784 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009785 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009786
9787 // Allocate an array to hold the result.
9788 Object* object = Heap::AllocateFixedArray(count);
9789 if (object->IsFailure()) return object;
9790 FixedArray* instances = FixedArray::cast(object);
9791
9792 // Fill the referencing objects.
9793 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009794 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009795
9796 // Return result as JS array.
9797 Object* result =
9798 Heap::AllocateJSObject(
9799 Top::context()->global_context()->array_function());
9800 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9801 return result;
9802}
9803
9804
9805// Helper function used by Runtime_DebugConstructedBy below.
9806static int DebugConstructedBy(JSFunction* constructor, int max_references,
9807 FixedArray* instances, int instances_size) {
9808 AssertNoAllocation no_alloc;
9809
9810 // Iterate the heap.
9811 int count = 0;
9812 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009813 HeapObject* heap_obj = NULL;
9814 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009815 (max_references == 0 || count < max_references)) {
9816 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009817 if (heap_obj->IsJSObject()) {
9818 JSObject* obj = JSObject::cast(heap_obj);
9819 if (obj->map()->constructor() == constructor) {
9820 // Valid reference found add to instance array if supplied an update
9821 // count.
9822 if (instances != NULL && count < instances_size) {
9823 instances->set(count, obj);
9824 }
9825 count++;
9826 }
9827 }
9828 }
9829
9830 // Return the number of referencing objects found.
9831 return count;
9832}
9833
9834
9835// Scan the heap for objects constructed by a specific function.
9836// args[0]: the constructor to find instances of
9837// args[1]: the the maximum number of objects to return
9838static Object* Runtime_DebugConstructedBy(Arguments args) {
9839 ASSERT(args.length() == 2);
9840
9841 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009842 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009843
9844 // Check parameters.
9845 CONVERT_CHECKED(JSFunction, constructor, args[0]);
9846 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
9847 RUNTIME_ASSERT(max_references >= 0);
9848
9849 // Get the number of referencing objects.
9850 int count;
9851 count = DebugConstructedBy(constructor, max_references, NULL, 0);
9852
9853 // Allocate an array to hold the result.
9854 Object* object = Heap::AllocateFixedArray(count);
9855 if (object->IsFailure()) return object;
9856 FixedArray* instances = FixedArray::cast(object);
9857
9858 // Fill the referencing objects.
9859 count = DebugConstructedBy(constructor, max_references, instances, count);
9860
9861 // Return result as JS array.
9862 Object* result =
9863 Heap::AllocateJSObject(
9864 Top::context()->global_context()->array_function());
9865 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9866 return result;
9867}
9868
9869
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009870// Find the effective prototype object as returned by __proto__.
9871// args[0]: the object to find the prototype for.
9872static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009873 ASSERT(args.length() == 1);
9874
9875 CONVERT_CHECKED(JSObject, obj, args[0]);
9876
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009877 // Use the __proto__ accessor.
9878 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009879}
9880
9881
9882static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009883 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009884 CPU::DebugBreak();
9885 return Heap::undefined_value();
9886}
9887
9888
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009889static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009890#ifdef DEBUG
9891 HandleScope scope;
9892 ASSERT(args.length() == 1);
9893 // Get the function and make sure it is compiled.
9894 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009895 Handle<SharedFunctionInfo> shared(func->shared());
9896 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009897 return Failure::Exception();
9898 }
9899 func->code()->PrintLn();
9900#endif // DEBUG
9901 return Heap::undefined_value();
9902}
ager@chromium.org9085a012009-05-11 19:22:57 +00009903
9904
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009905static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
9906#ifdef DEBUG
9907 HandleScope scope;
9908 ASSERT(args.length() == 1);
9909 // Get the function and make sure it is compiled.
9910 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009911 Handle<SharedFunctionInfo> shared(func->shared());
9912 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009913 return Failure::Exception();
9914 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009915 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009916#endif // DEBUG
9917 return Heap::undefined_value();
9918}
9919
9920
ager@chromium.org9085a012009-05-11 19:22:57 +00009921static Object* Runtime_FunctionGetInferredName(Arguments args) {
9922 NoHandleAllocation ha;
9923 ASSERT(args.length() == 1);
9924
9925 CONVERT_CHECKED(JSFunction, f, args[0]);
9926 return f->shared()->inferred_name();
9927}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009928
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009929
9930static int FindSharedFunctionInfosForScript(Script* script,
9931 FixedArray* buffer) {
9932 AssertNoAllocation no_allocations;
9933
9934 int counter = 0;
9935 int buffer_size = buffer->length();
9936 HeapIterator iterator;
9937 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
9938 ASSERT(obj != NULL);
9939 if (!obj->IsSharedFunctionInfo()) {
9940 continue;
9941 }
9942 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
9943 if (shared->script() != script) {
9944 continue;
9945 }
9946 if (counter < buffer_size) {
9947 buffer->set(counter, shared);
9948 }
9949 counter++;
9950 }
9951 return counter;
9952}
9953
9954// For a script finds all SharedFunctionInfo's in the heap that points
9955// to this script. Returns JSArray of SharedFunctionInfo wrapped
9956// in OpaqueReferences.
9957static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
9958 Arguments args) {
9959 ASSERT(args.length() == 1);
9960 HandleScope scope;
9961 CONVERT_CHECKED(JSValue, script_value, args[0]);
9962
9963 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
9964
9965 const int kBufferSize = 32;
9966
9967 Handle<FixedArray> array;
9968 array = Factory::NewFixedArray(kBufferSize);
9969 int number = FindSharedFunctionInfosForScript(*script, *array);
9970 if (number > kBufferSize) {
9971 array = Factory::NewFixedArray(number);
9972 FindSharedFunctionInfosForScript(*script, *array);
9973 }
9974
9975 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
9976 result->set_length(Smi::FromInt(number));
9977
9978 LiveEdit::WrapSharedFunctionInfos(result);
9979
9980 return *result;
9981}
9982
9983// For a script calculates compilation information about all its functions.
9984// The script source is explicitly specified by the second argument.
9985// The source of the actual script is not used, however it is important that
9986// all generated code keeps references to this particular instance of script.
9987// Returns a JSArray of compilation infos. The array is ordered so that
9988// each function with all its descendant is always stored in a continues range
9989// with the function itself going first. The root function is a script function.
9990static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
9991 ASSERT(args.length() == 2);
9992 HandleScope scope;
9993 CONVERT_CHECKED(JSValue, script, args[0]);
9994 CONVERT_ARG_CHECKED(String, source, 1);
9995 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
9996
9997 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
9998
9999 if (Top::has_pending_exception()) {
10000 return Failure::Exception();
10001 }
10002
10003 return result;
10004}
10005
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010006// Changes the source of the script to a new_source.
10007// If old_script_name is provided (i.e. is a String), also creates a copy of
10008// the script with its original source and sends notification to debugger.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010009static Object* Runtime_LiveEditReplaceScript(Arguments args) {
10010 ASSERT(args.length() == 3);
10011 HandleScope scope;
10012 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
10013 CONVERT_ARG_CHECKED(String, new_source, 1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010014 Handle<Object> old_script_name(args[2]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010015
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010016 CONVERT_CHECKED(Script, original_script_pointer,
10017 original_script_value->value());
10018 Handle<Script> original_script(original_script_pointer);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010019
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010020 Object* old_script = LiveEdit::ChangeScriptSource(original_script,
10021 new_source,
10022 old_script_name);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010023
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010024 if (old_script->IsScript()) {
10025 Handle<Script> script_handle(Script::cast(old_script));
10026 return *(GetScriptWrapper(script_handle));
10027 } else {
10028 return Heap::null_value();
10029 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010030}
10031
10032// Replaces code of SharedFunctionInfo with a new one.
10033static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
10034 ASSERT(args.length() == 2);
10035 HandleScope scope;
10036 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
10037 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
10038
ager@chromium.orgac091b72010-05-05 07:34:42 +000010039 return LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010040}
10041
10042// Connects SharedFunctionInfo to another script.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010043static Object* Runtime_LiveEditFunctionSetScript(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010044 ASSERT(args.length() == 2);
10045 HandleScope scope;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010046 Handle<Object> function_object(args[0]);
10047 Handle<Object> script_object(args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010048
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010049 if (function_object->IsJSValue()) {
10050 Handle<JSValue> function_wrapper = Handle<JSValue>::cast(function_object);
10051 if (script_object->IsJSValue()) {
10052 CONVERT_CHECKED(Script, script, JSValue::cast(*script_object)->value());
10053 script_object = Handle<Object>(script);
10054 }
10055
10056 LiveEdit::SetFunctionScript(function_wrapper, script_object);
10057 } else {
10058 // Just ignore this. We may not have a SharedFunctionInfo for some functions
10059 // and we check it in this function.
10060 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010061
10062 return Heap::undefined_value();
10063}
10064
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010065
10066// In a code of a parent function replaces original function as embedded object
10067// with a substitution one.
10068static Object* Runtime_LiveEditReplaceRefToNestedFunction(Arguments args) {
10069 ASSERT(args.length() == 3);
10070 HandleScope scope;
10071
10072 CONVERT_ARG_CHECKED(JSValue, parent_wrapper, 0);
10073 CONVERT_ARG_CHECKED(JSValue, orig_wrapper, 1);
10074 CONVERT_ARG_CHECKED(JSValue, subst_wrapper, 2);
10075
10076 LiveEdit::ReplaceRefToNestedFunction(parent_wrapper, orig_wrapper,
10077 subst_wrapper);
10078
10079 return Heap::undefined_value();
10080}
10081
10082
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010083// Updates positions of a shared function info (first parameter) according
10084// to script source change. Text change is described in second parameter as
10085// array of groups of 3 numbers:
10086// (change_begin, change_end, change_end_new_position).
10087// Each group describes a change in text; groups are sorted by change_begin.
10088static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
10089 ASSERT(args.length() == 2);
10090 HandleScope scope;
10091 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
10092 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
10093
ager@chromium.orgac091b72010-05-05 07:34:42 +000010094 return LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010095}
10096
10097
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010098// For array of SharedFunctionInfo's (each wrapped in JSValue)
10099// checks that none of them have activations on stacks (of any thread).
10100// Returns array of the same length with corresponding results of
10101// LiveEdit::FunctionPatchabilityStatus type.
ager@chromium.org357bf652010-04-12 11:30:10 +000010102static Object* Runtime_LiveEditCheckAndDropActivations(Arguments args) {
10103 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010104 HandleScope scope;
10105 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +000010106 CONVERT_BOOLEAN_CHECKED(do_drop, args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010107
ager@chromium.org357bf652010-04-12 11:30:10 +000010108 return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010109}
10110
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010111// Compares 2 strings line-by-line and returns diff in form of JSArray of
fschneider@chromium.org013f3e12010-04-26 13:27:52 +000010112// triplets (pos1, pos1_end, pos2_end) describing list of diff chunks.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010113static Object* Runtime_LiveEditCompareStringsLinewise(Arguments args) {
10114 ASSERT(args.length() == 2);
10115 HandleScope scope;
10116 CONVERT_ARG_CHECKED(String, s1, 0);
10117 CONVERT_ARG_CHECKED(String, s2, 1);
10118
10119 return *LiveEdit::CompareStringsLinewise(s1, s2);
10120}
10121
10122
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010123
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010124// A testing entry. Returns statement position which is the closest to
10125// source_position.
10126static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
10127 ASSERT(args.length() == 2);
10128 HandleScope scope;
10129 CONVERT_ARG_CHECKED(JSFunction, function, 0);
10130 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
10131
10132 Handle<Code> code(function->code());
10133
10134 RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
10135 int closest_pc = 0;
10136 int distance = kMaxInt;
10137 while (!it.done()) {
10138 int statement_position = static_cast<int>(it.rinfo()->data());
10139 // Check if this break point is closer that what was previously found.
10140 if (source_position <= statement_position &&
10141 statement_position - source_position < distance) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +000010142 closest_pc =
10143 static_cast<int>(it.rinfo()->pc() - code->instruction_start());
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010144 distance = statement_position - source_position;
10145 // Check whether we can't get any closer.
10146 if (distance == 0) break;
10147 }
10148 it.next();
10149 }
10150
10151 return Smi::FromInt(closest_pc);
10152}
10153
10154
ager@chromium.org357bf652010-04-12 11:30:10 +000010155// Calls specified function with or without entering the debugger.
10156// This is used in unit tests to run code as if debugger is entered or simply
10157// to have a stack with C++ frame in the middle.
10158static Object* Runtime_ExecuteInDebugContext(Arguments args) {
10159 ASSERT(args.length() == 2);
10160 HandleScope scope;
10161 CONVERT_ARG_CHECKED(JSFunction, function, 0);
10162 CONVERT_BOOLEAN_CHECKED(without_debugger, args[1]);
10163
10164 Handle<Object> result;
10165 bool pending_exception;
10166 {
10167 if (without_debugger) {
10168 result = Execution::Call(function, Top::global(), 0, NULL,
10169 &pending_exception);
10170 } else {
10171 EnterDebugger enter_debugger;
10172 result = Execution::Call(function, Top::global(), 0, NULL,
10173 &pending_exception);
10174 }
10175 }
10176 if (!pending_exception) {
10177 return *result;
10178 } else {
10179 return Failure::Exception();
10180 }
10181}
10182
10183
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010184#endif // ENABLE_DEBUGGER_SUPPORT
10185
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010186#ifdef ENABLE_LOGGING_AND_PROFILING
10187
10188static Object* Runtime_ProfilerResume(Arguments args) {
10189 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010190 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010191
10192 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010193 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10194 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010195 return Heap::undefined_value();
10196}
10197
10198
10199static Object* Runtime_ProfilerPause(Arguments args) {
10200 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010201 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010202
10203 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010204 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10205 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010206 return Heap::undefined_value();
10207}
10208
10209#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010210
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010211// Finds the script object from the script data. NOTE: This operation uses
10212// heap traversal to find the function generated for the source position
10213// for the requested break point. For lazily compiled functions several heap
10214// traversals might be required rendering this operation as a rather slow
10215// operation. However for setting break points which is normally done through
10216// some kind of user interaction the performance is not crucial.
10217static Handle<Object> Runtime_GetScriptFromScriptName(
10218 Handle<String> script_name) {
10219 // Scan the heap for Script objects to find the script with the requested
10220 // script data.
10221 Handle<Script> script;
10222 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010223 HeapObject* obj = NULL;
10224 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010225 // If a script is found check if it has the script data requested.
10226 if (obj->IsScript()) {
10227 if (Script::cast(obj)->name()->IsString()) {
10228 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
10229 script = Handle<Script>(Script::cast(obj));
10230 }
10231 }
10232 }
10233 }
10234
10235 // If no script with the requested script data is found return undefined.
10236 if (script.is_null()) return Factory::undefined_value();
10237
10238 // Return the script found.
10239 return GetScriptWrapper(script);
10240}
10241
10242
10243// Get the script object from script data. NOTE: Regarding performance
10244// see the NOTE for GetScriptFromScriptData.
10245// args[0]: script data for the script to find the source for
10246static Object* Runtime_GetScript(Arguments args) {
10247 HandleScope scope;
10248
10249 ASSERT(args.length() == 1);
10250
10251 CONVERT_CHECKED(String, script_name, args[0]);
10252
10253 // Find the requested script.
10254 Handle<Object> result =
10255 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
10256 return *result;
10257}
10258
10259
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010260// Determines whether the given stack frame should be displayed in
10261// a stack trace. The caller is the error constructor that asked
10262// for the stack trace to be collected. The first time a construct
10263// call to this function is encountered it is skipped. The seen_caller
10264// in/out parameter is used to remember if the caller has been seen
10265// yet.
10266static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
10267 bool* seen_caller) {
10268 // Only display JS frames.
10269 if (!raw_frame->is_java_script())
10270 return false;
10271 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
10272 Object* raw_fun = frame->function();
10273 // Not sure when this can happen but skip it just in case.
10274 if (!raw_fun->IsJSFunction())
10275 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010276 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010277 *seen_caller = true;
10278 return false;
10279 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010280 // Skip all frames until we've seen the caller. Also, skip the most
10281 // obvious builtin calls. Some builtin calls (such as Number.ADD
10282 // which is invoked using 'call') are very difficult to recognize
10283 // so we're leaving them in for now.
10284 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010285}
10286
10287
10288// Collect the raw data for a stack trace. Returns an array of three
10289// element segments each containing a receiver, function and native
10290// code offset.
10291static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010292 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010293 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010294 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
10295
10296 HandleScope scope;
10297
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000010298 limit = Max(limit, 0); // Ensure that limit is not negative.
10299 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010300 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010301
10302 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010303 // If the caller parameter is a function we skip frames until we're
10304 // under it before starting to collect.
10305 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010306 int cursor = 0;
10307 int frames_seen = 0;
10308 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010309 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010310 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010311 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010312 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010313 Object* recv = frame->receiver();
10314 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010315 Address pc = frame->pc();
10316 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010317 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010318 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010319 if (cursor + 2 < elements->length()) {
10320 elements->set(cursor++, recv);
10321 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010322 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010323 } else {
10324 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010325 Handle<Object> recv_handle(recv);
10326 Handle<Object> fun_handle(fun);
10327 SetElement(result, cursor++, recv_handle);
10328 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010329 SetElement(result, cursor++, Handle<Smi>(offset));
10330 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010331 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010332 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010333 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010334
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010335 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010336 return *result;
10337}
10338
10339
ager@chromium.org3811b432009-10-28 14:53:37 +000010340// Returns V8 version as a string.
10341static Object* Runtime_GetV8Version(Arguments args) {
10342 ASSERT_EQ(args.length(), 0);
10343
10344 NoHandleAllocation ha;
10345
10346 const char* version_string = v8::V8::GetVersion();
10347
10348 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
10349}
10350
10351
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010352static Object* Runtime_Abort(Arguments args) {
10353 ASSERT(args.length() == 2);
10354 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
10355 Smi::cast(args[1])->value());
10356 Top::PrintStack();
10357 OS::Abort();
10358 UNREACHABLE();
10359 return NULL;
10360}
10361
10362
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010363static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
10364 ASSERT(args.length() == 0);
10365 HandleScope::DeleteExtensions();
10366 return Heap::undefined_value();
10367}
10368
10369
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010370static Object* CacheMiss(FixedArray* cache_obj, int index, Object* key_obj) {
10371 ASSERT(index % 2 == 0); // index of the key
10372 ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
10373 ASSERT(index < cache_obj->length());
10374
10375 HandleScope scope;
10376
10377 Handle<FixedArray> cache(cache_obj);
10378 Handle<Object> key(key_obj);
10379 Handle<JSFunction> factory(JSFunction::cast(
10380 cache->get(JSFunctionResultCache::kFactoryIndex)));
10381 // TODO(antonm): consider passing a receiver when constructing a cache.
10382 Handle<Object> receiver(Top::global_context()->global());
10383
10384 Handle<Object> value;
10385 {
10386 // This handle is nor shared, nor used later, so it's safe.
10387 Object** argv[] = { key.location() };
10388 bool pending_exception = false;
10389 value = Execution::Call(factory,
10390 receiver,
10391 1,
10392 argv,
10393 &pending_exception);
10394 if (pending_exception) return Failure::Exception();
10395 }
10396
10397 cache->set(index, *key);
10398 cache->set(index + 1, *value);
10399 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(index));
10400
10401 return *value;
10402}
10403
10404
10405static Object* Runtime_GetFromCache(Arguments args) {
10406 // This is only called from codegen, so checks might be more lax.
10407 CONVERT_CHECKED(FixedArray, cache, args[0]);
10408 Object* key = args[1];
10409
10410 const int finger_index =
10411 Smi::cast(cache->get(JSFunctionResultCache::kFingerIndex))->value();
10412
10413 Object* o = cache->get(finger_index);
10414 if (o == key) {
10415 // The fastest case: hit the same place again.
10416 return cache->get(finger_index + 1);
10417 }
10418
10419 for (int i = finger_index - 2;
10420 i >= JSFunctionResultCache::kEntriesIndex;
10421 i -= 2) {
10422 o = cache->get(i);
10423 if (o == key) {
10424 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10425 return cache->get(i + 1);
10426 }
10427 }
10428
10429 const int size =
10430 Smi::cast(cache->get(JSFunctionResultCache::kCacheSizeIndex))->value();
10431 ASSERT(size <= cache->length());
10432
10433 for (int i = size - 2; i > finger_index; i -= 2) {
10434 o = cache->get(i);
10435 if (o == key) {
10436 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10437 return cache->get(i + 1);
10438 }
10439 }
10440
10441 // Cache miss. If we have spare room, put new data into it, otherwise
10442 // evict post finger entry which must be least recently used.
10443 if (size < cache->length()) {
10444 cache->set(JSFunctionResultCache::kCacheSizeIndex, Smi::FromInt(size + 2));
10445 return CacheMiss(cache, size, key);
10446 } else {
antonm@chromium.org397e23c2010-04-21 12:00:05 +000010447 int target_index = finger_index + JSFunctionResultCache::kEntrySize;
10448 if (target_index == cache->length()) {
10449 target_index = JSFunctionResultCache::kEntriesIndex;
10450 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010451 return CacheMiss(cache, target_index, key);
10452 }
10453}
10454
kasper.lund44510672008-07-25 07:37:58 +000010455#ifdef DEBUG
10456// ListNatives is ONLY used by the fuzz-natives.js in debug mode
10457// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010458static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +000010459 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010460 HandleScope scope;
10461 Handle<JSArray> result = Factory::NewJSArray(0);
10462 int index = 0;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010463 bool inline_runtime_functions = false;
ager@chromium.orga1645e22009-09-09 19:27:10 +000010464#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010465 { \
10466 HandleScope inner; \
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010467 Handle<String> name; \
10468 /* Inline runtime functions have an underscore in front of the name. */ \
10469 if (inline_runtime_functions) { \
10470 name = Factory::NewStringFromAscii( \
10471 Vector<const char>("_" #Name, StrLength("_" #Name))); \
10472 } else { \
10473 name = Factory::NewStringFromAscii( \
10474 Vector<const char>(#Name, StrLength(#Name))); \
10475 } \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010476 Handle<JSArray> pair = Factory::NewJSArray(0); \
10477 SetElement(pair, 0, name); \
10478 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
10479 SetElement(result, index++, pair); \
10480 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010481 inline_runtime_functions = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010482 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010483 inline_runtime_functions = true;
10484 INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010485#undef ADD_ENTRY
10486 return *result;
10487}
kasper.lund44510672008-07-25 07:37:58 +000010488#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010489
10490
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010491static Object* Runtime_Log(Arguments args) {
10492 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +000010493 CONVERT_CHECKED(String, format, args[0]);
10494 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010495 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010496 Logger::LogRuntime(chars, elms);
10497 return Heap::undefined_value();
10498}
10499
10500
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010501static Object* Runtime_IS_VAR(Arguments args) {
10502 UNREACHABLE(); // implemented as macro in the parser
10503 return NULL;
10504}
10505
10506
10507// ----------------------------------------------------------------------------
10508// Implementation of Runtime
10509
ager@chromium.orga1645e22009-09-09 19:27:10 +000010510#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010511 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +000010512 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010513
10514static Runtime::Function Runtime_functions[] = {
10515 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010516 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010517};
10518
10519#undef F
10520
10521
10522Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
10523 ASSERT(0 <= fid && fid < kNofFunctions);
10524 return &Runtime_functions[fid];
10525}
10526
10527
10528Runtime::Function* Runtime::FunctionForName(const char* name) {
10529 for (Function* f = Runtime_functions; f->name != NULL; f++) {
10530 if (strcmp(f->name, name) == 0) {
10531 return f;
10532 }
10533 }
10534 return NULL;
10535}
10536
10537
10538void Runtime::PerformGC(Object* result) {
10539 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010540 if (failure->IsRetryAfterGC()) {
10541 // Try to do a garbage collection; ignore it if it fails. The C
10542 // entry stub will throw an out-of-memory exception in that case.
10543 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
10544 } else {
10545 // Handle last resort GC and make sure to allow future allocations
10546 // to grow the heap without causing GCs (if possible).
10547 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +000010548 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010549 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010550}
10551
10552
10553} } // namespace v8::internal