blob: 0ac0641c37e0d0e96bc811094d14973528dc3cc6 [file] [log] [blame]
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001// Copyright 2006-2009 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include <stdlib.h>
29
30#include "v8.h"
31
32#include "accessors.h"
33#include "api.h"
34#include "arguments.h"
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000035#include "codegen.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000036#include "compiler.h"
37#include "cpu.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000038#include "dateparser-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000039#include "debug.h"
40#include "execution.h"
41#include "jsregexp.h"
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000042#include "liveedit.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000043#include "parser.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000044#include "platform.h"
45#include "runtime.h"
46#include "scopeinfo.h"
ager@chromium.org7c537e22008-10-16 08:43:32 +000047#include "smart-pointer.h"
ager@chromium.org18ad94b2009-09-02 08:22:29 +000048#include "stub-cache.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000049#include "v8threads.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000050
kasperl@chromium.org71affb52009-05-26 05:44:31 +000051namespace v8 {
52namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000053
54
ager@chromium.org3e875802009-06-29 08:26:34 +000055#define RUNTIME_ASSERT(value) \
56 if (!(value)) return Top::ThrowIllegalOperation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000057
58// Cast the given object to a value of the specified type and store
59// it in a variable with the given name. If the object is not of the
60// expected type call IllegalOperation and return.
61#define CONVERT_CHECKED(Type, name, obj) \
62 RUNTIME_ASSERT(obj->Is##Type()); \
63 Type* name = Type::cast(obj);
64
65#define CONVERT_ARG_CHECKED(Type, name, index) \
66 RUNTIME_ASSERT(args[index]->Is##Type()); \
67 Handle<Type> name = args.at<Type>(index);
68
kasper.lundbd3ec4e2008-07-09 11:06:54 +000069// Cast the given object to a boolean and store it in a variable with
70// the given name. If the object is not a boolean call IllegalOperation
71// and return.
72#define CONVERT_BOOLEAN_CHECKED(name, obj) \
73 RUNTIME_ASSERT(obj->IsBoolean()); \
74 bool name = (obj)->IsTrue();
75
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000076// Cast the given object to a Smi and store its value in an int variable
77// with the given name. If the object is not a Smi call IllegalOperation
78// and return.
79#define CONVERT_SMI_CHECKED(name, obj) \
80 RUNTIME_ASSERT(obj->IsSmi()); \
81 int name = Smi::cast(obj)->value();
82
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000083// Cast the given object to a double and store it in a variable with
84// the given name. If the object is not a number (as opposed to
85// the number not-a-number) call IllegalOperation and return.
86#define CONVERT_DOUBLE_CHECKED(name, obj) \
87 RUNTIME_ASSERT(obj->IsNumber()); \
88 double name = (obj)->Number();
89
90// Call the specified converter on the object *comand store the result in
91// a variable of the specified type with the given name. If the
92// object is not a Number call IllegalOperation and return.
93#define CONVERT_NUMBER_CHECKED(type, name, Type, obj) \
94 RUNTIME_ASSERT(obj->IsNumber()); \
95 type name = NumberTo##Type(obj);
96
97// Non-reentrant string buffer for efficient general use in this file.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000098static StaticResource<StringInputBuffer> runtime_string_input_buffer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000099
100
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000101static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
102 StackLimitCheck check;
103 if (check.HasOverflowed()) return Top::StackOverflow();
104
105 Object* result = Heap::CopyJSObject(boilerplate);
106 if (result->IsFailure()) return result;
107 JSObject* copy = JSObject::cast(result);
108
109 // Deep copy local properties.
110 if (copy->HasFastProperties()) {
111 FixedArray* properties = copy->properties();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000112 for (int i = 0; i < properties->length(); i++) {
113 Object* value = properties->get(i);
114 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000115 JSObject* js_object = JSObject::cast(value);
116 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000117 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000118 properties->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000119 }
120 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000121 int nof = copy->map()->inobject_properties();
122 for (int i = 0; i < nof; i++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000123 Object* value = copy->InObjectPropertyAt(i);
124 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000125 JSObject* js_object = JSObject::cast(value);
126 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000127 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000128 copy->InObjectPropertyAtPut(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000129 }
130 }
131 } else {
132 result = Heap::AllocateFixedArray(copy->NumberOfLocalProperties(NONE));
133 if (result->IsFailure()) return result;
134 FixedArray* names = FixedArray::cast(result);
135 copy->GetLocalPropertyNames(names, 0);
136 for (int i = 0; i < names->length(); i++) {
137 ASSERT(names->get(i)->IsString());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000138 String* key_string = String::cast(names->get(i));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000139 PropertyAttributes attributes =
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000140 copy->GetLocalPropertyAttribute(key_string);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000141 // Only deep copy fields from the object literal expression.
142 // In particular, don't try to copy the length attribute of
143 // an array.
144 if (attributes != NONE) continue;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000145 Object* value = copy->GetProperty(key_string, &attributes);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000146 ASSERT(!value->IsFailure());
147 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000148 JSObject* js_object = JSObject::cast(value);
149 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000150 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000151 result = copy->SetProperty(key_string, result, NONE);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000152 if (result->IsFailure()) return result;
153 }
154 }
155 }
156
157 // Deep copy local elements.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000158 // Pixel elements cannot be created using an object literal.
ager@chromium.org3811b432009-10-28 14:53:37 +0000159 ASSERT(!copy->HasPixelElements() && !copy->HasExternalArrayElements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000160 switch (copy->GetElementsKind()) {
161 case JSObject::FAST_ELEMENTS: {
162 FixedArray* elements = FixedArray::cast(copy->elements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000163 for (int i = 0; i < elements->length(); i++) {
164 Object* value = elements->get(i);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000165 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000166 JSObject* js_object = JSObject::cast(value);
167 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000168 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000169 elements->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000170 }
171 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000172 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000173 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000174 case JSObject::DICTIONARY_ELEMENTS: {
175 NumberDictionary* element_dictionary = copy->element_dictionary();
176 int capacity = element_dictionary->Capacity();
177 for (int i = 0; i < capacity; i++) {
178 Object* k = element_dictionary->KeyAt(i);
179 if (element_dictionary->IsKey(k)) {
180 Object* value = element_dictionary->ValueAt(i);
181 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000182 JSObject* js_object = JSObject::cast(value);
183 result = DeepCopyBoilerplate(js_object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000184 if (result->IsFailure()) return result;
185 element_dictionary->ValueAtPut(i, result);
186 }
187 }
188 }
189 break;
190 }
191 default:
192 UNREACHABLE();
193 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000194 }
195 return copy;
196}
197
198
199static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
200 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
201 return DeepCopyBoilerplate(boilerplate);
202}
203
204
205static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000206 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000207 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000208}
209
210
ager@chromium.org236ad962008-09-25 09:45:57 +0000211static Handle<Map> ComputeObjectLiteralMap(
212 Handle<Context> context,
213 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000214 bool* is_result_from_cache) {
ager@chromium.org32912102009-01-16 10:38:43 +0000215 int number_of_properties = constant_properties->length() / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000216 if (FLAG_canonicalize_object_literal_maps) {
217 // First find prefix of consecutive symbol keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000218 int number_of_symbol_keys = 0;
219 while ((number_of_symbol_keys < number_of_properties) &&
220 (constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
221 number_of_symbol_keys++;
222 }
223 // Based on the number of prefix symbols key we decide whether
224 // to use the map cache in the global context.
225 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000226 if ((number_of_symbol_keys == number_of_properties) &&
227 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000228 // Create the fixed array with the key.
229 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
230 for (int i = 0; i < number_of_symbol_keys; i++) {
231 keys->set(i, constant_properties->get(i*2));
232 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000233 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000234 return Factory::ObjectLiteralMapFromCache(context, keys);
235 }
236 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000237 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000238 return Factory::CopyMap(
239 Handle<Map>(context->object_function()->initial_map()),
240 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000241}
242
243
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000244static Handle<Object> CreateLiteralBoilerplate(
245 Handle<FixedArray> literals,
246 Handle<FixedArray> constant_properties);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000247
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000248
249static Handle<Object> CreateObjectLiteralBoilerplate(
250 Handle<FixedArray> literals,
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000251 Handle<FixedArray> constant_properties,
252 bool should_have_fast_elements) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000253 // Get the global context from the literals array. This is the
254 // context in which the function was created and we use the object
255 // function from this context to create the object literal. We do
256 // not use the object function from the current global context
257 // because this might be the object function from another context
258 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000259 Handle<Context> context =
260 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
261
262 bool is_result_from_cache;
263 Handle<Map> map = ComputeObjectLiteralMap(context,
264 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000265 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000266
ager@chromium.org236ad962008-09-25 09:45:57 +0000267 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000268
269 // Normalize the elements of the boilerplate to save space if needed.
270 if (!should_have_fast_elements) NormalizeElements(boilerplate);
271
ager@chromium.org32912102009-01-16 10:38:43 +0000272 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000273 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000274 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000275 length / 2,
ager@chromium.org236ad962008-09-25 09:45:57 +0000276 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000277 for (int index = 0; index < length; index +=2) {
278 Handle<Object> key(constant_properties->get(index+0));
279 Handle<Object> value(constant_properties->get(index+1));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000280 if (value->IsFixedArray()) {
281 // The value contains the constant_properties of a
282 // simple object literal.
283 Handle<FixedArray> array = Handle<FixedArray>::cast(value);
284 value = CreateLiteralBoilerplate(literals, array);
285 if (value.is_null()) return value;
286 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000287 Handle<Object> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000288 uint32_t element_index = 0;
289 if (key->IsSymbol()) {
290 // If key is a symbol it is not an array element.
291 Handle<String> name(String::cast(*key));
292 ASSERT(!name->AsArrayIndex(&element_index));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000293 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000294 } else if (Array::IndexFromObject(*key, &element_index)) {
295 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000296 result = SetElement(boilerplate, element_index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000297 } else {
298 // Non-uint32 number.
299 ASSERT(key->IsNumber());
300 double num = key->Number();
301 char arr[100];
302 Vector<char> buffer(arr, ARRAY_SIZE(arr));
303 const char* str = DoubleToCString(num, buffer);
304 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000305 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000306 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000307 // If setting the property on the boilerplate throws an
308 // exception, the exception is converted to an empty handle in
309 // the handle based operations. In that case, we need to
310 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000311 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000312 }
313 }
314
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000315 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000316}
317
318
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000319static Handle<Object> CreateArrayLiteralBoilerplate(
320 Handle<FixedArray> literals,
321 Handle<FixedArray> elements) {
322 // Create the JSArray.
323 Handle<JSFunction> constructor(
324 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
325 Handle<Object> object = Factory::NewJSObject(constructor);
326
327 Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
328
329 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
330 for (int i = 0; i < content->length(); i++) {
331 if (content->get(i)->IsFixedArray()) {
332 // The value contains the constant_properties of a
333 // simple object literal.
334 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
335 Handle<Object> result =
336 CreateLiteralBoilerplate(literals, fa);
337 if (result.is_null()) return result;
338 content->set(i, *result);
339 }
340 }
341
342 // Set the elements.
343 Handle<JSArray>::cast(object)->SetContent(*content);
344 return object;
345}
346
347
348static Handle<Object> CreateLiteralBoilerplate(
349 Handle<FixedArray> literals,
350 Handle<FixedArray> array) {
351 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
352 switch (CompileTimeValue::GetType(array)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000353 case CompileTimeValue::OBJECT_LITERAL_FAST_ELEMENTS:
354 return CreateObjectLiteralBoilerplate(literals, elements, true);
355 case CompileTimeValue::OBJECT_LITERAL_SLOW_ELEMENTS:
356 return CreateObjectLiteralBoilerplate(literals, elements, false);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000357 case CompileTimeValue::ARRAY_LITERAL:
358 return CreateArrayLiteralBoilerplate(literals, elements);
359 default:
360 UNREACHABLE();
361 return Handle<Object>::null();
362 }
363}
364
365
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000366static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000367 // Takes a FixedArray of elements containing the literal elements of
368 // the array literal and produces JSArray with those elements.
369 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000370 // which contains the context from which to get the Array function
371 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000372 HandleScope scope;
373 ASSERT(args.length() == 3);
374 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
375 CONVERT_SMI_CHECKED(literals_index, args[1]);
376 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000377
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000378 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
379 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000380
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000381 // Update the functions literal and return the boilerplate.
382 literals->set(literals_index, *object);
383 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000384}
385
386
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000387static Object* Runtime_CreateObjectLiteral(Arguments args) {
388 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000389 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000390 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
391 CONVERT_SMI_CHECKED(literals_index, args[1]);
392 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000393 CONVERT_SMI_CHECKED(fast_elements, args[3]);
394 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000395
396 // Check if boilerplate exists. If not, create it first.
397 Handle<Object> boilerplate(literals->get(literals_index));
398 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000399 boilerplate = CreateObjectLiteralBoilerplate(literals,
400 constant_properties,
401 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000402 if (boilerplate.is_null()) return Failure::Exception();
403 // Update the functions literal and return the boilerplate.
404 literals->set(literals_index, *boilerplate);
405 }
406 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
407}
408
409
410static Object* Runtime_CreateObjectLiteralShallow(Arguments args) {
411 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000412 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000413 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
414 CONVERT_SMI_CHECKED(literals_index, args[1]);
415 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000416 CONVERT_SMI_CHECKED(fast_elements, args[3]);
417 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000418
419 // Check if boilerplate exists. If not, create it first.
420 Handle<Object> boilerplate(literals->get(literals_index));
421 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000422 boilerplate = CreateObjectLiteralBoilerplate(literals,
423 constant_properties,
424 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000425 if (boilerplate.is_null()) return Failure::Exception();
426 // Update the functions literal and return the boilerplate.
427 literals->set(literals_index, *boilerplate);
428 }
429 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
430}
431
432
433static Object* Runtime_CreateArrayLiteral(Arguments args) {
434 HandleScope scope;
435 ASSERT(args.length() == 3);
436 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
437 CONVERT_SMI_CHECKED(literals_index, args[1]);
438 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
439
440 // Check if boilerplate exists. If not, create it first.
441 Handle<Object> boilerplate(literals->get(literals_index));
442 if (*boilerplate == Heap::undefined_value()) {
443 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
444 if (boilerplate.is_null()) return Failure::Exception();
445 // Update the functions literal and return the boilerplate.
446 literals->set(literals_index, *boilerplate);
447 }
448 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
449}
450
451
452static Object* Runtime_CreateArrayLiteralShallow(Arguments args) {
453 HandleScope scope;
454 ASSERT(args.length() == 3);
455 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
456 CONVERT_SMI_CHECKED(literals_index, args[1]);
457 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
458
459 // Check if boilerplate exists. If not, create it first.
460 Handle<Object> boilerplate(literals->get(literals_index));
461 if (*boilerplate == Heap::undefined_value()) {
462 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
463 if (boilerplate.is_null()) return Failure::Exception();
464 // Update the functions literal and return the boilerplate.
465 literals->set(literals_index, *boilerplate);
466 }
467 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
468}
469
470
ager@chromium.org32912102009-01-16 10:38:43 +0000471static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
472 ASSERT(args.length() == 2);
473 CONVERT_CHECKED(String, key, args[0]);
474 Object* value = args[1];
475 // Create a catch context extension object.
476 JSFunction* constructor =
477 Top::context()->global_context()->context_extension_function();
478 Object* object = Heap::AllocateJSObject(constructor);
479 if (object->IsFailure()) return object;
480 // Assign the exception value to the catch variable and make sure
481 // that the catch variable is DontDelete.
482 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
483 if (value->IsFailure()) return value;
484 return object;
485}
486
487
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000488static Object* Runtime_ClassOf(Arguments args) {
489 NoHandleAllocation ha;
490 ASSERT(args.length() == 1);
491 Object* obj = args[0];
492 if (!obj->IsJSObject()) return Heap::null_value();
493 return JSObject::cast(obj)->class_name();
494}
495
ager@chromium.org7c537e22008-10-16 08:43:32 +0000496
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000497static Object* Runtime_IsInPrototypeChain(Arguments args) {
498 NoHandleAllocation ha;
499 ASSERT(args.length() == 2);
500 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
501 Object* O = args[0];
502 Object* V = args[1];
503 while (true) {
504 Object* prototype = V->GetPrototype();
505 if (prototype->IsNull()) return Heap::false_value();
506 if (O == prototype) return Heap::true_value();
507 V = prototype;
508 }
509}
510
511
ager@chromium.org9085a012009-05-11 19:22:57 +0000512// Inserts an object as the hidden prototype of another object.
513static Object* Runtime_SetHiddenPrototype(Arguments args) {
514 NoHandleAllocation ha;
515 ASSERT(args.length() == 2);
516 CONVERT_CHECKED(JSObject, jsobject, args[0]);
517 CONVERT_CHECKED(JSObject, proto, args[1]);
518
519 // Sanity checks. The old prototype (that we are replacing) could
520 // theoretically be null, but if it is not null then check that we
521 // didn't already install a hidden prototype here.
522 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
523 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
524 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
525
526 // Allocate up front before we start altering state in case we get a GC.
527 Object* map_or_failure = proto->map()->CopyDropTransitions();
528 if (map_or_failure->IsFailure()) return map_or_failure;
529 Map* new_proto_map = Map::cast(map_or_failure);
530
531 map_or_failure = jsobject->map()->CopyDropTransitions();
532 if (map_or_failure->IsFailure()) return map_or_failure;
533 Map* new_map = Map::cast(map_or_failure);
534
535 // Set proto's prototype to be the old prototype of the object.
536 new_proto_map->set_prototype(jsobject->GetPrototype());
537 proto->set_map(new_proto_map);
538 new_proto_map->set_is_hidden_prototype();
539
540 // Set the object's prototype to proto.
541 new_map->set_prototype(proto);
542 jsobject->set_map(new_map);
543
544 return Heap::undefined_value();
545}
546
547
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000548static Object* Runtime_IsConstructCall(Arguments args) {
549 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000550 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000551 JavaScriptFrameIterator it;
552 return Heap::ToBoolean(it.frame()->IsConstructor());
553}
554
555
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000556// Recursively traverses hidden prototypes if property is not found
557static void GetOwnPropertyImplementation(JSObject* obj,
558 String* name,
559 LookupResult* result) {
560 obj->LocalLookupRealNamedProperty(name, result);
561
562 if (!result->IsProperty()) {
563 Object* proto = obj->GetPrototype();
564 if (proto->IsJSObject() &&
565 JSObject::cast(proto)->map()->is_hidden_prototype())
566 GetOwnPropertyImplementation(JSObject::cast(proto),
567 name, result);
568 }
569}
570
571
572// Returns an array with the property description:
573// if args[1] is not a property on args[0]
574// returns undefined
575// if args[1] is a data property on args[0]
576// [false, value, Writeable, Enumerable, Configurable]
577// if args[1] is an accessor on args[0]
578// [true, GetFunction, SetFunction, Enumerable, Configurable]
579static Object* Runtime_GetOwnProperty(Arguments args) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000580 ASSERT(args.length() == 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000581 HandleScope scope;
582 Handle<FixedArray> elms = Factory::NewFixedArray(5);
583 Handle<JSArray> desc = Factory::NewJSArrayWithElements(elms);
584 LookupResult result;
585 CONVERT_CHECKED(JSObject, obj, args[0]);
586 CONVERT_CHECKED(String, name, args[1]);
587
588 // Use recursive implementation to also traverse hidden prototypes
589 GetOwnPropertyImplementation(obj, name, &result);
590
591 if (!result.IsProperty())
592 return Heap::undefined_value();
593
594 if (result.type() == CALLBACKS) {
595 Object* structure = result.GetCallbackObject();
ager@chromium.org5c838252010-02-19 08:53:10 +0000596 if (structure->IsProxy() || structure->IsAccessorInfo()) {
597 // Property that is internally implemented as a callback or
598 // an API defined callback.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000599 Object* value = obj->GetPropertyWithCallback(
600 obj, structure, name, result.holder());
601 elms->set(0, Heap::false_value());
602 elms->set(1, value);
603 elms->set(2, Heap::ToBoolean(!result.IsReadOnly()));
604 } else if (structure->IsFixedArray()) {
605 // __defineGetter__/__defineSetter__ callback.
606 elms->set(0, Heap::true_value());
607 elms->set(1, FixedArray::cast(structure)->get(0));
608 elms->set(2, FixedArray::cast(structure)->get(1));
609 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000610 return Heap::undefined_value();
611 }
612 } else {
613 elms->set(0, Heap::false_value());
614 elms->set(1, result.GetLazyValue());
615 elms->set(2, Heap::ToBoolean(!result.IsReadOnly()));
616 }
617
618 elms->set(3, Heap::ToBoolean(!result.IsDontEnum()));
ager@chromium.org5c838252010-02-19 08:53:10 +0000619 elms->set(4, Heap::ToBoolean(!result.IsDontDelete()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000620 return *desc;
621}
622
623
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000624static Object* Runtime_IsExtensible(Arguments args) {
625 ASSERT(args.length() == 1);
626 CONVERT_CHECKED(JSObject, obj, args[0]);
627 return obj->map()->is_extensible() ? Heap::true_value()
628 : Heap::false_value();
629}
630
631
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000632static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000633 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000634 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000635 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
636 CONVERT_ARG_CHECKED(String, pattern, 1);
637 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000638 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
639 if (result.is_null()) return Failure::Exception();
640 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000641}
642
643
644static Object* Runtime_CreateApiFunction(Arguments args) {
645 HandleScope scope;
646 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000647 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000648 return *Factory::CreateApiFunction(data);
649}
650
651
652static Object* Runtime_IsTemplate(Arguments args) {
653 ASSERT(args.length() == 1);
654 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000655 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000656 return Heap::ToBoolean(result);
657}
658
659
660static Object* Runtime_GetTemplateField(Arguments args) {
661 ASSERT(args.length() == 2);
662 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000663 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000664 int index = field->value();
665 int offset = index * kPointerSize + HeapObject::kHeaderSize;
666 InstanceType type = templ->map()->instance_type();
667 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
668 type == OBJECT_TEMPLATE_INFO_TYPE);
669 RUNTIME_ASSERT(offset > 0);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000670 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000671 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
672 } else {
673 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
674 }
675 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000676}
677
678
ager@chromium.org870a0b62008-11-04 11:43:05 +0000679static Object* Runtime_DisableAccessChecks(Arguments args) {
680 ASSERT(args.length() == 1);
681 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000682 Map* old_map = object->map();
683 bool needs_access_checks = old_map->is_access_check_needed();
684 if (needs_access_checks) {
685 // Copy map so it won't interfere constructor's initial map.
686 Object* new_map = old_map->CopyDropTransitions();
687 if (new_map->IsFailure()) return new_map;
688
689 Map::cast(new_map)->set_is_access_check_needed(false);
690 object->set_map(Map::cast(new_map));
691 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000692 return needs_access_checks ? Heap::true_value() : Heap::false_value();
693}
694
695
696static Object* Runtime_EnableAccessChecks(Arguments args) {
697 ASSERT(args.length() == 1);
698 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000699 Map* old_map = object->map();
700 if (!old_map->is_access_check_needed()) {
701 // Copy map so it won't interfere constructor's initial map.
702 Object* new_map = old_map->CopyDropTransitions();
703 if (new_map->IsFailure()) return new_map;
704
705 Map::cast(new_map)->set_is_access_check_needed(true);
706 object->set_map(Map::cast(new_map));
707 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000708 return Heap::undefined_value();
709}
710
711
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000712static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
713 HandleScope scope;
714 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
715 Handle<Object> args[2] = { type_handle, name };
716 Handle<Object> error =
717 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
718 return Top::Throw(*error);
719}
720
721
722static Object* Runtime_DeclareGlobals(Arguments args) {
723 HandleScope scope;
724 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
725
ager@chromium.org3811b432009-10-28 14:53:37 +0000726 Handle<Context> context = args.at<Context>(0);
727 CONVERT_ARG_CHECKED(FixedArray, pairs, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000728 bool is_eval = Smi::cast(args[2])->value() == 1;
729
730 // Compute the property attributes. According to ECMA-262, section
731 // 13, page 71, the property must be read-only and
732 // non-deletable. However, neither SpiderMonkey nor KJS creates the
733 // property as read-only, so we don't either.
734 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
735
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000736 // Traverse the name/value pairs and set the properties.
737 int length = pairs->length();
738 for (int i = 0; i < length; i += 2) {
739 HandleScope scope;
740 Handle<String> name(String::cast(pairs->get(i)));
741 Handle<Object> value(pairs->get(i + 1));
742
743 // We have to declare a global const property. To capture we only
744 // assign to it when evaluating the assignment for "const x =
745 // <expr>" the initial value is the hole.
746 bool is_const_property = value->IsTheHole();
747
748 if (value->IsUndefined() || is_const_property) {
749 // Lookup the property in the global object, and don't set the
750 // value of the variable if the property is already there.
751 LookupResult lookup;
752 global->Lookup(*name, &lookup);
753 if (lookup.IsProperty()) {
754 // Determine if the property is local by comparing the holder
755 // against the global object. The information will be used to
756 // avoid throwing re-declaration errors when declaring
757 // variables or constants that exist in the prototype chain.
758 bool is_local = (*global == lookup.holder());
759 // Get the property attributes and determine if the property is
760 // read-only.
761 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
762 bool is_read_only = (attributes & READ_ONLY) != 0;
763 if (lookup.type() == INTERCEPTOR) {
764 // If the interceptor says the property is there, we
765 // just return undefined without overwriting the property.
766 // Otherwise, we continue to setting the property.
767 if (attributes != ABSENT) {
768 // Check if the existing property conflicts with regards to const.
769 if (is_local && (is_read_only || is_const_property)) {
770 const char* type = (is_read_only) ? "const" : "var";
771 return ThrowRedeclarationError(type, name);
772 };
773 // The property already exists without conflicting: Go to
774 // the next declaration.
775 continue;
776 }
777 // Fall-through and introduce the absent property by using
778 // SetProperty.
779 } else {
780 if (is_local && (is_read_only || is_const_property)) {
781 const char* type = (is_read_only) ? "const" : "var";
782 return ThrowRedeclarationError(type, name);
783 }
784 // The property already exists without conflicting: Go to
785 // the next declaration.
786 continue;
787 }
788 }
789 } else {
790 // Copy the function and update its context. Use it as value.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000791 Handle<SharedFunctionInfo> shared =
792 Handle<SharedFunctionInfo>::cast(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000793 Handle<JSFunction> function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000794 Factory::NewFunctionFromSharedFunctionInfo(shared, context, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000795 value = function;
796 }
797
798 LookupResult lookup;
799 global->LocalLookup(*name, &lookup);
800
801 PropertyAttributes attributes = is_const_property
802 ? static_cast<PropertyAttributes>(base | READ_ONLY)
803 : base;
804
805 if (lookup.IsProperty()) {
806 // There's a local property that we need to overwrite because
807 // we're either declaring a function or there's an interceptor
808 // that claims the property is absent.
809
810 // Check for conflicting re-declarations. We cannot have
811 // conflicting types in case of intercepted properties because
812 // they are absent.
813 if (lookup.type() != INTERCEPTOR &&
814 (lookup.IsReadOnly() || is_const_property)) {
815 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
816 return ThrowRedeclarationError(type, name);
817 }
818 SetProperty(global, name, value, attributes);
819 } else {
820 // If a property with this name does not already exist on the
821 // global object add the property locally. We take special
822 // precautions to always add it as a local property even in case
823 // of callbacks in the prototype chain (this rules out using
824 // SetProperty). Also, we must use the handle-based version to
825 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000826 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000827 }
828 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000829
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000830 return Heap::undefined_value();
831}
832
833
834static Object* Runtime_DeclareContextSlot(Arguments args) {
835 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000836 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000837
ager@chromium.org7c537e22008-10-16 08:43:32 +0000838 CONVERT_ARG_CHECKED(Context, context, 0);
839 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000840 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000841 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000842 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000843 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000844
845 // Declarations are always done in the function context.
846 context = Handle<Context>(context->fcontext());
847
848 int index;
849 PropertyAttributes attributes;
850 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000851 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000852 context->Lookup(name, flags, &index, &attributes);
853
854 if (attributes != ABSENT) {
855 // The name was declared before; check for conflicting
856 // re-declarations: This is similar to the code in parser.cc in
857 // the AstBuildingParser::Declare function.
858 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
859 // Functions are not read-only.
860 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
861 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
862 return ThrowRedeclarationError(type, name);
863 }
864
865 // Initialize it if necessary.
866 if (*initial_value != NULL) {
867 if (index >= 0) {
868 // The variable or constant context slot should always be in
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000869 // the function context or the arguments object.
870 if (holder->IsContext()) {
871 ASSERT(holder.is_identical_to(context));
872 if (((attributes & READ_ONLY) == 0) ||
873 context->get(index)->IsTheHole()) {
874 context->set(index, *initial_value);
875 }
876 } else {
877 Handle<JSObject>::cast(holder)->SetElement(index, *initial_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000878 }
879 } else {
880 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000881 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000882 SetProperty(context_ext, name, initial_value, mode);
883 }
884 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000885
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000886 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000887 // The property is not in the function context. It needs to be
888 // "declared" in the function context's extension context, or in the
889 // global context.
890 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000891 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000892 // The function context's extension context exists - use it.
893 context_ext = Handle<JSObject>(context->extension());
894 } else {
895 // The function context's extension context does not exists - allocate
896 // it.
897 context_ext = Factory::NewJSObject(Top::context_extension_function());
898 // And store it in the extension slot.
899 context->set_extension(*context_ext);
900 }
901 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000902
ager@chromium.org7c537e22008-10-16 08:43:32 +0000903 // Declare the property by setting it to the initial value if provided,
904 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
905 // constant declarations).
906 ASSERT(!context_ext->HasLocalProperty(*name));
907 Handle<Object> value(Heap::undefined_value());
908 if (*initial_value != NULL) value = initial_value;
909 SetProperty(context_ext, name, value, mode);
910 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
911 }
912
913 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000914}
915
916
917static Object* Runtime_InitializeVarGlobal(Arguments args) {
918 NoHandleAllocation nha;
919
920 // Determine if we need to assign to the variable if it already
921 // exists (based on the number of arguments).
922 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
923 bool assign = args.length() == 2;
924
925 CONVERT_ARG_CHECKED(String, name, 0);
926 GlobalObject* global = Top::context()->global();
927
928 // According to ECMA-262, section 12.2, page 62, the property must
929 // not be deletable.
930 PropertyAttributes attributes = DONT_DELETE;
931
932 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000933 // there, there is a property with this name in the prototype chain.
934 // We follow Safari and Firefox behavior and only set the property
935 // locally if there is an explicit initialization value that we have
936 // to assign to the property. When adding the property we take
937 // special precautions to always add it as a local property even in
938 // case of callbacks in the prototype chain (this rules out using
939 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
940 // this.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000941 // Note that objects can have hidden prototypes, so we need to traverse
942 // the whole chain of hidden prototypes to do a 'local' lookup.
943 JSObject* real_holder = global;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000944 LookupResult lookup;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000945 while (true) {
946 real_holder->LocalLookup(*name, &lookup);
947 if (lookup.IsProperty()) {
948 // Determine if this is a redeclaration of something read-only.
949 if (lookup.IsReadOnly()) {
950 // If we found readonly property on one of hidden prototypes,
951 // just shadow it.
952 if (real_holder != Top::context()->global()) break;
953 return ThrowRedeclarationError("const", name);
954 }
955
956 // Determine if this is a redeclaration of an intercepted read-only
957 // property and figure out if the property exists at all.
958 bool found = true;
959 PropertyType type = lookup.type();
960 if (type == INTERCEPTOR) {
961 HandleScope handle_scope;
962 Handle<JSObject> holder(real_holder);
963 PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
964 real_holder = *holder;
965 if (intercepted == ABSENT) {
966 // The interceptor claims the property isn't there. We need to
967 // make sure to introduce it.
968 found = false;
969 } else if ((intercepted & READ_ONLY) != 0) {
970 // The property is present, but read-only. Since we're trying to
971 // overwrite it with a variable declaration we must throw a
972 // re-declaration error. However if we found readonly property
973 // on one of hidden prototypes, just shadow it.
974 if (real_holder != Top::context()->global()) break;
975 return ThrowRedeclarationError("const", name);
976 }
977 }
978
979 if (found && !assign) {
980 // The global property is there and we're not assigning any value
981 // to it. Just return.
982 return Heap::undefined_value();
983 }
984
985 // Assign the value (or undefined) to the property.
986 Object* value = (assign) ? args[1] : Heap::undefined_value();
987 return real_holder->SetProperty(&lookup, *name, value, attributes);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000988 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000989
990 Object* proto = real_holder->GetPrototype();
991 if (!proto->IsJSObject())
992 break;
993
994 if (!JSObject::cast(proto)->map()->is_hidden_prototype())
995 break;
996
997 real_holder = JSObject::cast(proto);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000998 }
999
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001000 global = Top::context()->global();
1001 if (assign) {
1002 return global->IgnoreAttributesAndSetLocalProperty(*name,
1003 args[1],
1004 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001005 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001006 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001007}
1008
1009
1010static Object* Runtime_InitializeConstGlobal(Arguments args) {
1011 // All constants are declared with an initial value. The name
1012 // of the constant is the first argument and the initial value
1013 // is the second.
1014 RUNTIME_ASSERT(args.length() == 2);
1015 CONVERT_ARG_CHECKED(String, name, 0);
1016 Handle<Object> value = args.at<Object>(1);
1017
1018 // Get the current global object from top.
1019 GlobalObject* global = Top::context()->global();
1020
1021 // According to ECMA-262, section 12.2, page 62, the property must
1022 // not be deletable. Since it's a const, it must be READ_ONLY too.
1023 PropertyAttributes attributes =
1024 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
1025
1026 // Lookup the property locally in the global object. If it isn't
1027 // there, we add the property and take special precautions to always
1028 // add it as a local property even in case of callbacks in the
1029 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001030 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001031 LookupResult lookup;
1032 global->LocalLookup(*name, &lookup);
1033 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001034 return global->IgnoreAttributesAndSetLocalProperty(*name,
1035 *value,
1036 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001037 }
1038
1039 // Determine if this is a redeclaration of something not
1040 // read-only. In case the result is hidden behind an interceptor we
1041 // need to ask it for the property attributes.
1042 if (!lookup.IsReadOnly()) {
1043 if (lookup.type() != INTERCEPTOR) {
1044 return ThrowRedeclarationError("var", name);
1045 }
1046
1047 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
1048
1049 // Throw re-declaration error if the intercepted property is present
1050 // but not read-only.
1051 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
1052 return ThrowRedeclarationError("var", name);
1053 }
1054
1055 // Restore global object from context (in case of GC) and continue
1056 // with setting the value because the property is either absent or
1057 // read-only. We also have to do redo the lookup.
1058 global = Top::context()->global();
1059
1060 // BUG 1213579: Handle the case where we have to set a read-only
1061 // property through an interceptor and only do it if it's
1062 // uninitialized, e.g. the hole. Nirk...
1063 global->SetProperty(*name, *value, attributes);
1064 return *value;
1065 }
1066
1067 // Set the value, but only we're assigning the initial value to a
1068 // constant. For now, we determine this by checking if the
1069 // current value is the hole.
1070 PropertyType type = lookup.type();
1071 if (type == FIELD) {
1072 FixedArray* properties = global->properties();
1073 int index = lookup.GetFieldIndex();
1074 if (properties->get(index)->IsTheHole()) {
1075 properties->set(index, *value);
1076 }
1077 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001078 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
1079 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001080 }
1081 } else {
1082 // Ignore re-initialization of constants that have already been
1083 // assigned a function value.
1084 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
1085 }
1086
1087 // Use the set value as the result of the operation.
1088 return *value;
1089}
1090
1091
1092static Object* Runtime_InitializeConstContextSlot(Arguments args) {
1093 HandleScope scope;
1094 ASSERT(args.length() == 3);
1095
1096 Handle<Object> value(args[0]);
1097 ASSERT(!value->IsTheHole());
1098 CONVERT_ARG_CHECKED(Context, context, 1);
1099 Handle<String> name(String::cast(args[2]));
1100
1101 // Initializations are always done in the function context.
1102 context = Handle<Context>(context->fcontext());
1103
1104 int index;
1105 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001106 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001107 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001108 context->Lookup(name, flags, &index, &attributes);
1109
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001110 // In most situations, the property introduced by the const
1111 // declaration should be present in the context extension object.
1112 // However, because declaration and initialization are separate, the
1113 // property might have been deleted (if it was introduced by eval)
1114 // before we reach the initialization point.
1115 //
1116 // Example:
1117 //
1118 // function f() { eval("delete x; const x;"); }
1119 //
1120 // In that case, the initialization behaves like a normal assignment
1121 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001122 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001123 // Property was found in a context.
1124 if (holder->IsContext()) {
1125 // The holder cannot be the function context. If it is, there
1126 // should have been a const redeclaration error when declaring
1127 // the const property.
1128 ASSERT(!holder.is_identical_to(context));
1129 if ((attributes & READ_ONLY) == 0) {
1130 Handle<Context>::cast(holder)->set(index, *value);
1131 }
1132 } else {
1133 // The holder is an arguments object.
1134 ASSERT((attributes & READ_ONLY) == 0);
1135 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001136 }
1137 return *value;
1138 }
1139
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001140 // The property could not be found, we introduce it in the global
1141 // context.
1142 if (attributes == ABSENT) {
1143 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1144 SetProperty(global, name, value, NONE);
1145 return *value;
1146 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001147
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001148 // The property was present in a context extension object.
1149 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001150
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001151 if (*context_ext == context->extension()) {
1152 // This is the property that was introduced by the const
1153 // declaration. Set it if it hasn't been set before. NOTE: We
1154 // cannot use GetProperty() to get the current value as it
1155 // 'unholes' the value.
1156 LookupResult lookup;
1157 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1158 ASSERT(lookup.IsProperty()); // the property was declared
1159 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1160
1161 PropertyType type = lookup.type();
1162 if (type == FIELD) {
1163 FixedArray* properties = context_ext->properties();
1164 int index = lookup.GetFieldIndex();
1165 if (properties->get(index)->IsTheHole()) {
1166 properties->set(index, *value);
1167 }
1168 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001169 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
1170 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001171 }
1172 } else {
1173 // We should not reach here. Any real, named property should be
1174 // either a field or a dictionary slot.
1175 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001176 }
1177 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001178 // The property was found in a different context extension object.
1179 // Set it if it is not a read-only property.
1180 if ((attributes & READ_ONLY) == 0) {
1181 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1182 // Setting a property might throw an exception. Exceptions
1183 // are converted to empty handles in handle operations. We
1184 // need to convert back to exceptions here.
1185 if (set.is_null()) {
1186 ASSERT(Top::has_pending_exception());
1187 return Failure::Exception();
1188 }
1189 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001190 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001191
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001192 return *value;
1193}
1194
1195
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001196static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1197 Arguments args) {
1198 HandleScope scope;
1199 ASSERT(args.length() == 2);
1200 CONVERT_ARG_CHECKED(JSObject, object, 0);
1201 CONVERT_SMI_CHECKED(properties, args[1]);
1202 if (object->HasFastProperties()) {
1203 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1204 }
1205 return *object;
1206}
1207
1208
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001209static Object* Runtime_RegExpExec(Arguments args) {
1210 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001211 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001212 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1213 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001214 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001215 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001216 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001217 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001218 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001219 RUNTIME_ASSERT(index >= 0);
1220 RUNTIME_ASSERT(index <= subject->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001221 Counters::regexp_entry_runtime.Increment();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001222 Handle<Object> result = RegExpImpl::Exec(regexp,
1223 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001224 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001225 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001226 if (result.is_null()) return Failure::Exception();
1227 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001228}
1229
1230
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00001231static Object* Runtime_RegExpConstructResult(Arguments args) {
1232 ASSERT(args.length() == 3);
1233 CONVERT_SMI_CHECKED(elements_count, args[0]);
1234 if (elements_count > JSArray::kMaxFastElementsLength) {
1235 return Top::ThrowIllegalOperation();
1236 }
1237 Object* new_object = Heap::AllocateFixedArrayWithHoles(elements_count);
1238 if (new_object->IsFailure()) return new_object;
1239 FixedArray* elements = FixedArray::cast(new_object);
1240 new_object = Heap::AllocateRaw(JSRegExpResult::kSize,
1241 NEW_SPACE,
1242 OLD_POINTER_SPACE);
1243 if (new_object->IsFailure()) return new_object;
1244 {
1245 AssertNoAllocation no_gc;
1246 HandleScope scope;
1247 reinterpret_cast<HeapObject*>(new_object)->
1248 set_map(Top::global_context()->regexp_result_map());
1249 }
1250 JSArray* array = JSArray::cast(new_object);
1251 array->set_properties(Heap::empty_fixed_array());
1252 array->set_elements(elements);
1253 array->set_length(Smi::FromInt(elements_count));
1254 // Write in-object properties after the length of the array.
1255 array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, args[1]);
1256 array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, args[2]);
1257 return array;
1258}
1259
1260
lrn@chromium.org25156de2010-04-06 13:10:27 +00001261static Object* Runtime_RegExpInitializeObject(Arguments args) {
1262 AssertNoAllocation no_alloc;
1263 ASSERT(args.length() == 5);
1264 CONVERT_CHECKED(JSRegExp, regexp, args[0]);
1265 CONVERT_CHECKED(String, source, args[1]);
1266
1267 Object* global = args[2];
1268 if (!global->IsTrue()) global = Heap::false_value();
1269
1270 Object* ignoreCase = args[3];
1271 if (!ignoreCase->IsTrue()) ignoreCase = Heap::false_value();
1272
1273 Object* multiline = args[4];
1274 if (!multiline->IsTrue()) multiline = Heap::false_value();
1275
1276 Map* map = regexp->map();
1277 Object* constructor = map->constructor();
1278 if (constructor->IsJSFunction() &&
1279 JSFunction::cast(constructor)->initial_map() == map) {
1280 // If we still have the original map, set in-object properties directly.
1281 regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, source);
1282 // TODO(lrn): Consider skipping write barrier on booleans as well.
1283 // Both true and false should be in oldspace at all times.
1284 regexp->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, global);
1285 regexp->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex, ignoreCase);
1286 regexp->InObjectPropertyAtPut(JSRegExp::kMultilineFieldIndex, multiline);
1287 regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex,
1288 Smi::FromInt(0),
1289 SKIP_WRITE_BARRIER);
1290 return regexp;
1291 }
1292
1293 // Map has changed, so use generic, but slower, method.
1294 PropertyAttributes final =
1295 static_cast<PropertyAttributes>(READ_ONLY | DONT_ENUM | DONT_DELETE);
1296 PropertyAttributes writable =
1297 static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
1298 regexp->IgnoreAttributesAndSetLocalProperty(Heap::source_symbol(),
1299 source,
1300 final);
1301 regexp->IgnoreAttributesAndSetLocalProperty(Heap::global_symbol(),
1302 global,
1303 final);
1304 regexp->IgnoreAttributesAndSetLocalProperty(Heap::ignore_case_symbol(),
1305 ignoreCase,
1306 final);
1307 regexp->IgnoreAttributesAndSetLocalProperty(Heap::multiline_symbol(),
1308 multiline,
1309 final);
1310 regexp->IgnoreAttributesAndSetLocalProperty(Heap::last_index_symbol(),
1311 Smi::FromInt(0),
1312 writable);
1313 return regexp;
1314}
1315
1316
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001317static Object* Runtime_FinishArrayPrototypeSetup(Arguments args) {
1318 HandleScope scope;
1319 ASSERT(args.length() == 1);
1320 CONVERT_ARG_CHECKED(JSArray, prototype, 0);
1321 // This is necessary to enable fast checks for absence of elements
1322 // on Array.prototype and below.
1323 prototype->set_elements(Heap::empty_fixed_array());
1324 return Smi::FromInt(0);
1325}
1326
1327
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001328static void SetCustomCallGenerator(Handle<JSFunction> function,
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001329 ExternalReference* generator) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001330 if (function->shared()->function_data()->IsUndefined()) {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001331 function->shared()->set_function_data(*FromCData(generator->address()));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001332 }
1333}
1334
1335
1336static Handle<JSFunction> InstallBuiltin(Handle<JSObject> holder,
1337 const char* name,
1338 Builtins::Name builtin_name,
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001339 ExternalReference* generator = NULL) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001340 Handle<String> key = Factory::LookupAsciiSymbol(name);
1341 Handle<Code> code(Builtins::builtin(builtin_name));
1342 Handle<JSFunction> optimized = Factory::NewFunction(key,
1343 JS_OBJECT_TYPE,
1344 JSObject::kHeaderSize,
1345 code,
1346 false);
1347 optimized->shared()->DontAdaptArguments();
1348 if (generator != NULL) {
1349 SetCustomCallGenerator(optimized, generator);
1350 }
1351 SetProperty(holder, key, optimized, NONE);
1352 return optimized;
1353}
1354
1355
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001356Object* CompileArrayPushCall(CallStubCompiler* compiler,
1357 Object* object,
1358 JSObject* holder,
1359 JSFunction* function,
1360 String* name,
1361 StubCompiler::CheckType check) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001362 return compiler->CompileArrayPushCall(object, holder, function, name, check);
1363}
1364
1365
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001366Object* CompileArrayPopCall(CallStubCompiler* compiler,
1367 Object* object,
1368 JSObject* holder,
1369 JSFunction* function,
1370 String* name,
1371 StubCompiler::CheckType check) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001372 return compiler->CompileArrayPopCall(object, holder, function, name, check);
1373}
1374
1375
1376static Object* Runtime_SpecialArrayFunctions(Arguments args) {
1377 HandleScope scope;
1378 ASSERT(args.length() == 1);
1379 CONVERT_ARG_CHECKED(JSObject, holder, 0);
1380
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001381 ExternalReference pop = ExternalReference::compile_array_pop_call();
1382 ExternalReference push = ExternalReference::compile_array_push_call();
1383
1384 InstallBuiltin(holder, "pop", Builtins::ArrayPop, &pop);
1385 InstallBuiltin(holder, "push", Builtins::ArrayPush, &push);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001386 InstallBuiltin(holder, "shift", Builtins::ArrayShift);
1387 InstallBuiltin(holder, "unshift", Builtins::ArrayUnshift);
1388 InstallBuiltin(holder, "slice", Builtins::ArraySlice);
1389 InstallBuiltin(holder, "splice", Builtins::ArraySplice);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001390 InstallBuiltin(holder, "concat", Builtins::ArrayConcat);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001391
1392 return *holder;
1393}
1394
1395
ager@chromium.org357bf652010-04-12 11:30:10 +00001396static Object* Runtime_GetGlobalReceiver(Arguments args) {
1397 // Returns a real global receiver, not one of builtins object.
1398 Context* global_context = Top::context()->global()->global_context();
1399 return global_context->global()->global_receiver();
1400}
1401
1402
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001403static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1404 HandleScope scope;
1405 ASSERT(args.length() == 4);
1406 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1407 int index = Smi::cast(args[1])->value();
1408 Handle<String> pattern = args.at<String>(2);
1409 Handle<String> flags = args.at<String>(3);
1410
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001411 // Get the RegExp function from the context in the literals array.
1412 // This is the RegExp function from the context in which the
1413 // function was created. We do not use the RegExp function from the
1414 // current global context because this might be the RegExp function
1415 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001416 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001417 Handle<JSFunction>(
1418 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001419 // Compute the regular expression literal.
1420 bool has_pending_exception;
1421 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001422 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1423 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001424 if (has_pending_exception) {
1425 ASSERT(Top::has_pending_exception());
1426 return Failure::Exception();
1427 }
1428 literals->set(index, *regexp);
1429 return *regexp;
1430}
1431
1432
1433static Object* Runtime_FunctionGetName(Arguments args) {
1434 NoHandleAllocation ha;
1435 ASSERT(args.length() == 1);
1436
1437 CONVERT_CHECKED(JSFunction, f, args[0]);
1438 return f->shared()->name();
1439}
1440
1441
ager@chromium.org236ad962008-09-25 09:45:57 +00001442static Object* Runtime_FunctionSetName(Arguments args) {
1443 NoHandleAllocation ha;
1444 ASSERT(args.length() == 2);
1445
1446 CONVERT_CHECKED(JSFunction, f, args[0]);
1447 CONVERT_CHECKED(String, name, args[1]);
1448 f->shared()->set_name(name);
1449 return Heap::undefined_value();
1450}
1451
1452
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001453static Object* Runtime_FunctionRemovePrototype(Arguments args) {
1454 NoHandleAllocation ha;
1455 ASSERT(args.length() == 1);
1456
1457 CONVERT_CHECKED(JSFunction, f, args[0]);
1458 Object* obj = f->RemovePrototype();
1459 if (obj->IsFailure()) return obj;
1460
1461 return Heap::undefined_value();
1462}
1463
1464
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001465static Object* Runtime_FunctionGetScript(Arguments args) {
1466 HandleScope scope;
1467 ASSERT(args.length() == 1);
1468
1469 CONVERT_CHECKED(JSFunction, fun, args[0]);
1470 Handle<Object> script = Handle<Object>(fun->shared()->script());
1471 if (!script->IsScript()) return Heap::undefined_value();
1472
1473 return *GetScriptWrapper(Handle<Script>::cast(script));
1474}
1475
1476
1477static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1478 NoHandleAllocation ha;
1479 ASSERT(args.length() == 1);
1480
1481 CONVERT_CHECKED(JSFunction, f, args[0]);
1482 return f->shared()->GetSourceCode();
1483}
1484
1485
1486static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1487 NoHandleAllocation ha;
1488 ASSERT(args.length() == 1);
1489
1490 CONVERT_CHECKED(JSFunction, fun, args[0]);
1491 int pos = fun->shared()->start_position();
1492 return Smi::FromInt(pos);
1493}
1494
1495
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001496static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1497 ASSERT(args.length() == 2);
1498
1499 CONVERT_CHECKED(JSFunction, fun, args[0]);
1500 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1501
1502 Code* code = fun->code();
1503 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1504
1505 Address pc = code->address() + offset;
1506 return Smi::FromInt(fun->code()->SourcePosition(pc));
1507}
1508
1509
1510
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001511static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1512 NoHandleAllocation ha;
1513 ASSERT(args.length() == 2);
1514
1515 CONVERT_CHECKED(JSFunction, fun, args[0]);
1516 CONVERT_CHECKED(String, name, args[1]);
1517 fun->SetInstanceClassName(name);
1518 return Heap::undefined_value();
1519}
1520
1521
1522static Object* Runtime_FunctionSetLength(Arguments args) {
1523 NoHandleAllocation ha;
1524 ASSERT(args.length() == 2);
1525
1526 CONVERT_CHECKED(JSFunction, fun, args[0]);
1527 CONVERT_CHECKED(Smi, length, args[1]);
1528 fun->shared()->set_length(length->value());
1529 return length;
1530}
1531
1532
1533static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001534 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001535 ASSERT(args.length() == 2);
1536
1537 CONVERT_CHECKED(JSFunction, fun, args[0]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001538 ASSERT(fun->should_have_prototype());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001539 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1540 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001541 return args[0]; // return TOS
1542}
1543
1544
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001545static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1546 NoHandleAllocation ha;
1547 ASSERT(args.length() == 1);
1548
1549 CONVERT_CHECKED(JSFunction, f, args[0]);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001550 return f->shared()->IsApiFunction() ? Heap::true_value()
1551 : Heap::false_value();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001552}
1553
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001554static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1555 NoHandleAllocation ha;
1556 ASSERT(args.length() == 1);
1557
1558 CONVERT_CHECKED(JSFunction, f, args[0]);
1559 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1560}
1561
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001562
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001563static Object* Runtime_SetCode(Arguments args) {
1564 HandleScope scope;
1565 ASSERT(args.length() == 2);
1566
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001567 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001568 Handle<Object> code = args.at<Object>(1);
1569
1570 Handle<Context> context(target->context());
1571
1572 if (!code->IsNull()) {
1573 RUNTIME_ASSERT(code->IsJSFunction());
1574 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001575 Handle<SharedFunctionInfo> shared(fun->shared());
1576 SetExpectedNofProperties(target, shared->expected_nof_properties());
1577
1578 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001579 return Failure::Exception();
1580 }
1581 // Set the code, formal parameter count, and the length of the target
1582 // function.
1583 target->set_code(fun->code());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001584 target->shared()->set_length(shared->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001585 target->shared()->set_formal_parameter_count(
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001586 shared->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001587 // Set the source code of the target function to undefined.
1588 // SetCode is only used for built-in constructors like String,
1589 // Array, and Object, and some web code
1590 // doesn't like seeing source code for constructors.
1591 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001592 // Clear the optimization hints related to the compiled code as these are no
1593 // longer valid when the code is overwritten.
1594 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001595 context = Handle<Context>(fun->context());
1596
1597 // Make sure we get a fresh copy of the literal vector to avoid
1598 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001599 int number_of_literals = fun->NumberOfLiterals();
1600 Handle<FixedArray> literals =
1601 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001602 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001603 // Insert the object, regexp and array functions in the literals
1604 // array prefix. These are the functions that will be used when
1605 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001606 literals->set(JSFunction::kLiteralGlobalContextIndex,
1607 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001608 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001609 // It's okay to skip the write barrier here because the literals
1610 // are guaranteed to be in old space.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001611 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001612 }
1613
1614 target->set_context(*context);
1615 return *target;
1616}
1617
1618
1619static Object* CharCodeAt(String* subject, Object* index) {
1620 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001621 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001622 // Flatten the string. If someone wants to get a char at an index
1623 // in a cons string, it is likely that more indices will be
1624 // accessed.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001625 Object* flat = subject->TryFlatten();
1626 if (flat->IsFailure()) return flat;
1627 subject = String::cast(flat);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001628 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001629 return Heap::nan_value();
1630 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001631 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001632}
1633
1634
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001635static Object* CharFromCode(Object* char_code) {
1636 uint32_t code;
1637 if (Array::IndexFromObject(char_code, &code)) {
1638 if (code <= 0xffff) {
1639 return Heap::LookupSingleCharacterStringFromCode(code);
1640 }
1641 }
1642 return Heap::empty_string();
1643}
1644
1645
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001646static Object* Runtime_StringCharCodeAt(Arguments args) {
1647 NoHandleAllocation ha;
1648 ASSERT(args.length() == 2);
1649
1650 CONVERT_CHECKED(String, subject, args[0]);
1651 Object* index = args[1];
1652 return CharCodeAt(subject, index);
1653}
1654
1655
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001656static Object* Runtime_StringCharAt(Arguments args) {
1657 NoHandleAllocation ha;
1658 ASSERT(args.length() == 2);
1659
1660 CONVERT_CHECKED(String, subject, args[0]);
1661 Object* index = args[1];
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001662 Object* code = CharCodeAt(subject, index);
1663 if (code == Heap::nan_value()) {
1664 return Heap::undefined_value();
1665 }
1666 return CharFromCode(code);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001667}
1668
1669
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001670static Object* Runtime_CharFromCode(Arguments args) {
1671 NoHandleAllocation ha;
1672 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001673 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001674}
1675
lrn@chromium.org25156de2010-04-06 13:10:27 +00001676
1677class FixedArrayBuilder {
1678 public:
1679 explicit FixedArrayBuilder(int initial_capacity)
1680 : array_(Factory::NewFixedArrayWithHoles(initial_capacity)),
1681 length_(0) {
1682 // Require a non-zero initial size. Ensures that doubling the size to
1683 // extend the array will work.
1684 ASSERT(initial_capacity > 0);
1685 }
1686
1687 explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
1688 : array_(backing_store),
1689 length_(0) {
1690 // Require a non-zero initial size. Ensures that doubling the size to
1691 // extend the array will work.
1692 ASSERT(backing_store->length() > 0);
1693 }
1694
1695 bool HasCapacity(int elements) {
1696 int length = array_->length();
1697 int required_length = length_ + elements;
1698 return (length >= required_length);
1699 }
1700
1701 void EnsureCapacity(int elements) {
1702 int length = array_->length();
1703 int required_length = length_ + elements;
1704 if (length < required_length) {
1705 int new_length = length;
1706 do {
1707 new_length *= 2;
1708 } while (new_length < required_length);
1709 Handle<FixedArray> extended_array =
1710 Factory::NewFixedArrayWithHoles(new_length);
1711 array_->CopyTo(0, *extended_array, 0, length_);
1712 array_ = extended_array;
1713 }
1714 }
1715
1716 void Add(Object* value) {
1717 ASSERT(length_ < capacity());
1718 array_->set(length_, value);
1719 length_++;
1720 }
1721
1722 void Add(Smi* value) {
1723 ASSERT(length_ < capacity());
1724 array_->set(length_, value);
1725 length_++;
1726 }
1727
1728 Handle<FixedArray> array() {
1729 return array_;
1730 }
1731
1732 int length() {
1733 return length_;
1734 }
1735
1736 int capacity() {
1737 return array_->length();
1738 }
1739
1740 Handle<JSArray> ToJSArray() {
1741 Handle<JSArray> result_array = Factory::NewJSArrayWithElements(array_);
1742 result_array->set_length(Smi::FromInt(length_));
1743 return result_array;
1744 }
1745
1746 Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
1747 target_array->set_elements(*array_);
1748 target_array->set_length(Smi::FromInt(length_));
1749 return target_array;
1750 }
1751
1752 private:
1753 Handle<FixedArray> array_;
1754 int length_;
1755};
1756
1757
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001758// Forward declarations.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001759const int kStringBuilderConcatHelperLengthBits = 11;
1760const int kStringBuilderConcatHelperPositionBits = 19;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001761
1762template <typename schar>
1763static inline void StringBuilderConcatHelper(String*,
1764 schar*,
1765 FixedArray*,
1766 int);
1767
lrn@chromium.org25156de2010-04-06 13:10:27 +00001768typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
1769 StringBuilderSubstringLength;
1770typedef BitField<int,
1771 kStringBuilderConcatHelperLengthBits,
1772 kStringBuilderConcatHelperPositionBits>
1773 StringBuilderSubstringPosition;
1774
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001775
1776class ReplacementStringBuilder {
1777 public:
1778 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
lrn@chromium.org25156de2010-04-06 13:10:27 +00001779 : array_builder_(estimated_part_count),
1780 subject_(subject),
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001781 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001782 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001783 // Require a non-zero initial size. Ensures that doubling the size to
1784 // extend the array will work.
1785 ASSERT(estimated_part_count > 0);
1786 }
1787
lrn@chromium.org25156de2010-04-06 13:10:27 +00001788 static inline void AddSubjectSlice(FixedArrayBuilder* builder,
1789 int from,
1790 int to) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001791 ASSERT(from >= 0);
1792 int length = to - from;
1793 ASSERT(length > 0);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001794 if (StringBuilderSubstringLength::is_valid(length) &&
1795 StringBuilderSubstringPosition::is_valid(from)) {
1796 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1797 StringBuilderSubstringPosition::encode(from);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001798 builder->Add(Smi::FromInt(encoded_slice));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001799 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001800 // Otherwise encode as two smis.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001801 builder->Add(Smi::FromInt(-length));
1802 builder->Add(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001803 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00001804 }
1805
1806
1807 void EnsureCapacity(int elements) {
1808 array_builder_.EnsureCapacity(elements);
1809 }
1810
1811
1812 void AddSubjectSlice(int from, int to) {
1813 AddSubjectSlice(&array_builder_, from, to);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001814 IncrementCharacterCount(to - from);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001815 }
1816
1817
1818 void AddString(Handle<String> string) {
1819 int length = string->length();
1820 ASSERT(length > 0);
1821 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001822 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001823 is_ascii_ = false;
1824 }
1825 IncrementCharacterCount(length);
1826 }
1827
1828
1829 Handle<String> ToString() {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001830 if (array_builder_.length() == 0) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001831 return Factory::empty_string();
1832 }
1833
1834 Handle<String> joined_string;
1835 if (is_ascii_) {
1836 joined_string = NewRawAsciiString(character_count_);
1837 AssertNoAllocation no_alloc;
1838 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1839 char* char_buffer = seq->GetChars();
1840 StringBuilderConcatHelper(*subject_,
1841 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001842 *array_builder_.array(),
1843 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001844 } else {
1845 // Non-ASCII.
1846 joined_string = NewRawTwoByteString(character_count_);
1847 AssertNoAllocation no_alloc;
1848 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1849 uc16* char_buffer = seq->GetChars();
1850 StringBuilderConcatHelper(*subject_,
1851 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001852 *array_builder_.array(),
1853 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001854 }
1855 return joined_string;
1856 }
1857
1858
1859 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001860 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001861 V8::FatalProcessOutOfMemory("String.replace result too large.");
1862 }
1863 character_count_ += by;
1864 }
1865
lrn@chromium.org25156de2010-04-06 13:10:27 +00001866 Handle<JSArray> GetParts() {
1867 Handle<JSArray> result =
1868 Factory::NewJSArrayWithElements(array_builder_.array());
1869 result->set_length(Smi::FromInt(array_builder_.length()));
1870 return result;
1871 }
kmillikin@chromium.orgd9825192010-03-30 08:36:16 +00001872
lrn@chromium.org25156de2010-04-06 13:10:27 +00001873 private:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001874 Handle<String> NewRawAsciiString(int size) {
1875 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1876 }
1877
1878
1879 Handle<String> NewRawTwoByteString(int size) {
1880 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1881 }
1882
1883
1884 void AddElement(Object* element) {
1885 ASSERT(element->IsSmi() || element->IsString());
lrn@chromium.org25156de2010-04-06 13:10:27 +00001886 ASSERT(array_builder_.capacity() > array_builder_.length());
1887 array_builder_.Add(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001888 }
1889
lrn@chromium.org25156de2010-04-06 13:10:27 +00001890 FixedArrayBuilder array_builder_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001891 Handle<String> subject_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001892 int character_count_;
1893 bool is_ascii_;
1894};
1895
1896
1897class CompiledReplacement {
1898 public:
1899 CompiledReplacement()
1900 : parts_(1), replacement_substrings_(0) {}
1901
1902 void Compile(Handle<String> replacement,
1903 int capture_count,
1904 int subject_length);
1905
1906 void Apply(ReplacementStringBuilder* builder,
1907 int match_from,
1908 int match_to,
1909 Handle<JSArray> last_match_info);
1910
1911 // Number of distinct parts of the replacement pattern.
1912 int parts() {
1913 return parts_.length();
1914 }
1915 private:
1916 enum PartType {
1917 SUBJECT_PREFIX = 1,
1918 SUBJECT_SUFFIX,
1919 SUBJECT_CAPTURE,
1920 REPLACEMENT_SUBSTRING,
1921 REPLACEMENT_STRING,
1922
1923 NUMBER_OF_PART_TYPES
1924 };
1925
1926 struct ReplacementPart {
1927 static inline ReplacementPart SubjectMatch() {
1928 return ReplacementPart(SUBJECT_CAPTURE, 0);
1929 }
1930 static inline ReplacementPart SubjectCapture(int capture_index) {
1931 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1932 }
1933 static inline ReplacementPart SubjectPrefix() {
1934 return ReplacementPart(SUBJECT_PREFIX, 0);
1935 }
1936 static inline ReplacementPart SubjectSuffix(int subject_length) {
1937 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1938 }
1939 static inline ReplacementPart ReplacementString() {
1940 return ReplacementPart(REPLACEMENT_STRING, 0);
1941 }
1942 static inline ReplacementPart ReplacementSubString(int from, int to) {
1943 ASSERT(from >= 0);
1944 ASSERT(to > from);
1945 return ReplacementPart(-from, to);
1946 }
1947
1948 // If tag <= 0 then it is the negation of a start index of a substring of
1949 // the replacement pattern, otherwise it's a value from PartType.
1950 ReplacementPart(int tag, int data)
1951 : tag(tag), data(data) {
1952 // Must be non-positive or a PartType value.
1953 ASSERT(tag < NUMBER_OF_PART_TYPES);
1954 }
1955 // Either a value of PartType or a non-positive number that is
1956 // the negation of an index into the replacement string.
1957 int tag;
1958 // The data value's interpretation depends on the value of tag:
1959 // tag == SUBJECT_PREFIX ||
1960 // tag == SUBJECT_SUFFIX: data is unused.
1961 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1962 // tag == REPLACEMENT_SUBSTRING ||
1963 // tag == REPLACEMENT_STRING: data is index into array of substrings
1964 // of the replacement string.
1965 // tag <= 0: Temporary representation of the substring of the replacement
1966 // string ranging over -tag .. data.
1967 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1968 // substring objects.
1969 int data;
1970 };
1971
1972 template<typename Char>
1973 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1974 Vector<Char> characters,
1975 int capture_count,
1976 int subject_length) {
1977 int length = characters.length();
1978 int last = 0;
1979 for (int i = 0; i < length; i++) {
1980 Char c = characters[i];
1981 if (c == '$') {
1982 int next_index = i + 1;
1983 if (next_index == length) { // No next character!
1984 break;
1985 }
1986 Char c2 = characters[next_index];
1987 switch (c2) {
1988 case '$':
1989 if (i > last) {
1990 // There is a substring before. Include the first "$".
1991 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1992 last = next_index + 1; // Continue after the second "$".
1993 } else {
1994 // Let the next substring start with the second "$".
1995 last = next_index;
1996 }
1997 i = next_index;
1998 break;
1999 case '`':
2000 if (i > last) {
2001 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2002 }
2003 parts->Add(ReplacementPart::SubjectPrefix());
2004 i = next_index;
2005 last = i + 1;
2006 break;
2007 case '\'':
2008 if (i > last) {
2009 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2010 }
2011 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
2012 i = next_index;
2013 last = i + 1;
2014 break;
2015 case '&':
2016 if (i > last) {
2017 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2018 }
2019 parts->Add(ReplacementPart::SubjectMatch());
2020 i = next_index;
2021 last = i + 1;
2022 break;
2023 case '0':
2024 case '1':
2025 case '2':
2026 case '3':
2027 case '4':
2028 case '5':
2029 case '6':
2030 case '7':
2031 case '8':
2032 case '9': {
2033 int capture_ref = c2 - '0';
2034 if (capture_ref > capture_count) {
2035 i = next_index;
2036 continue;
2037 }
2038 int second_digit_index = next_index + 1;
2039 if (second_digit_index < length) {
2040 // Peek ahead to see if we have two digits.
2041 Char c3 = characters[second_digit_index];
2042 if ('0' <= c3 && c3 <= '9') { // Double digits.
2043 int double_digit_ref = capture_ref * 10 + c3 - '0';
2044 if (double_digit_ref <= capture_count) {
2045 next_index = second_digit_index;
2046 capture_ref = double_digit_ref;
2047 }
2048 }
2049 }
2050 if (capture_ref > 0) {
2051 if (i > last) {
2052 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2053 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002054 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002055 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
2056 last = next_index + 1;
2057 }
2058 i = next_index;
2059 break;
2060 }
2061 default:
2062 i = next_index;
2063 break;
2064 }
2065 }
2066 }
2067 if (length > last) {
2068 if (last == 0) {
2069 parts->Add(ReplacementPart::ReplacementString());
2070 } else {
2071 parts->Add(ReplacementPart::ReplacementSubString(last, length));
2072 }
2073 }
2074 }
2075
2076 ZoneList<ReplacementPart> parts_;
2077 ZoneList<Handle<String> > replacement_substrings_;
2078};
2079
2080
2081void CompiledReplacement::Compile(Handle<String> replacement,
2082 int capture_count,
2083 int subject_length) {
2084 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00002085 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002086 AssertNoAllocation no_alloc;
2087 ParseReplacementPattern(&parts_,
2088 replacement->ToAsciiVector(),
2089 capture_count,
2090 subject_length);
2091 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00002092 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002093 AssertNoAllocation no_alloc;
2094
2095 ParseReplacementPattern(&parts_,
2096 replacement->ToUC16Vector(),
2097 capture_count,
2098 subject_length);
2099 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002100 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002101 int substring_index = 0;
2102 for (int i = 0, n = parts_.length(); i < n; i++) {
2103 int tag = parts_[i].tag;
2104 if (tag <= 0) { // A replacement string slice.
2105 int from = -tag;
2106 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002107 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002108 parts_[i].tag = REPLACEMENT_SUBSTRING;
2109 parts_[i].data = substring_index;
2110 substring_index++;
2111 } else if (tag == REPLACEMENT_STRING) {
2112 replacement_substrings_.Add(replacement);
2113 parts_[i].data = substring_index;
2114 substring_index++;
2115 }
2116 }
2117}
2118
2119
2120void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
2121 int match_from,
2122 int match_to,
2123 Handle<JSArray> last_match_info) {
2124 for (int i = 0, n = parts_.length(); i < n; i++) {
2125 ReplacementPart part = parts_[i];
2126 switch (part.tag) {
2127 case SUBJECT_PREFIX:
2128 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
2129 break;
2130 case SUBJECT_SUFFIX: {
2131 int subject_length = part.data;
2132 if (match_to < subject_length) {
2133 builder->AddSubjectSlice(match_to, subject_length);
2134 }
2135 break;
2136 }
2137 case SUBJECT_CAPTURE: {
2138 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002139 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002140 int from = RegExpImpl::GetCapture(match_info, capture * 2);
2141 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
2142 if (from >= 0 && to > from) {
2143 builder->AddSubjectSlice(from, to);
2144 }
2145 break;
2146 }
2147 case REPLACEMENT_SUBSTRING:
2148 case REPLACEMENT_STRING:
2149 builder->AddString(replacement_substrings_[part.data]);
2150 break;
2151 default:
2152 UNREACHABLE();
2153 }
2154 }
2155}
2156
2157
2158
2159static Object* StringReplaceRegExpWithString(String* subject,
2160 JSRegExp* regexp,
2161 String* replacement,
2162 JSArray* last_match_info) {
2163 ASSERT(subject->IsFlat());
2164 ASSERT(replacement->IsFlat());
2165
2166 HandleScope handles;
2167
2168 int length = subject->length();
2169 Handle<String> subject_handle(subject);
2170 Handle<JSRegExp> regexp_handle(regexp);
2171 Handle<String> replacement_handle(replacement);
2172 Handle<JSArray> last_match_info_handle(last_match_info);
2173 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2174 subject_handle,
2175 0,
2176 last_match_info_handle);
2177 if (match.is_null()) {
2178 return Failure::Exception();
2179 }
2180 if (match->IsNull()) {
2181 return *subject_handle;
2182 }
2183
2184 int capture_count = regexp_handle->CaptureCount();
2185
2186 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002187 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002188 CompiledReplacement compiled_replacement;
2189 compiled_replacement.Compile(replacement_handle,
2190 capture_count,
2191 length);
2192
2193 bool is_global = regexp_handle->GetFlags().is_global();
2194
2195 // Guessing the number of parts that the final result string is built
2196 // from. Global regexps can match any number of times, so we guess
2197 // conservatively.
2198 int expected_parts =
2199 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
2200 ReplacementStringBuilder builder(subject_handle, expected_parts);
2201
2202 // Index of end of last match.
2203 int prev = 0;
2204
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002205 // Number of parts added by compiled replacement plus preceeding
2206 // string and possibly suffix after last match. It is possible for
2207 // all components to use two elements when encoded as two smis.
2208 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002209 bool matched = true;
2210 do {
2211 ASSERT(last_match_info_handle->HasFastElements());
2212 // Increase the capacity of the builder before entering local handle-scope,
2213 // so its internal buffer can safely allocate a new handle if it grows.
2214 builder.EnsureCapacity(parts_added_per_loop);
2215
2216 HandleScope loop_scope;
2217 int start, end;
2218 {
2219 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002220 FixedArray* match_info_array =
2221 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002222
2223 ASSERT_EQ(capture_count * 2 + 2,
2224 RegExpImpl::GetLastCaptureCount(match_info_array));
2225 start = RegExpImpl::GetCapture(match_info_array, 0);
2226 end = RegExpImpl::GetCapture(match_info_array, 1);
2227 }
2228
2229 if (prev < start) {
2230 builder.AddSubjectSlice(prev, start);
2231 }
2232 compiled_replacement.Apply(&builder,
2233 start,
2234 end,
2235 last_match_info_handle);
2236 prev = end;
2237
2238 // Only continue checking for global regexps.
2239 if (!is_global) break;
2240
2241 // Continue from where the match ended, unless it was an empty match.
2242 int next = end;
2243 if (start == end) {
2244 next = end + 1;
2245 if (next > length) break;
2246 }
2247
2248 match = RegExpImpl::Exec(regexp_handle,
2249 subject_handle,
2250 next,
2251 last_match_info_handle);
2252 if (match.is_null()) {
2253 return Failure::Exception();
2254 }
2255 matched = !match->IsNull();
2256 } while (matched);
2257
2258 if (prev < length) {
2259 builder.AddSubjectSlice(prev, length);
2260 }
2261
2262 return *(builder.ToString());
2263}
2264
2265
2266static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2267 ASSERT(args.length() == 4);
2268
2269 CONVERT_CHECKED(String, subject, args[0]);
2270 if (!subject->IsFlat()) {
2271 Object* flat_subject = subject->TryFlatten();
2272 if (flat_subject->IsFailure()) {
2273 return flat_subject;
2274 }
2275 subject = String::cast(flat_subject);
2276 }
2277
2278 CONVERT_CHECKED(String, replacement, args[2]);
2279 if (!replacement->IsFlat()) {
2280 Object* flat_replacement = replacement->TryFlatten();
2281 if (flat_replacement->IsFailure()) {
2282 return flat_replacement;
2283 }
2284 replacement = String::cast(flat_replacement);
2285 }
2286
2287 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2288 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2289
2290 ASSERT(last_match_info->HasFastElements());
2291
2292 return StringReplaceRegExpWithString(subject,
2293 regexp,
2294 replacement,
2295 last_match_info);
2296}
2297
2298
ager@chromium.org7c537e22008-10-16 08:43:32 +00002299// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2300// limit, we can fix the size of tables.
2301static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002302// Reduce alphabet to this size.
2303static const int kBMAlphabetSize = 0x100;
2304// For patterns below this length, the skip length of Boyer-Moore is too short
2305// to compensate for the algorithmic overhead compared to simple brute force.
2306static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002307
ager@chromium.org7c537e22008-10-16 08:43:32 +00002308// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2309// shift. Only allows the last kBMMaxShift characters of the needle
2310// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002311class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002312 public:
2313 BMGoodSuffixBuffers() {}
2314 inline void init(int needle_length) {
2315 ASSERT(needle_length > 1);
2316 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2317 int len = needle_length - start;
2318 biased_suffixes_ = suffixes_ - start;
2319 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2320 for (int i = 0; i <= len; i++) {
2321 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002322 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002323 }
2324 inline int& suffix(int index) {
2325 ASSERT(biased_suffixes_ + index >= suffixes_);
2326 return biased_suffixes_[index];
2327 }
2328 inline int& shift(int index) {
2329 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2330 return biased_good_suffix_shift_[index];
2331 }
2332 private:
2333 int suffixes_[kBMMaxShift + 1];
2334 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002335 int* biased_suffixes_;
2336 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002337 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2338};
2339
2340// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002341static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002342static BMGoodSuffixBuffers bmgs_buffers;
2343
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002344// State of the string match tables.
2345// SIMPLE: No usable content in the buffers.
2346// BOYER_MOORE_HORSPOOL: The bad_char_occurences table has been populated.
2347// BOYER_MOORE: The bmgs_buffers tables have also been populated.
2348// Whenever starting with a new needle, one should call InitializeStringSearch
2349// to determine which search strategy to use, and in the case of a long-needle
2350// strategy, the call also initializes the algorithm to SIMPLE.
2351enum StringSearchAlgorithm { SIMPLE_SEARCH, BOYER_MOORE_HORSPOOL, BOYER_MOORE };
2352static StringSearchAlgorithm algorithm;
2353
2354
ager@chromium.org7c537e22008-10-16 08:43:32 +00002355// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002356template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002357static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern) {
2358 // Only preprocess at most kBMMaxShift last characters of pattern.
2359 int start = pattern.length() < kBMMaxShift ? 0
2360 : pattern.length() - kBMMaxShift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002361 // Run forwards to populate bad_char_table, so that *last* instance
2362 // of character equivalence class is the one registered.
2363 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002364 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2365 : kBMAlphabetSize;
2366 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002367 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002368 } else {
2369 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002370 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002371 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002372 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002373 for (int i = start; i < pattern.length() - 1; i++) {
2374 pchar c = pattern[i];
2375 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002376 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002377 }
2378}
2379
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002380
ager@chromium.org7c537e22008-10-16 08:43:32 +00002381template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002382static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002383 int m = pattern.length();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002384 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002385 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002386 // Compute Good Suffix tables.
2387 bmgs_buffers.init(m);
2388
2389 bmgs_buffers.shift(m-1) = 1;
2390 bmgs_buffers.suffix(m) = m + 1;
2391 pchar last_char = pattern[m - 1];
2392 int suffix = m + 1;
2393 for (int i = m; i > start;) {
2394 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2395 if (bmgs_buffers.shift(suffix) == len) {
2396 bmgs_buffers.shift(suffix) = suffix - i;
2397 }
2398 suffix = bmgs_buffers.suffix(suffix);
2399 }
2400 i--;
2401 suffix--;
2402 bmgs_buffers.suffix(i) = suffix;
2403 if (suffix == m) {
2404 // No suffix to extend, so we check against last_char only.
2405 while (i > start && pattern[i - 1] != last_char) {
2406 if (bmgs_buffers.shift(m) == len) {
2407 bmgs_buffers.shift(m) = m - i;
2408 }
2409 i--;
2410 bmgs_buffers.suffix(i) = m;
2411 }
2412 if (i > start) {
2413 i--;
2414 suffix--;
2415 bmgs_buffers.suffix(i) = suffix;
2416 }
2417 }
2418 }
2419 if (suffix < m) {
2420 for (int i = start; i <= m; i++) {
2421 if (bmgs_buffers.shift(i) == len) {
2422 bmgs_buffers.shift(i) = suffix - start;
2423 }
2424 if (i == suffix) {
2425 suffix = bmgs_buffers.suffix(suffix);
2426 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002427 }
2428 }
2429}
2430
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002431
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002432template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002433static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002434 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002435 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002436 }
2437 if (sizeof(pchar) == 1) {
2438 if (char_code > String::kMaxAsciiCharCode) {
2439 return -1;
2440 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002441 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002442 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002443 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002444}
2445
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002446
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002447// Restricted simplified Boyer-Moore string matching.
2448// Uses only the bad-shift table of Boyer-Moore and only uses it
2449// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002450template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002451static int BoyerMooreHorspool(Vector<const schar> subject,
2452 Vector<const pchar> pattern,
2453 int start_index,
2454 bool* complete) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002455 ASSERT(algorithm <= BOYER_MOORE_HORSPOOL);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002456 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002457 int m = pattern.length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002458
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002459 int badness = -m;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002460
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002461 // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002462 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002463 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002464 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002465 // Perform search
2466 for (idx = start_index; idx <= n - m;) {
2467 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002468 int c;
2469 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002470 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002471 int shift = j - bc_occ;
2472 idx += shift;
2473 badness += 1 - shift; // at most zero, so badness cannot increase.
2474 if (idx > n - m) {
2475 *complete = true;
2476 return -1;
2477 }
2478 }
2479 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002480 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002481 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002482 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002483 return idx;
2484 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002485 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002486 // Badness increases by the number of characters we have
2487 // checked, and decreases by the number of characters we
2488 // can skip by shifting. It's a measure of how we are doing
2489 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002490 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002491 if (badness > 0) {
2492 *complete = false;
2493 return idx;
2494 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002495 }
2496 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002497 *complete = true;
2498 return -1;
2499}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002500
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002501
2502template <typename schar, typename pchar>
2503static int BoyerMooreIndexOf(Vector<const schar> subject,
2504 Vector<const pchar> pattern,
2505 int idx) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002506 ASSERT(algorithm <= BOYER_MOORE);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002507 int n = subject.length();
2508 int m = pattern.length();
2509 // Only preprocess at most kBMMaxShift last characters of pattern.
2510 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2511
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002512 pchar last_char = pattern[m - 1];
2513 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002514 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002515 int j = m - 1;
2516 schar c;
2517 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002518 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002519 idx += shift;
2520 if (idx > n - m) {
2521 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002522 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002523 }
2524 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2525 if (j < 0) {
2526 return idx;
2527 } else if (j < start) {
2528 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002529 // Fall back on BMH shift.
2530 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002531 } else {
2532 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002533 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002534 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002535 if (gs_shift > shift) {
2536 shift = gs_shift;
2537 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002538 idx += shift;
2539 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002540 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002541
2542 return -1;
2543}
2544
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002545
2546template <typename schar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002547static inline int SingleCharIndexOf(Vector<const schar> string,
2548 schar pattern_char,
2549 int start_index) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002550 if (sizeof(schar) == 1) {
2551 const schar* pos = reinterpret_cast<const schar*>(
2552 memchr(string.start() + start_index,
2553 pattern_char,
2554 string.length() - start_index));
2555 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002556 return static_cast<int>(pos - string.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002557 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002558 for (int i = start_index, n = string.length(); i < n; i++) {
2559 if (pattern_char == string[i]) {
2560 return i;
2561 }
2562 }
2563 return -1;
2564}
2565
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002566
2567template <typename schar>
2568static int SingleCharLastIndexOf(Vector<const schar> string,
2569 schar pattern_char,
2570 int start_index) {
2571 for (int i = start_index; i >= 0; i--) {
2572 if (pattern_char == string[i]) {
2573 return i;
2574 }
2575 }
2576 return -1;
2577}
2578
2579
ager@chromium.org7c537e22008-10-16 08:43:32 +00002580// Trivial string search for shorter strings.
2581// On return, if "complete" is set to true, the return value is the
2582// final result of searching for the patter in the subject.
2583// If "complete" is set to false, the return value is the index where
2584// further checking should start, i.e., it's guaranteed that the pattern
2585// does not occur at a position prior to the returned index.
2586template <typename pchar, typename schar>
2587static int SimpleIndexOf(Vector<const schar> subject,
2588 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002589 int idx,
2590 bool* complete) {
2591 // Badness is a count of how much work we have done. When we have
2592 // done enough work we decide it's probably worth switching to a better
2593 // algorithm.
2594 int badness = -10 - (pattern.length() << 2);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002595
ager@chromium.org7c537e22008-10-16 08:43:32 +00002596 // We know our pattern is at least 2 characters, we cache the first so
2597 // the common case of the first character not matching is faster.
2598 pchar pattern_first_char = pattern[0];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002599 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2600 badness++;
2601 if (badness > 0) {
2602 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002603 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002604 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002605 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2606 const schar* pos = reinterpret_cast<const schar*>(
2607 memchr(subject.start() + i,
2608 pattern_first_char,
2609 n - i + 1));
2610 if (pos == NULL) {
2611 *complete = true;
2612 return -1;
2613 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002614 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002615 } else {
2616 if (subject[i] != pattern_first_char) continue;
2617 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002618 int j = 1;
2619 do {
2620 if (pattern[j] != subject[i+j]) {
2621 break;
2622 }
2623 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002624 } while (j < pattern.length());
2625 if (j == pattern.length()) {
2626 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002627 return i;
2628 }
2629 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002630 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002631 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002632 return -1;
2633}
2634
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002635// Simple indexOf that never bails out. For short patterns only.
2636template <typename pchar, typename schar>
2637static int SimpleIndexOf(Vector<const schar> subject,
2638 Vector<const pchar> pattern,
2639 int idx) {
2640 pchar pattern_first_char = pattern[0];
2641 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002642 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2643 const schar* pos = reinterpret_cast<const schar*>(
2644 memchr(subject.start() + i,
2645 pattern_first_char,
2646 n - i + 1));
2647 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002648 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002649 } else {
2650 if (subject[i] != pattern_first_char) continue;
2651 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002652 int j = 1;
2653 do {
2654 if (pattern[j] != subject[i+j]) {
2655 break;
2656 }
2657 j++;
2658 } while (j < pattern.length());
2659 if (j == pattern.length()) {
2660 return i;
2661 }
2662 }
2663 return -1;
2664}
2665
2666
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002667// Strategy for searching for a string in another string.
2668enum StringSearchStrategy { SEARCH_FAIL, SEARCH_SHORT, SEARCH_LONG };
ager@chromium.org7c537e22008-10-16 08:43:32 +00002669
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002670
2671template <typename pchar>
2672static inline StringSearchStrategy InitializeStringSearch(
2673 Vector<const pchar> pat, bool ascii_subject) {
2674 ASSERT(pat.length() > 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002675 // We have an ASCII haystack and a non-ASCII needle. Check if there
2676 // really is a non-ASCII character in the needle and bail out if there
2677 // is.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002678 if (ascii_subject && sizeof(pchar) > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002679 for (int i = 0; i < pat.length(); i++) {
2680 uc16 c = pat[i];
2681 if (c > String::kMaxAsciiCharCode) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002682 return SEARCH_FAIL;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002683 }
2684 }
2685 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002686 if (pat.length() < kBMMinPatternLength) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002687 return SEARCH_SHORT;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002688 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002689 algorithm = SIMPLE_SEARCH;
2690 return SEARCH_LONG;
2691}
2692
2693
2694// Dispatch long needle searches to different algorithms.
2695template <typename schar, typename pchar>
2696static int ComplexIndexOf(Vector<const schar> sub,
2697 Vector<const pchar> pat,
2698 int start_index) {
2699 ASSERT(pat.length() >= kBMMinPatternLength);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002700 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002701 bool complete;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002702 int idx = start_index;
2703 switch (algorithm) {
2704 case SIMPLE_SEARCH:
2705 idx = SimpleIndexOf(sub, pat, idx, &complete);
2706 if (complete) return idx;
2707 BoyerMoorePopulateBadCharTable(pat);
2708 algorithm = BOYER_MOORE_HORSPOOL;
2709 // FALLTHROUGH.
2710 case BOYER_MOORE_HORSPOOL:
2711 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
2712 if (complete) return idx;
2713 // Build the Good Suffix table and continue searching.
2714 BoyerMoorePopulateGoodSuffixTable(pat);
2715 algorithm = BOYER_MOORE;
2716 // FALLTHROUGH.
2717 case BOYER_MOORE:
2718 return BoyerMooreIndexOf(sub, pat, idx);
2719 }
2720 UNREACHABLE();
2721 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002722}
2723
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002724
2725// Dispatch to different search strategies for a single search.
2726// If searching multiple times on the same needle, the search
2727// strategy should only be computed once and then dispatch to different
2728// loops.
2729template <typename schar, typename pchar>
2730static int StringSearch(Vector<const schar> sub,
2731 Vector<const pchar> pat,
2732 int start_index) {
2733 bool ascii_subject = (sizeof(schar) == 1);
2734 StringSearchStrategy strategy = InitializeStringSearch(pat, ascii_subject);
2735 switch (strategy) {
2736 case SEARCH_FAIL: return -1;
2737 case SEARCH_SHORT: return SimpleIndexOf(sub, pat, start_index);
2738 case SEARCH_LONG: return ComplexIndexOf(sub, pat, start_index);
2739 }
2740 UNREACHABLE();
2741 return -1;
2742}
2743
2744
ager@chromium.org7c537e22008-10-16 08:43:32 +00002745// Perform string match of pattern on subject, starting at start index.
2746// Caller must ensure that 0 <= start_index <= sub->length(),
2747// and should check that pat->length() + start_index <= sub->length()
2748int Runtime::StringMatch(Handle<String> sub,
2749 Handle<String> pat,
2750 int start_index) {
2751 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002752 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002753
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002754 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002755 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002756
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002757 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002758 if (start_index + pattern_length > subject_length) return -1;
2759
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002760 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002761 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002762 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002763
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002764 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002765 // character patterns linear search is necessary, so any smart
2766 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002767 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002768 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002769 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002770 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002771 if (pchar > String::kMaxAsciiCharCode) {
2772 return -1;
2773 }
2774 Vector<const char> ascii_vector =
2775 sub->ToAsciiVector().SubVector(start_index, subject_length);
2776 const void* pos = memchr(ascii_vector.start(),
2777 static_cast<const char>(pchar),
2778 static_cast<size_t>(ascii_vector.length()));
2779 if (pos == NULL) {
2780 return -1;
2781 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002782 return static_cast<int>(reinterpret_cast<const char*>(pos)
2783 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002784 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002785 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002786 }
2787
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002788 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002789 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002790 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002791
ager@chromium.org7c537e22008-10-16 08:43:32 +00002792 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2793 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002794 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002795 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002796 if (sub->IsAsciiRepresentation()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002797 return StringSearch(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002798 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002799 return StringSearch(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002800 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002801 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002802 if (sub->IsAsciiRepresentation()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002803 return StringSearch(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002804 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002805 return StringSearch(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002806}
2807
2808
2809static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002810 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002811 ASSERT(args.length() == 3);
2812
ager@chromium.org7c537e22008-10-16 08:43:32 +00002813 CONVERT_ARG_CHECKED(String, sub, 0);
2814 CONVERT_ARG_CHECKED(String, pat, 1);
2815
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002816 Object* index = args[2];
2817 uint32_t start_index;
2818 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2819
ager@chromium.org870a0b62008-11-04 11:43:05 +00002820 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002821 int position = Runtime::StringMatch(sub, pat, start_index);
2822 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002823}
2824
2825
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002826template <typename schar, typename pchar>
2827static int StringMatchBackwards(Vector<const schar> sub,
2828 Vector<const pchar> pat,
2829 int idx) {
2830 ASSERT(pat.length() >= 1);
2831 ASSERT(idx + pat.length() <= sub.length());
2832
2833 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
2834 for (int i = 0; i < pat.length(); i++) {
2835 uc16 c = pat[i];
2836 if (c > String::kMaxAsciiCharCode) {
2837 return -1;
2838 }
2839 }
2840 }
2841
2842 pchar pattern_first_char = pat[0];
2843 for (int i = idx; i >= 0; i--) {
2844 if (sub[i] != pattern_first_char) continue;
2845 int j = 1;
2846 while (j < pat.length()) {
2847 if (pat[j] != sub[i+j]) {
2848 break;
2849 }
2850 j++;
2851 }
2852 if (j == pat.length()) {
2853 return i;
2854 }
2855 }
2856 return -1;
2857}
2858
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002859static Object* Runtime_StringLastIndexOf(Arguments args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002860 HandleScope scope; // create a new handle scope
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002861 ASSERT(args.length() == 3);
2862
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002863 CONVERT_ARG_CHECKED(String, sub, 0);
2864 CONVERT_ARG_CHECKED(String, pat, 1);
2865
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002866 Object* index = args[2];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002867 uint32_t start_index;
2868 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2869
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002870 uint32_t pat_length = pat->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002871 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002872
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002873 if (start_index + pat_length > sub_length) {
2874 start_index = sub_length - pat_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002875 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002876
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002877 if (pat_length == 0) {
2878 return Smi::FromInt(start_index);
2879 }
2880
2881 if (!sub->IsFlat()) {
2882 FlattenString(sub);
2883 }
2884
2885 if (pat_length == 1) {
2886 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2887 if (sub->IsAsciiRepresentation()) {
2888 uc16 pchar = pat->Get(0);
2889 if (pchar > String::kMaxAsciiCharCode) {
2890 return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002891 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002892 return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
2893 static_cast<char>(pat->Get(0)),
2894 start_index));
2895 } else {
2896 return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
2897 pat->Get(0),
2898 start_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002899 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002900 }
2901
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002902 if (!pat->IsFlat()) {
2903 FlattenString(pat);
2904 }
2905
2906 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2907
2908 int position = -1;
2909
2910 if (pat->IsAsciiRepresentation()) {
2911 Vector<const char> pat_vector = pat->ToAsciiVector();
2912 if (sub->IsAsciiRepresentation()) {
2913 position = StringMatchBackwards(sub->ToAsciiVector(),
2914 pat_vector,
2915 start_index);
2916 } else {
2917 position = StringMatchBackwards(sub->ToUC16Vector(),
2918 pat_vector,
2919 start_index);
2920 }
2921 } else {
2922 Vector<const uc16> pat_vector = pat->ToUC16Vector();
2923 if (sub->IsAsciiRepresentation()) {
2924 position = StringMatchBackwards(sub->ToAsciiVector(),
2925 pat_vector,
2926 start_index);
2927 } else {
2928 position = StringMatchBackwards(sub->ToUC16Vector(),
2929 pat_vector,
2930 start_index);
2931 }
2932 }
2933
2934 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002935}
2936
2937
2938static Object* Runtime_StringLocaleCompare(Arguments args) {
2939 NoHandleAllocation ha;
2940 ASSERT(args.length() == 2);
2941
2942 CONVERT_CHECKED(String, str1, args[0]);
2943 CONVERT_CHECKED(String, str2, args[1]);
2944
2945 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002946 int str1_length = str1->length();
2947 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002948
2949 // Decide trivial cases without flattening.
2950 if (str1_length == 0) {
2951 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2952 return Smi::FromInt(-str2_length);
2953 } else {
2954 if (str2_length == 0) return Smi::FromInt(str1_length);
2955 }
2956
2957 int end = str1_length < str2_length ? str1_length : str2_length;
2958
2959 // No need to flatten if we are going to find the answer on the first
2960 // character. At this point we know there is at least one character
2961 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002962 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002963 if (d != 0) return Smi::FromInt(d);
2964
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002965 str1->TryFlatten();
2966 str2->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002967
2968 static StringInputBuffer buf1;
2969 static StringInputBuffer buf2;
2970
2971 buf1.Reset(str1);
2972 buf2.Reset(str2);
2973
2974 for (int i = 0; i < end; i++) {
2975 uint16_t char1 = buf1.GetNext();
2976 uint16_t char2 = buf2.GetNext();
2977 if (char1 != char2) return Smi::FromInt(char1 - char2);
2978 }
2979
2980 return Smi::FromInt(str1_length - str2_length);
2981}
2982
2983
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002984static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002985 NoHandleAllocation ha;
2986 ASSERT(args.length() == 3);
2987
2988 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002989 Object* from = args[1];
2990 Object* to = args[2];
2991 int start, end;
2992 // We have a fast integer-only case here to avoid a conversion to double in
2993 // the common case where from and to are Smis.
2994 if (from->IsSmi() && to->IsSmi()) {
2995 start = Smi::cast(from)->value();
2996 end = Smi::cast(to)->value();
2997 } else {
2998 CONVERT_DOUBLE_CHECKED(from_number, from);
2999 CONVERT_DOUBLE_CHECKED(to_number, to);
3000 start = FastD2I(from_number);
3001 end = FastD2I(to_number);
3002 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003003 RUNTIME_ASSERT(end >= start);
3004 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003005 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003006 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003007 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003008}
3009
3010
ager@chromium.org41826e72009-03-30 13:30:57 +00003011static Object* Runtime_StringMatch(Arguments args) {
3012 ASSERT_EQ(3, args.length());
3013
3014 CONVERT_ARG_CHECKED(String, subject, 0);
3015 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
3016 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
3017 HandleScope handles;
3018
3019 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
3020
3021 if (match.is_null()) {
3022 return Failure::Exception();
3023 }
3024 if (match->IsNull()) {
3025 return Heap::null_value();
3026 }
3027 int length = subject->length();
3028
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00003029 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00003030 ZoneList<int> offsets(8);
3031 do {
3032 int start;
3033 int end;
3034 {
3035 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003036 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00003037 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
3038 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
3039 }
3040 offsets.Add(start);
3041 offsets.Add(end);
3042 int index = start < end ? end : end + 1;
3043 if (index > length) break;
3044 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
3045 if (match.is_null()) {
3046 return Failure::Exception();
3047 }
3048 } while (!match->IsNull());
3049 int matches = offsets.length() / 2;
3050 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
3051 for (int i = 0; i < matches ; i++) {
3052 int from = offsets.at(i * 2);
3053 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003054 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00003055 }
3056 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
3057 result->set_length(Smi::FromInt(matches));
3058 return *result;
3059}
3060
3061
lrn@chromium.org25156de2010-04-06 13:10:27 +00003062// Two smis before and after the match, for very long strings.
3063const int kMaxBuilderEntriesPerRegExpMatch = 5;
3064
3065
3066static void SetLastMatchInfoNoCaptures(Handle<String> subject,
3067 Handle<JSArray> last_match_info,
3068 int match_start,
3069 int match_end) {
3070 // Fill last_match_info with a single capture.
3071 last_match_info->EnsureSize(2 + RegExpImpl::kLastMatchOverhead);
3072 AssertNoAllocation no_gc;
3073 FixedArray* elements = FixedArray::cast(last_match_info->elements());
3074 RegExpImpl::SetLastCaptureCount(elements, 2);
3075 RegExpImpl::SetLastInput(elements, *subject);
3076 RegExpImpl::SetLastSubject(elements, *subject);
3077 RegExpImpl::SetCapture(elements, 0, match_start);
3078 RegExpImpl::SetCapture(elements, 1, match_end);
3079}
3080
3081
3082template <typename schar>
3083static bool SearchCharMultiple(Vector<schar> subject,
3084 String* pattern,
3085 schar pattern_char,
3086 FixedArrayBuilder* builder,
3087 int* match_pos) {
3088 // Position of last match.
3089 int pos = *match_pos;
3090 int subject_length = subject.length();
3091 while (pos < subject_length) {
3092 int match_end = pos + 1;
3093 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3094 *match_pos = pos;
3095 return false;
3096 }
3097 int new_pos = SingleCharIndexOf(subject, pattern_char, match_end);
3098 if (new_pos >= 0) {
3099 // Match has been found.
3100 if (new_pos > match_end) {
3101 ReplacementStringBuilder::AddSubjectSlice(builder, match_end, new_pos);
3102 }
3103 pos = new_pos;
3104 builder->Add(pattern);
3105 } else {
3106 break;
3107 }
3108 }
3109 if (pos + 1 < subject_length) {
3110 ReplacementStringBuilder::AddSubjectSlice(builder, pos + 1, subject_length);
3111 }
3112 *match_pos = pos;
3113 return true;
3114}
3115
3116
3117static bool SearchCharMultiple(Handle<String> subject,
3118 Handle<String> pattern,
3119 Handle<JSArray> last_match_info,
3120 FixedArrayBuilder* builder) {
3121 ASSERT(subject->IsFlat());
3122 ASSERT_EQ(1, pattern->length());
3123 uc16 pattern_char = pattern->Get(0);
3124 // Treating position before first as initial "previous match position".
3125 int match_pos = -1;
3126
3127 for (;;) { // Break when search complete.
3128 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3129 AssertNoAllocation no_gc;
3130 if (subject->IsAsciiRepresentation()) {
3131 if (pattern_char > String::kMaxAsciiCharCode) {
3132 break;
3133 }
3134 Vector<const char> subject_vector = subject->ToAsciiVector();
3135 char pattern_ascii_char = static_cast<char>(pattern_char);
3136 bool complete = SearchCharMultiple<const char>(subject_vector,
3137 *pattern,
3138 pattern_ascii_char,
3139 builder,
3140 &match_pos);
3141 if (complete) break;
3142 } else {
3143 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3144 bool complete = SearchCharMultiple<const uc16>(subject_vector,
3145 *pattern,
3146 pattern_char,
3147 builder,
3148 &match_pos);
3149 if (complete) break;
3150 }
3151 }
3152
3153 if (match_pos >= 0) {
3154 SetLastMatchInfoNoCaptures(subject,
3155 last_match_info,
3156 match_pos,
3157 match_pos + 1);
3158 return true;
3159 }
3160 return false; // No matches at all.
3161}
3162
3163
3164template <typename schar, typename pchar>
3165static bool SearchStringMultiple(Vector<schar> subject,
3166 String* pattern,
3167 Vector<pchar> pattern_string,
3168 FixedArrayBuilder* builder,
3169 int* match_pos) {
3170 int pos = *match_pos;
3171 int subject_length = subject.length();
3172 int pattern_length = pattern_string.length();
3173 int max_search_start = subject_length - pattern_length;
3174 bool is_ascii = (sizeof(schar) == 1);
3175 StringSearchStrategy strategy =
3176 InitializeStringSearch(pattern_string, is_ascii);
3177 switch (strategy) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003178 case SEARCH_FAIL: break;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003179 case SEARCH_SHORT:
3180 while (pos <= max_search_start) {
3181 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3182 *match_pos = pos;
3183 return false;
3184 }
3185 // Position of end of previous match.
3186 int match_end = pos + pattern_length;
3187 int new_pos = SimpleIndexOf(subject, pattern_string, match_end);
3188 if (new_pos >= 0) {
3189 // A match.
3190 if (new_pos > match_end) {
3191 ReplacementStringBuilder::AddSubjectSlice(builder,
3192 match_end,
3193 new_pos);
3194 }
3195 pos = new_pos;
3196 builder->Add(pattern);
3197 } else {
3198 break;
3199 }
3200 }
3201 break;
3202 case SEARCH_LONG:
3203 while (pos <= max_search_start) {
3204 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003205 *match_pos = pos;
3206 return false;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003207 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003208 int match_end = pos + pattern_length;
3209 int new_pos = ComplexIndexOf(subject, pattern_string, match_end);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003210 if (new_pos >= 0) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003211 // A match has been found.
3212 if (new_pos > match_end) {
3213 ReplacementStringBuilder::AddSubjectSlice(builder,
3214 match_end,
3215 new_pos);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003216 }
3217 pos = new_pos;
3218 builder->Add(pattern);
3219 } else {
3220 break;
3221 }
3222 }
3223 break;
3224 }
3225 if (pos < max_search_start) {
3226 ReplacementStringBuilder::AddSubjectSlice(builder,
3227 pos + pattern_length,
3228 subject_length);
3229 }
3230 *match_pos = pos;
3231 return true;
3232}
3233
3234
3235static bool SearchStringMultiple(Handle<String> subject,
3236 Handle<String> pattern,
3237 Handle<JSArray> last_match_info,
3238 FixedArrayBuilder* builder) {
3239 ASSERT(subject->IsFlat());
3240 ASSERT(pattern->IsFlat());
3241 ASSERT(pattern->length() > 1);
3242
3243 // Treating as if a previous match was before first character.
3244 int match_pos = -pattern->length();
3245
3246 for (;;) { // Break when search complete.
3247 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3248 AssertNoAllocation no_gc;
3249 if (subject->IsAsciiRepresentation()) {
3250 Vector<const char> subject_vector = subject->ToAsciiVector();
3251 if (pattern->IsAsciiRepresentation()) {
3252 if (SearchStringMultiple(subject_vector,
3253 *pattern,
3254 pattern->ToAsciiVector(),
3255 builder,
3256 &match_pos)) break;
3257 } else {
3258 if (SearchStringMultiple(subject_vector,
3259 *pattern,
3260 pattern->ToUC16Vector(),
3261 builder,
3262 &match_pos)) break;
3263 }
3264 } else {
3265 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3266 if (pattern->IsAsciiRepresentation()) {
3267 if (SearchStringMultiple(subject_vector,
3268 *pattern,
3269 pattern->ToAsciiVector(),
3270 builder,
3271 &match_pos)) break;
3272 } else {
3273 if (SearchStringMultiple(subject_vector,
3274 *pattern,
3275 pattern->ToUC16Vector(),
3276 builder,
3277 &match_pos)) break;
3278 }
3279 }
3280 }
3281
3282 if (match_pos >= 0) {
3283 SetLastMatchInfoNoCaptures(subject,
3284 last_match_info,
3285 match_pos,
3286 match_pos + pattern->length());
3287 return true;
3288 }
3289 return false; // No matches at all.
3290}
3291
3292
3293static RegExpImpl::IrregexpResult SearchRegExpNoCaptureMultiple(
3294 Handle<String> subject,
3295 Handle<JSRegExp> regexp,
3296 Handle<JSArray> last_match_array,
3297 FixedArrayBuilder* builder) {
3298 ASSERT(subject->IsFlat());
3299 int match_start = -1;
3300 int match_end = 0;
3301 int pos = 0;
3302 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3303 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3304
3305 OffsetsVector registers(required_registers);
3306 Vector<int> register_vector(registers.vector(), registers.length());
3307 int subject_length = subject->length();
3308
3309 for (;;) { // Break on failure, return on exception.
3310 RegExpImpl::IrregexpResult result =
3311 RegExpImpl::IrregexpExecOnce(regexp,
3312 subject,
3313 pos,
3314 register_vector);
3315 if (result == RegExpImpl::RE_SUCCESS) {
3316 match_start = register_vector[0];
3317 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3318 if (match_end < match_start) {
3319 ReplacementStringBuilder::AddSubjectSlice(builder,
3320 match_end,
3321 match_start);
3322 }
3323 match_end = register_vector[1];
3324 HandleScope loop_scope;
3325 builder->Add(*Factory::NewSubString(subject, match_start, match_end));
3326 if (match_start != match_end) {
3327 pos = match_end;
3328 } else {
3329 pos = match_end + 1;
3330 if (pos > subject_length) break;
3331 }
3332 } else if (result == RegExpImpl::RE_FAILURE) {
3333 break;
3334 } else {
3335 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3336 return result;
3337 }
3338 }
3339
3340 if (match_start >= 0) {
3341 if (match_end < subject_length) {
3342 ReplacementStringBuilder::AddSubjectSlice(builder,
3343 match_end,
3344 subject_length);
3345 }
3346 SetLastMatchInfoNoCaptures(subject,
3347 last_match_array,
3348 match_start,
3349 match_end);
3350 return RegExpImpl::RE_SUCCESS;
3351 } else {
3352 return RegExpImpl::RE_FAILURE; // No matches at all.
3353 }
3354}
3355
3356
3357static RegExpImpl::IrregexpResult SearchRegExpMultiple(
3358 Handle<String> subject,
3359 Handle<JSRegExp> regexp,
3360 Handle<JSArray> last_match_array,
3361 FixedArrayBuilder* builder) {
3362
3363 ASSERT(subject->IsFlat());
3364 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3365 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3366
3367 OffsetsVector registers(required_registers);
3368 Vector<int> register_vector(registers.vector(), registers.length());
3369
3370 RegExpImpl::IrregexpResult result =
3371 RegExpImpl::IrregexpExecOnce(regexp,
3372 subject,
3373 0,
3374 register_vector);
3375
3376 int capture_count = regexp->CaptureCount();
3377 int subject_length = subject->length();
3378
3379 // Position to search from.
3380 int pos = 0;
3381 // End of previous match. Differs from pos if match was empty.
3382 int match_end = 0;
3383 if (result == RegExpImpl::RE_SUCCESS) {
3384 // Need to keep a copy of the previous match for creating last_match_info
3385 // at the end, so we have two vectors that we swap between.
3386 OffsetsVector registers2(required_registers);
3387 Vector<int> prev_register_vector(registers2.vector(), registers2.length());
3388
3389 do {
3390 int match_start = register_vector[0];
3391 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3392 if (match_end < match_start) {
3393 ReplacementStringBuilder::AddSubjectSlice(builder,
3394 match_end,
3395 match_start);
3396 }
3397 match_end = register_vector[1];
3398
3399 {
3400 // Avoid accumulating new handles inside loop.
3401 HandleScope temp_scope;
3402 // Arguments array to replace function is match, captures, index and
3403 // subject, i.e., 3 + capture count in total.
3404 Handle<FixedArray> elements = Factory::NewFixedArray(3 + capture_count);
3405 elements->set(0, *Factory::NewSubString(subject,
3406 match_start,
3407 match_end));
3408 for (int i = 1; i <= capture_count; i++) {
3409 int start = register_vector[i * 2];
3410 if (start >= 0) {
3411 int end = register_vector[i * 2 + 1];
3412 ASSERT(start <= end);
3413 Handle<String> substring = Factory::NewSubString(subject,
3414 start,
3415 end);
3416 elements->set(i, *substring);
3417 } else {
3418 ASSERT(register_vector[i * 2 + 1] < 0);
3419 elements->set(i, Heap::undefined_value());
3420 }
3421 }
3422 elements->set(capture_count + 1, Smi::FromInt(match_start));
3423 elements->set(capture_count + 2, *subject);
3424 builder->Add(*Factory::NewJSArrayWithElements(elements));
3425 }
3426 // Swap register vectors, so the last successful match is in
3427 // prev_register_vector.
3428 Vector<int> tmp = prev_register_vector;
3429 prev_register_vector = register_vector;
3430 register_vector = tmp;
3431
3432 if (match_end > match_start) {
3433 pos = match_end;
3434 } else {
3435 pos = match_end + 1;
3436 if (pos > subject_length) {
3437 break;
3438 }
3439 }
3440
3441 result = RegExpImpl::IrregexpExecOnce(regexp,
3442 subject,
3443 pos,
3444 register_vector);
3445 } while (result == RegExpImpl::RE_SUCCESS);
3446
3447 if (result != RegExpImpl::RE_EXCEPTION) {
3448 // Finished matching, with at least one match.
3449 if (match_end < subject_length) {
3450 ReplacementStringBuilder::AddSubjectSlice(builder,
3451 match_end,
3452 subject_length);
3453 }
3454
3455 int last_match_capture_count = (capture_count + 1) * 2;
3456 int last_match_array_size =
3457 last_match_capture_count + RegExpImpl::kLastMatchOverhead;
3458 last_match_array->EnsureSize(last_match_array_size);
3459 AssertNoAllocation no_gc;
3460 FixedArray* elements = FixedArray::cast(last_match_array->elements());
3461 RegExpImpl::SetLastCaptureCount(elements, last_match_capture_count);
3462 RegExpImpl::SetLastSubject(elements, *subject);
3463 RegExpImpl::SetLastInput(elements, *subject);
3464 for (int i = 0; i < last_match_capture_count; i++) {
3465 RegExpImpl::SetCapture(elements, i, prev_register_vector[i]);
3466 }
3467 return RegExpImpl::RE_SUCCESS;
3468 }
3469 }
3470 // No matches at all, return failure or exception result directly.
3471 return result;
3472}
3473
3474
3475static Object* Runtime_RegExpExecMultiple(Arguments args) {
3476 ASSERT(args.length() == 4);
3477 HandleScope handles;
3478
3479 CONVERT_ARG_CHECKED(String, subject, 1);
3480 if (!subject->IsFlat()) { FlattenString(subject); }
3481 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
3482 CONVERT_ARG_CHECKED(JSArray, last_match_info, 2);
3483 CONVERT_ARG_CHECKED(JSArray, result_array, 3);
3484
3485 ASSERT(last_match_info->HasFastElements());
3486 ASSERT(regexp->GetFlags().is_global());
3487 Handle<FixedArray> result_elements;
3488 if (result_array->HasFastElements()) {
3489 result_elements =
3490 Handle<FixedArray>(FixedArray::cast(result_array->elements()));
3491 } else {
3492 result_elements = Factory::NewFixedArrayWithHoles(16);
3493 }
3494 FixedArrayBuilder builder(result_elements);
3495
3496 if (regexp->TypeTag() == JSRegExp::ATOM) {
3497 Handle<String> pattern(
3498 String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex)));
3499 int pattern_length = pattern->length();
3500 if (pattern_length == 1) {
3501 if (SearchCharMultiple(subject, pattern, last_match_info, &builder)) {
3502 return *builder.ToJSArray(result_array);
3503 }
3504 return Heap::null_value();
3505 }
3506
3507 if (!pattern->IsFlat()) FlattenString(pattern);
3508 if (SearchStringMultiple(subject, pattern, last_match_info, &builder)) {
3509 return *builder.ToJSArray(result_array);
3510 }
3511 return Heap::null_value();
3512 }
3513
3514 ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
3515
3516 RegExpImpl::IrregexpResult result;
3517 if (regexp->CaptureCount() == 0) {
3518 result = SearchRegExpNoCaptureMultiple(subject,
3519 regexp,
3520 last_match_info,
3521 &builder);
3522 } else {
3523 result = SearchRegExpMultiple(subject, regexp, last_match_info, &builder);
3524 }
3525 if (result == RegExpImpl::RE_SUCCESS) return *builder.ToJSArray(result_array);
3526 if (result == RegExpImpl::RE_FAILURE) return Heap::null_value();
3527 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3528 return Failure::Exception();
3529}
3530
3531
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003532static Object* Runtime_NumberToRadixString(Arguments args) {
3533 NoHandleAllocation ha;
3534 ASSERT(args.length() == 2);
3535
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003536 // Fast case where the result is a one character string.
3537 if (args[0]->IsSmi() && args[1]->IsSmi()) {
3538 int value = Smi::cast(args[0])->value();
3539 int radix = Smi::cast(args[1])->value();
3540 if (value >= 0 && value < radix) {
3541 RUNTIME_ASSERT(radix <= 36);
3542 // Character array used for conversion.
3543 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
3544 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
3545 }
3546 }
3547
3548 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003549 CONVERT_DOUBLE_CHECKED(value, args[0]);
3550 if (isnan(value)) {
3551 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3552 }
3553 if (isinf(value)) {
3554 if (value < 0) {
3555 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3556 }
3557 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3558 }
3559 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
3560 int radix = FastD2I(radix_number);
3561 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3562 char* str = DoubleToRadixCString(value, radix);
3563 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3564 DeleteArray(str);
3565 return result;
3566}
3567
3568
3569static Object* Runtime_NumberToFixed(Arguments args) {
3570 NoHandleAllocation ha;
3571 ASSERT(args.length() == 2);
3572
3573 CONVERT_DOUBLE_CHECKED(value, args[0]);
3574 if (isnan(value)) {
3575 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3576 }
3577 if (isinf(value)) {
3578 if (value < 0) {
3579 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3580 }
3581 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3582 }
3583 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3584 int f = FastD2I(f_number);
3585 RUNTIME_ASSERT(f >= 0);
3586 char* str = DoubleToFixedCString(value, f);
3587 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3588 DeleteArray(str);
3589 return res;
3590}
3591
3592
3593static Object* Runtime_NumberToExponential(Arguments args) {
3594 NoHandleAllocation ha;
3595 ASSERT(args.length() == 2);
3596
3597 CONVERT_DOUBLE_CHECKED(value, args[0]);
3598 if (isnan(value)) {
3599 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3600 }
3601 if (isinf(value)) {
3602 if (value < 0) {
3603 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3604 }
3605 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3606 }
3607 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3608 int f = FastD2I(f_number);
3609 RUNTIME_ASSERT(f >= -1 && f <= 20);
3610 char* str = DoubleToExponentialCString(value, f);
3611 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3612 DeleteArray(str);
3613 return res;
3614}
3615
3616
3617static Object* Runtime_NumberToPrecision(Arguments args) {
3618 NoHandleAllocation ha;
3619 ASSERT(args.length() == 2);
3620
3621 CONVERT_DOUBLE_CHECKED(value, args[0]);
3622 if (isnan(value)) {
3623 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3624 }
3625 if (isinf(value)) {
3626 if (value < 0) {
3627 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3628 }
3629 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3630 }
3631 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3632 int f = FastD2I(f_number);
3633 RUNTIME_ASSERT(f >= 1 && f <= 21);
3634 char* str = DoubleToPrecisionCString(value, f);
3635 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3636 DeleteArray(str);
3637 return res;
3638}
3639
3640
3641// Returns a single character string where first character equals
3642// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003643static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003644 if (index < static_cast<uint32_t>(string->length())) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003645 string->TryFlatten();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003646 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003647 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003648 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003649 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003650}
3651
3652
3653Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
3654 // Handle [] indexing on Strings
3655 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003656 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
3657 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003658 }
3659
3660 // Handle [] indexing on String objects
3661 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003662 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
3663 Handle<Object> result =
3664 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
3665 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003666 }
3667
3668 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003669 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003670 return prototype->GetElement(index);
3671 }
3672
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003673 return GetElement(object, index);
3674}
3675
3676
3677Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003678 return object->GetElement(index);
3679}
3680
3681
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003682Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
3683 HandleScope scope;
3684
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003685 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003686 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003687 Handle<Object> error =
3688 Factory::NewTypeError("non_object_property_load",
3689 HandleVector(args, 2));
3690 return Top::Throw(*error);
3691 }
3692
3693 // Check if the given key is an array index.
3694 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003695 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003696 return GetElementOrCharAt(object, index);
3697 }
3698
3699 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003700 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003701 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003702 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003703 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003704 bool has_pending_exception = false;
3705 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003706 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003707 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003708 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003709 }
3710
ager@chromium.org32912102009-01-16 10:38:43 +00003711 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003712 // the element if so.
3713 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003714 return GetElementOrCharAt(object, index);
3715 } else {
3716 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003717 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003718 }
3719}
3720
3721
3722static Object* Runtime_GetProperty(Arguments args) {
3723 NoHandleAllocation ha;
3724 ASSERT(args.length() == 2);
3725
3726 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003727 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003728
3729 return Runtime::GetObjectProperty(object, key);
3730}
3731
3732
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003733// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003734static Object* Runtime_KeyedGetProperty(Arguments args) {
3735 NoHandleAllocation ha;
3736 ASSERT(args.length() == 2);
3737
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003738 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00003739 // itself.
3740 //
3741 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00003742 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00003743 // global proxy object never has properties. This is the case
3744 // because the global proxy object forwards everything to its hidden
3745 // prototype including local lookups.
3746 //
3747 // Additionally, we need to make sure that we do not cache results
3748 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003749 if (args[0]->IsJSObject() &&
3750 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00003751 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003752 args[1]->IsString()) {
3753 JSObject* receiver = JSObject::cast(args[0]);
3754 String* key = String::cast(args[1]);
3755 if (receiver->HasFastProperties()) {
3756 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003757 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003758 int offset = KeyedLookupCache::Lookup(receiver_map, key);
3759 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003760 Object* value = receiver->FastPropertyAt(offset);
3761 return value->IsTheHole() ? Heap::undefined_value() : value;
3762 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003763 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003764 LookupResult result;
3765 receiver->LocalLookup(key, &result);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00003766 if (result.IsProperty() && result.type() == FIELD) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003767 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003768 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003769 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003770 }
3771 } else {
3772 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003773 StringDictionary* dictionary = receiver->property_dictionary();
3774 int entry = dictionary->FindEntry(key);
3775 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003776 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003777 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003778 if (!receiver->IsGlobalObject()) return value;
3779 value = JSGlobalPropertyCell::cast(value)->value();
3780 if (!value->IsTheHole()) return value;
3781 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003782 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003783 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003784 } else if (args[0]->IsString() && args[1]->IsSmi()) {
3785 // Fast case for string indexing using [] with a smi index.
3786 HandleScope scope;
3787 Handle<String> str = args.at<String>(0);
3788 int index = Smi::cast(args[1])->value();
3789 Handle<Object> result = GetCharAt(str, index);
3790 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003791 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003792
3793 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003794 return Runtime::GetObjectProperty(args.at<Object>(0),
3795 args.at<Object>(1));
3796}
3797
3798
ager@chromium.org5c838252010-02-19 08:53:10 +00003799static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
3800 ASSERT(args.length() == 5);
3801 HandleScope scope;
3802 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3803 CONVERT_CHECKED(String, name, args[1]);
3804 CONVERT_CHECKED(Smi, flag_setter, args[2]);
3805 CONVERT_CHECKED(JSFunction, fun, args[3]);
3806 CONVERT_CHECKED(Smi, flag_attr, args[4]);
3807 int unchecked = flag_attr->value();
3808 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3809 RUNTIME_ASSERT(!obj->IsNull());
3810 LookupResult result;
3811 obj->LocalLookupRealNamedProperty(name, &result);
3812
3813 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3814 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
3815 // delete it to avoid running into trouble in DefineAccessor, which
3816 // handles this incorrectly if the property is readonly (does nothing)
3817 if (result.IsProperty() &&
3818 (result.type() == FIELD || result.type() == NORMAL
3819 || result.type() == CONSTANT_FUNCTION)) {
3820 obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
3821 }
3822 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
3823}
3824
3825static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
3826 ASSERT(args.length() == 4);
3827 HandleScope scope;
3828 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
3829 CONVERT_ARG_CHECKED(String, name, 1);
3830 Handle<Object> obj_value = args.at<Object>(2);
3831
3832 CONVERT_CHECKED(Smi, flag, args[3]);
3833 int unchecked = flag->value();
3834 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3835
3836 LookupResult result;
3837 js_object->LocalLookupRealNamedProperty(*name, &result);
3838
3839 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3840
3841 // Take special care when attributes are different and there is already
3842 // a property. For simplicity we normalize the property which enables us
3843 // to not worry about changing the instance_descriptor and creating a new
3844 // map. The current version of SetObjectProperty does not handle attributes
3845 // correctly in the case where a property is a field and is reset with
3846 // new attributes.
3847 if (result.IsProperty() && attr != result.GetAttributes()) {
3848 // New attributes - normalize to avoid writing to instance descriptor
3849 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3850 // Use IgnoreAttributes version since a readonly property may be
3851 // overridden and SetProperty does not allow this.
3852 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
3853 *obj_value,
3854 attr);
3855 }
3856 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
3857}
3858
3859
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003860Object* Runtime::SetObjectProperty(Handle<Object> object,
3861 Handle<Object> key,
3862 Handle<Object> value,
3863 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003864 HandleScope scope;
3865
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003866 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003867 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003868 Handle<Object> error =
3869 Factory::NewTypeError("non_object_property_store",
3870 HandleVector(args, 2));
3871 return Top::Throw(*error);
3872 }
3873
3874 // If the object isn't a JavaScript object, we ignore the store.
3875 if (!object->IsJSObject()) return *value;
3876
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003877 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3878
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003879 // Check if the given key is an array index.
3880 uint32_t index;
3881 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003882 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3883 // of a string using [] notation. We need to support this too in
3884 // JavaScript.
3885 // In the case of a String object we just need to redirect the assignment to
3886 // the underlying string if the index is in range. Since the underlying
3887 // string does nothing with the assignment then we can ignore such
3888 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003889 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003890 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003891 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003892
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003893 Handle<Object> result = SetElement(js_object, index, value);
3894 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003895 return *value;
3896 }
3897
3898 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003899 Handle<Object> result;
3900 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003901 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003902 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003903 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003904 key_string->TryFlatten();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003905 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003906 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003907 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003908 return *value;
3909 }
3910
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003911 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003912 bool has_pending_exception = false;
3913 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3914 if (has_pending_exception) return Failure::Exception();
3915 Handle<String> name = Handle<String>::cast(converted);
3916
3917 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003918 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003919 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003920 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003921 }
3922}
3923
3924
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003925Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
3926 Handle<Object> key,
3927 Handle<Object> value,
3928 PropertyAttributes attr) {
3929 HandleScope scope;
3930
3931 // Check if the given key is an array index.
3932 uint32_t index;
3933 if (Array::IndexFromObject(*key, &index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003934 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3935 // of a string using [] notation. We need to support this too in
3936 // JavaScript.
3937 // In the case of a String object we just need to redirect the assignment to
3938 // the underlying string if the index is in range. Since the underlying
3939 // string does nothing with the assignment then we can ignore such
3940 // assignments.
3941 if (js_object->IsStringObjectWithCharacterAt(index)) {
3942 return *value;
3943 }
3944
3945 return js_object->SetElement(index, *value);
3946 }
3947
3948 if (key->IsString()) {
3949 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003950 return js_object->SetElement(index, *value);
3951 } else {
3952 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003953 key_string->TryFlatten();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003954 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
3955 *value,
3956 attr);
3957 }
3958 }
3959
3960 // Call-back into JavaScript to convert the key to a string.
3961 bool has_pending_exception = false;
3962 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3963 if (has_pending_exception) return Failure::Exception();
3964 Handle<String> name = Handle<String>::cast(converted);
3965
3966 if (name->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003967 return js_object->SetElement(index, *value);
3968 } else {
3969 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
3970 }
3971}
3972
3973
ager@chromium.orge2902be2009-06-08 12:21:35 +00003974Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
3975 Handle<Object> key) {
3976 HandleScope scope;
3977
3978 // Check if the given key is an array index.
3979 uint32_t index;
3980 if (Array::IndexFromObject(*key, &index)) {
3981 // In Firefox/SpiderMonkey, Safari and Opera you can access the
3982 // characters of a string using [] notation. In the case of a
3983 // String object we just need to redirect the deletion to the
3984 // underlying string if the index is in range. Since the
3985 // underlying string does nothing with the deletion, we can ignore
3986 // such deletions.
3987 if (js_object->IsStringObjectWithCharacterAt(index)) {
3988 return Heap::true_value();
3989 }
3990
3991 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
3992 }
3993
3994 Handle<String> key_string;
3995 if (key->IsString()) {
3996 key_string = Handle<String>::cast(key);
3997 } else {
3998 // Call-back into JavaScript to convert the key to a string.
3999 bool has_pending_exception = false;
4000 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4001 if (has_pending_exception) return Failure::Exception();
4002 key_string = Handle<String>::cast(converted);
4003 }
4004
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004005 key_string->TryFlatten();
ager@chromium.orge2902be2009-06-08 12:21:35 +00004006 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
4007}
4008
4009
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004010static Object* Runtime_SetProperty(Arguments args) {
4011 NoHandleAllocation ha;
4012 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
4013
4014 Handle<Object> object = args.at<Object>(0);
4015 Handle<Object> key = args.at<Object>(1);
4016 Handle<Object> value = args.at<Object>(2);
4017
4018 // Compute attributes.
4019 PropertyAttributes attributes = NONE;
4020 if (args.length() == 4) {
4021 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004022 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004023 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004024 RUNTIME_ASSERT(
4025 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4026 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004027 }
4028 return Runtime::SetObjectProperty(object, key, value, attributes);
4029}
4030
4031
4032// Set a local property, even if it is READ_ONLY. If the property does not
4033// exist, it will be added with attributes NONE.
4034static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
4035 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004036 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004037 CONVERT_CHECKED(JSObject, object, args[0]);
4038 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004039 // Compute attributes.
4040 PropertyAttributes attributes = NONE;
4041 if (args.length() == 4) {
4042 CONVERT_CHECKED(Smi, value_obj, args[3]);
4043 int unchecked_value = value_obj->value();
4044 // Only attribute bits should be set.
4045 RUNTIME_ASSERT(
4046 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4047 attributes = static_cast<PropertyAttributes>(unchecked_value);
4048 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004049
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004050 return object->
4051 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004052}
4053
4054
4055static Object* Runtime_DeleteProperty(Arguments args) {
4056 NoHandleAllocation ha;
4057 ASSERT(args.length() == 2);
4058
4059 CONVERT_CHECKED(JSObject, object, args[0]);
4060 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00004061 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004062}
4063
4064
ager@chromium.org9085a012009-05-11 19:22:57 +00004065static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
4066 Handle<String> key) {
4067 if (object->HasLocalProperty(*key)) return Heap::true_value();
4068 // Handle hidden prototypes. If there's a hidden prototype above this thing
4069 // then we have to check it for properties, because they are supposed to
4070 // look like they are on this object.
4071 Handle<Object> proto(object->GetPrototype());
4072 if (proto->IsJSObject() &&
4073 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
4074 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
4075 }
4076 return Heap::false_value();
4077}
4078
4079
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004080static Object* Runtime_HasLocalProperty(Arguments args) {
4081 NoHandleAllocation ha;
4082 ASSERT(args.length() == 2);
4083 CONVERT_CHECKED(String, key, args[1]);
4084
ager@chromium.org9085a012009-05-11 19:22:57 +00004085 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004086 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00004087 if (obj->IsJSObject()) {
4088 JSObject* object = JSObject::cast(obj);
4089 // Fast case - no interceptors.
4090 if (object->HasRealNamedProperty(key)) return Heap::true_value();
4091 // Slow case. Either it's not there or we have an interceptor. We should
4092 // have handles for this kind of deal.
4093 HandleScope scope;
4094 return HasLocalPropertyImplementation(Handle<JSObject>(object),
4095 Handle<String>(key));
4096 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004097 // Well, there is one exception: Handle [] on strings.
4098 uint32_t index;
4099 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00004100 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004101 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004102 return Heap::true_value();
4103 }
4104 }
4105 return Heap::false_value();
4106}
4107
4108
4109static Object* Runtime_HasProperty(Arguments args) {
4110 NoHandleAllocation na;
4111 ASSERT(args.length() == 2);
4112
4113 // Only JS objects can have properties.
4114 if (args[0]->IsJSObject()) {
4115 JSObject* object = JSObject::cast(args[0]);
4116 CONVERT_CHECKED(String, key, args[1]);
4117 if (object->HasProperty(key)) return Heap::true_value();
4118 }
4119 return Heap::false_value();
4120}
4121
4122
4123static Object* Runtime_HasElement(Arguments args) {
4124 NoHandleAllocation na;
4125 ASSERT(args.length() == 2);
4126
4127 // Only JS objects can have elements.
4128 if (args[0]->IsJSObject()) {
4129 JSObject* object = JSObject::cast(args[0]);
4130 CONVERT_CHECKED(Smi, index_obj, args[1]);
4131 uint32_t index = index_obj->value();
4132 if (object->HasElement(index)) return Heap::true_value();
4133 }
4134 return Heap::false_value();
4135}
4136
4137
4138static Object* Runtime_IsPropertyEnumerable(Arguments args) {
4139 NoHandleAllocation ha;
4140 ASSERT(args.length() == 2);
4141
4142 CONVERT_CHECKED(JSObject, object, args[0]);
4143 CONVERT_CHECKED(String, key, args[1]);
4144
4145 uint32_t index;
4146 if (key->AsArrayIndex(&index)) {
4147 return Heap::ToBoolean(object->HasElement(index));
4148 }
4149
ager@chromium.org870a0b62008-11-04 11:43:05 +00004150 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
4151 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004152}
4153
4154
4155static Object* Runtime_GetPropertyNames(Arguments args) {
4156 HandleScope scope;
4157 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004158 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004159 return *GetKeysFor(object);
4160}
4161
4162
4163// Returns either a FixedArray as Runtime_GetPropertyNames,
4164// or, if the given object has an enum cache that contains
4165// all enumerable properties of the object and its prototypes
4166// have none, the map of the object. This is used to speed up
4167// the check for deletions during a for-in.
4168static Object* Runtime_GetPropertyNamesFast(Arguments args) {
4169 ASSERT(args.length() == 1);
4170
4171 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4172
4173 if (raw_object->IsSimpleEnum()) return raw_object->map();
4174
4175 HandleScope scope;
4176 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004177 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
4178 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004179
4180 // Test again, since cache may have been built by preceding call.
4181 if (object->IsSimpleEnum()) return object->map();
4182
4183 return *content;
4184}
4185
4186
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004187// Find the length of the prototype chain that is to to handled as one. If a
4188// prototype object is hidden it is to be viewed as part of the the object it
4189// is prototype for.
4190static int LocalPrototypeChainLength(JSObject* obj) {
4191 int count = 1;
4192 Object* proto = obj->GetPrototype();
4193 while (proto->IsJSObject() &&
4194 JSObject::cast(proto)->map()->is_hidden_prototype()) {
4195 count++;
4196 proto = JSObject::cast(proto)->GetPrototype();
4197 }
4198 return count;
4199}
4200
4201
4202// Return the names of the local named properties.
4203// args[0]: object
4204static Object* Runtime_GetLocalPropertyNames(Arguments args) {
4205 HandleScope scope;
4206 ASSERT(args.length() == 1);
4207 if (!args[0]->IsJSObject()) {
4208 return Heap::undefined_value();
4209 }
4210 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4211
4212 // Skip the global proxy as it has no properties and always delegates to the
4213 // real global object.
4214 if (obj->IsJSGlobalProxy()) {
4215 // Only collect names if access is permitted.
4216 if (obj->IsAccessCheckNeeded() &&
4217 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
4218 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
4219 return *Factory::NewJSArray(0);
4220 }
4221 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
4222 }
4223
4224 // Find the number of objects making up this.
4225 int length = LocalPrototypeChainLength(*obj);
4226
4227 // Find the number of local properties for each of the objects.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004228 ScopedVector<int> local_property_count(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004229 int total_property_count = 0;
4230 Handle<JSObject> jsproto = obj;
4231 for (int i = 0; i < length; i++) {
4232 // Only collect names if access is permitted.
4233 if (jsproto->IsAccessCheckNeeded() &&
4234 !Top::MayNamedAccess(*jsproto,
4235 Heap::undefined_value(),
4236 v8::ACCESS_KEYS)) {
4237 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
4238 return *Factory::NewJSArray(0);
4239 }
4240 int n;
4241 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4242 local_property_count[i] = n;
4243 total_property_count += n;
4244 if (i < length - 1) {
4245 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4246 }
4247 }
4248
4249 // Allocate an array with storage for all the property names.
4250 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
4251
4252 // Get the property names.
4253 jsproto = obj;
4254 int proto_with_hidden_properties = 0;
4255 for (int i = 0; i < length; i++) {
4256 jsproto->GetLocalPropertyNames(*names,
4257 i == 0 ? 0 : local_property_count[i - 1]);
4258 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
4259 proto_with_hidden_properties++;
4260 }
4261 if (i < length - 1) {
4262 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4263 }
4264 }
4265
4266 // Filter out name of hidden propeties object.
4267 if (proto_with_hidden_properties > 0) {
4268 Handle<FixedArray> old_names = names;
4269 names = Factory::NewFixedArray(
4270 names->length() - proto_with_hidden_properties);
4271 int dest_pos = 0;
4272 for (int i = 0; i < total_property_count; i++) {
4273 Object* name = old_names->get(i);
4274 if (name == Heap::hidden_symbol()) {
4275 continue;
4276 }
4277 names->set(dest_pos++, name);
4278 }
4279 }
4280
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004281 return *Factory::NewJSArrayWithElements(names);
4282}
4283
4284
4285// Return the names of the local indexed properties.
4286// args[0]: object
4287static Object* Runtime_GetLocalElementNames(Arguments args) {
4288 HandleScope scope;
4289 ASSERT(args.length() == 1);
4290 if (!args[0]->IsJSObject()) {
4291 return Heap::undefined_value();
4292 }
4293 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4294
4295 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4296 Handle<FixedArray> names = Factory::NewFixedArray(n);
4297 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4298 return *Factory::NewJSArrayWithElements(names);
4299}
4300
4301
4302// Return information on whether an object has a named or indexed interceptor.
4303// args[0]: object
4304static Object* Runtime_GetInterceptorInfo(Arguments args) {
4305 HandleScope scope;
4306 ASSERT(args.length() == 1);
4307 if (!args[0]->IsJSObject()) {
4308 return Smi::FromInt(0);
4309 }
4310 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4311
4312 int result = 0;
4313 if (obj->HasNamedInterceptor()) result |= 2;
4314 if (obj->HasIndexedInterceptor()) result |= 1;
4315
4316 return Smi::FromInt(result);
4317}
4318
4319
4320// Return property names from named interceptor.
4321// args[0]: object
4322static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
4323 HandleScope scope;
4324 ASSERT(args.length() == 1);
4325 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4326
4327 if (obj->HasNamedInterceptor()) {
4328 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4329 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4330 }
4331 return Heap::undefined_value();
4332}
4333
4334
4335// Return element names from indexed interceptor.
4336// args[0]: object
4337static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
4338 HandleScope scope;
4339 ASSERT(args.length() == 1);
4340 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4341
4342 if (obj->HasIndexedInterceptor()) {
4343 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4344 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4345 }
4346 return Heap::undefined_value();
4347}
4348
4349
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004350static Object* Runtime_LocalKeys(Arguments args) {
4351 ASSERT_EQ(args.length(), 1);
4352 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4353 HandleScope scope;
4354 Handle<JSObject> object(raw_object);
4355 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
4356 LOCAL_ONLY);
4357 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
4358 // property array and since the result is mutable we have to create
4359 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004360 int length = contents->length();
4361 Handle<FixedArray> copy = Factory::NewFixedArray(length);
4362 for (int i = 0; i < length; i++) {
4363 Object* entry = contents->get(i);
4364 if (entry->IsString()) {
4365 copy->set(i, entry);
4366 } else {
4367 ASSERT(entry->IsNumber());
4368 HandleScope scope;
4369 Handle<Object> entry_handle(entry);
4370 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
4371 copy->set(i, *entry_str);
4372 }
4373 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004374 return *Factory::NewJSArrayWithElements(copy);
4375}
4376
4377
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004378static Object* Runtime_GetArgumentsProperty(Arguments args) {
4379 NoHandleAllocation ha;
4380 ASSERT(args.length() == 1);
4381
4382 // Compute the frame holding the arguments.
4383 JavaScriptFrameIterator it;
4384 it.AdvanceToArgumentsFrame();
4385 JavaScriptFrame* frame = it.frame();
4386
4387 // Get the actual number of provided arguments.
4388 const uint32_t n = frame->GetProvidedParametersCount();
4389
4390 // Try to convert the key to an index. If successful and within
4391 // index return the the argument from the frame.
4392 uint32_t index;
4393 if (Array::IndexFromObject(args[0], &index) && index < n) {
4394 return frame->GetParameter(index);
4395 }
4396
4397 // Convert the key to a string.
4398 HandleScope scope;
4399 bool exception = false;
4400 Handle<Object> converted =
4401 Execution::ToString(args.at<Object>(0), &exception);
4402 if (exception) return Failure::Exception();
4403 Handle<String> key = Handle<String>::cast(converted);
4404
4405 // Try to convert the string key into an array index.
4406 if (key->AsArrayIndex(&index)) {
4407 if (index < n) {
4408 return frame->GetParameter(index);
4409 } else {
4410 return Top::initial_object_prototype()->GetElement(index);
4411 }
4412 }
4413
4414 // Handle special arguments properties.
4415 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
4416 if (key->Equals(Heap::callee_symbol())) return frame->function();
4417
4418 // Lookup in the initial Object.prototype object.
4419 return Top::initial_object_prototype()->GetProperty(*key);
4420}
4421
4422
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004423static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004424 HandleScope scope;
4425
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004426 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004427 Handle<Object> object = args.at<Object>(0);
4428 if (object->IsJSObject()) {
4429 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00004430 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
4431 js_object->TransformToFastProperties(0);
4432 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004433 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004434 return *object;
4435}
4436
4437
4438static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004439 HandleScope scope;
4440
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004441 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004442 Handle<Object> object = args.at<Object>(0);
4443 if (object->IsJSObject()) {
4444 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004445 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004446 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004447 return *object;
4448}
4449
4450
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004451static Object* Runtime_ToBool(Arguments args) {
4452 NoHandleAllocation ha;
4453 ASSERT(args.length() == 1);
4454
4455 return args[0]->ToBoolean();
4456}
4457
4458
4459// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
4460// Possible optimizations: put the type string into the oddballs.
4461static Object* Runtime_Typeof(Arguments args) {
4462 NoHandleAllocation ha;
4463
4464 Object* obj = args[0];
4465 if (obj->IsNumber()) return Heap::number_symbol();
4466 HeapObject* heap_obj = HeapObject::cast(obj);
4467
4468 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004469 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004470
4471 InstanceType instance_type = heap_obj->map()->instance_type();
4472 if (instance_type < FIRST_NONSTRING_TYPE) {
4473 return Heap::string_symbol();
4474 }
4475
4476 switch (instance_type) {
4477 case ODDBALL_TYPE:
4478 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
4479 return Heap::boolean_symbol();
4480 }
4481 if (heap_obj->IsNull()) {
4482 return Heap::object_symbol();
4483 }
4484 ASSERT(heap_obj->IsUndefined());
4485 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004486 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004487 return Heap::function_symbol();
4488 default:
4489 // For any kind of object not handled above, the spec rule for
4490 // host objects gives that it is okay to return "object"
4491 return Heap::object_symbol();
4492 }
4493}
4494
4495
lrn@chromium.org25156de2010-04-06 13:10:27 +00004496static bool AreDigits(const char*s, int from, int to) {
4497 for (int i = from; i < to; i++) {
4498 if (s[i] < '0' || s[i] > '9') return false;
4499 }
4500
4501 return true;
4502}
4503
4504
4505static int ParseDecimalInteger(const char*s, int from, int to) {
4506 ASSERT(to - from < 10); // Overflow is not possible.
4507 ASSERT(from < to);
4508 int d = s[from] - '0';
4509
4510 for (int i = from + 1; i < to; i++) {
4511 d = 10 * d + (s[i] - '0');
4512 }
4513
4514 return d;
4515}
4516
4517
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004518static Object* Runtime_StringToNumber(Arguments args) {
4519 NoHandleAllocation ha;
4520 ASSERT(args.length() == 1);
4521 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004522 subject->TryFlatten();
lrn@chromium.org25156de2010-04-06 13:10:27 +00004523
4524 // Fast case: short integer or some sorts of junk values.
4525 int len = subject->length();
4526 if (subject->IsSeqAsciiString()) {
4527 if (len == 0) return Smi::FromInt(0);
4528
4529 char const* data = SeqAsciiString::cast(subject)->GetChars();
4530 bool minus = (data[0] == '-');
4531 int start_pos = (minus ? 1 : 0);
4532
4533 if (start_pos == len) {
4534 return Heap::nan_value();
4535 } else if (data[start_pos] > '9') {
4536 // Fast check for a junk value. A valid string may start from a
4537 // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit or
4538 // the 'I' character ('Infinity'). All of that have codes not greater than
4539 // '9' except 'I'.
4540 if (data[start_pos] != 'I') {
4541 return Heap::nan_value();
4542 }
4543 } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
4544 // The maximal/minimal smi has 10 digits. If the string has less digits we
4545 // know it will fit into the smi-data type.
4546 int d = ParseDecimalInteger(data, start_pos, len);
4547 if (minus) {
4548 if (d == 0) return Heap::minus_zero_value();
4549 d = -d;
4550 }
4551 return Smi::FromInt(d);
4552 }
4553 }
4554
4555 // Slower case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004556 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
4557}
4558
4559
4560static Object* Runtime_StringFromCharCodeArray(Arguments args) {
4561 NoHandleAllocation ha;
4562 ASSERT(args.length() == 1);
4563
4564 CONVERT_CHECKED(JSArray, codes, args[0]);
4565 int length = Smi::cast(codes->length())->value();
4566
4567 // Check if the string can be ASCII.
4568 int i;
4569 for (i = 0; i < length; i++) {
4570 Object* element = codes->GetElement(i);
4571 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
4572 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
4573 break;
4574 }
4575
4576 Object* object = NULL;
4577 if (i == length) { // The string is ASCII.
4578 object = Heap::AllocateRawAsciiString(length);
4579 } else { // The string is not ASCII.
4580 object = Heap::AllocateRawTwoByteString(length);
4581 }
4582
4583 if (object->IsFailure()) return object;
4584 String* result = String::cast(object);
4585 for (int i = 0; i < length; i++) {
4586 Object* element = codes->GetElement(i);
4587 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004588 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004589 }
4590 return result;
4591}
4592
4593
4594// kNotEscaped is generated by the following:
4595//
4596// #!/bin/perl
4597// for (my $i = 0; $i < 256; $i++) {
4598// print "\n" if $i % 16 == 0;
4599// my $c = chr($i);
4600// my $escaped = 1;
4601// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
4602// print $escaped ? "0, " : "1, ";
4603// }
4604
4605
4606static bool IsNotEscaped(uint16_t character) {
4607 // Only for 8 bit characters, the rest are always escaped (in a different way)
4608 ASSERT(character < 256);
4609 static const char kNotEscaped[256] = {
4610 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4611 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4612 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
4613 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
4614 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4615 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
4616 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4617 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
4618 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4619 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4620 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4621 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4622 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4623 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4624 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4625 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4626 };
4627 return kNotEscaped[character] != 0;
4628}
4629
4630
4631static Object* Runtime_URIEscape(Arguments args) {
4632 const char hex_chars[] = "0123456789ABCDEF";
4633 NoHandleAllocation ha;
4634 ASSERT(args.length() == 1);
4635 CONVERT_CHECKED(String, source, args[0]);
4636
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004637 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004638
4639 int escaped_length = 0;
4640 int length = source->length();
4641 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004642 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004643 buffer->Reset(source);
4644 while (buffer->has_more()) {
4645 uint16_t character = buffer->GetNext();
4646 if (character >= 256) {
4647 escaped_length += 6;
4648 } else if (IsNotEscaped(character)) {
4649 escaped_length++;
4650 } else {
4651 escaped_length += 3;
4652 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004653 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004654 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004655 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004656 Top::context()->mark_out_of_memory();
4657 return Failure::OutOfMemoryException();
4658 }
4659 }
4660 }
4661 // No length change implies no change. Return original string if no change.
4662 if (escaped_length == length) {
4663 return source;
4664 }
4665 Object* o = Heap::AllocateRawAsciiString(escaped_length);
4666 if (o->IsFailure()) return o;
4667 String* destination = String::cast(o);
4668 int dest_position = 0;
4669
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004670 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004671 buffer->Rewind();
4672 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00004673 uint16_t chr = buffer->GetNext();
4674 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004675 destination->Set(dest_position, '%');
4676 destination->Set(dest_position+1, 'u');
4677 destination->Set(dest_position+2, hex_chars[chr >> 12]);
4678 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
4679 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
4680 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004681 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00004682 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004683 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004684 dest_position++;
4685 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004686 destination->Set(dest_position, '%');
4687 destination->Set(dest_position+1, hex_chars[chr >> 4]);
4688 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004689 dest_position += 3;
4690 }
4691 }
4692 return destination;
4693}
4694
4695
4696static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
4697 static const signed char kHexValue['g'] = {
4698 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4699 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4700 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4701 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
4702 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4703 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4704 -1, 10, 11, 12, 13, 14, 15 };
4705
4706 if (character1 > 'f') return -1;
4707 int hi = kHexValue[character1];
4708 if (hi == -1) return -1;
4709 if (character2 > 'f') return -1;
4710 int lo = kHexValue[character2];
4711 if (lo == -1) return -1;
4712 return (hi << 4) + lo;
4713}
4714
4715
ager@chromium.org870a0b62008-11-04 11:43:05 +00004716static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004717 int i,
4718 int length,
4719 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004720 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00004721 int32_t hi = 0;
4722 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004723 if (character == '%' &&
4724 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004725 source->Get(i + 1) == 'u' &&
4726 (hi = TwoDigitHex(source->Get(i + 2),
4727 source->Get(i + 3))) != -1 &&
4728 (lo = TwoDigitHex(source->Get(i + 4),
4729 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004730 *step = 6;
4731 return (hi << 8) + lo;
4732 } else if (character == '%' &&
4733 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004734 (lo = TwoDigitHex(source->Get(i + 1),
4735 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004736 *step = 3;
4737 return lo;
4738 } else {
4739 *step = 1;
4740 return character;
4741 }
4742}
4743
4744
4745static Object* Runtime_URIUnescape(Arguments args) {
4746 NoHandleAllocation ha;
4747 ASSERT(args.length() == 1);
4748 CONVERT_CHECKED(String, source, args[0]);
4749
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004750 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004751
4752 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004753 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004754
4755 int unescaped_length = 0;
4756 for (int i = 0; i < length; unescaped_length++) {
4757 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004758 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004759 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004760 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004761 i += step;
4762 }
4763
4764 // No length change implies no change. Return original string if no change.
4765 if (unescaped_length == length)
4766 return source;
4767
4768 Object* o = ascii ?
4769 Heap::AllocateRawAsciiString(unescaped_length) :
4770 Heap::AllocateRawTwoByteString(unescaped_length);
4771 if (o->IsFailure()) return o;
4772 String* destination = String::cast(o);
4773
4774 int dest_position = 0;
4775 for (int i = 0; i < length; dest_position++) {
4776 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004777 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004778 i += step;
4779 }
4780 return destination;
4781}
4782
4783
4784static Object* Runtime_StringParseInt(Arguments args) {
4785 NoHandleAllocation ha;
4786
4787 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004788 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004789
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004790 s->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004791
lrn@chromium.org25156de2010-04-06 13:10:27 +00004792 RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
4793 double value = StringToInt(s, radix);
4794 return Heap::NumberFromDouble(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004795 return Heap::nan_value();
4796}
4797
4798
4799static Object* Runtime_StringParseFloat(Arguments args) {
4800 NoHandleAllocation ha;
4801 CONVERT_CHECKED(String, str, args[0]);
4802
4803 // ECMA-262 section 15.1.2.3, empty string is NaN
4804 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
4805
4806 // Create a number object from the value.
4807 return Heap::NumberFromDouble(value);
4808}
4809
4810
4811static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
4812static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
4813
4814
4815template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004816static Object* ConvertCaseHelper(String* s,
4817 int length,
4818 int input_string_length,
4819 unibrow::Mapping<Converter, 128>* mapping) {
4820 // We try this twice, once with the assumption that the result is no longer
4821 // than the input and, if that assumption breaks, again with the exact
4822 // length. This may not be pretty, but it is nicer than what was here before
4823 // and I hereby claim my vaffel-is.
4824 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004825 // Allocate the resulting string.
4826 //
4827 // NOTE: This assumes that the upper/lower case of an ascii
4828 // character is also ascii. This is currently the case, but it
4829 // might break in the future if we implement more context and locale
4830 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00004831 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004832 ? Heap::AllocateRawAsciiString(length)
4833 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004834 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004835 String* result = String::cast(o);
4836 bool has_changed_character = false;
4837
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004838 // Convert all characters to upper case, assuming that they will fit
4839 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004840 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004841 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004842 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004843 // We can assume that the string is not empty
4844 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004845 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00004846 bool has_next = buffer->has_more();
4847 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004848 int char_length = mapping->get(current, next, chars);
4849 if (char_length == 0) {
4850 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004851 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004852 i++;
4853 } else if (char_length == 1) {
4854 // Common case: converting the letter resulted in one character.
4855 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004856 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004857 has_changed_character = true;
4858 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004859 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004860 // We've assumed that the result would be as long as the
4861 // input but here is a character that converts to several
4862 // characters. No matter, we calculate the exact length
4863 // of the result and try the whole thing again.
4864 //
4865 // Note that this leaves room for optimization. We could just
4866 // memcpy what we already have to the result string. Also,
4867 // the result string is the last object allocated we could
4868 // "realloc" it and probably, in the vast majority of cases,
4869 // extend the existing string to be able to hold the full
4870 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00004871 int next_length = 0;
4872 if (has_next) {
4873 next_length = mapping->get(next, 0, chars);
4874 if (next_length == 0) next_length = 1;
4875 }
4876 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004877 while (buffer->has_more()) {
4878 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00004879 // NOTE: we use 0 as the next character here because, while
4880 // the next character may affect what a character converts to,
4881 // it does not in any case affect the length of what it convert
4882 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004883 int char_length = mapping->get(current, 0, chars);
4884 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00004885 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004886 if (current_length > Smi::kMaxValue) {
4887 Top::context()->mark_out_of_memory();
4888 return Failure::OutOfMemoryException();
4889 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004890 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004891 // Try again with the real length.
4892 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004893 } else {
4894 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004895 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004896 i++;
4897 }
4898 has_changed_character = true;
4899 }
4900 current = next;
4901 }
4902 if (has_changed_character) {
4903 return result;
4904 } else {
4905 // If we didn't actually change anything in doing the conversion
4906 // we simple return the result and let the converted string
4907 // become garbage; there is no reason to keep two identical strings
4908 // alive.
4909 return s;
4910 }
4911}
4912
4913
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004914static inline SeqAsciiString* TryGetSeqAsciiString(String* s) {
4915 if (!s->IsFlat() || !s->IsAsciiRepresentation()) return NULL;
4916 if (s->IsConsString()) {
4917 ASSERT(ConsString::cast(s)->second()->length() == 0);
4918 return SeqAsciiString::cast(ConsString::cast(s)->first());
4919 }
4920 return SeqAsciiString::cast(s);
4921}
4922
4923
4924namespace {
4925
4926struct ToLowerTraits {
4927 typedef unibrow::ToLowercase UnibrowConverter;
4928
4929 static bool ConvertAscii(char* dst, char* src, int length) {
4930 bool changed = false;
4931 for (int i = 0; i < length; ++i) {
4932 char c = src[i];
4933 if ('A' <= c && c <= 'Z') {
4934 c += ('a' - 'A');
4935 changed = true;
4936 }
4937 dst[i] = c;
4938 }
4939 return changed;
4940 }
4941};
4942
4943
4944struct ToUpperTraits {
4945 typedef unibrow::ToUppercase UnibrowConverter;
4946
4947 static bool ConvertAscii(char* dst, char* src, int length) {
4948 bool changed = false;
4949 for (int i = 0; i < length; ++i) {
4950 char c = src[i];
4951 if ('a' <= c && c <= 'z') {
4952 c -= ('a' - 'A');
4953 changed = true;
4954 }
4955 dst[i] = c;
4956 }
4957 return changed;
4958 }
4959};
4960
4961} // namespace
4962
4963
4964template <typename ConvertTraits>
4965static Object* ConvertCase(
4966 Arguments args,
4967 unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004968 NoHandleAllocation ha;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004969 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004970 s->TryFlatten();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004971
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004972 const int length = s->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004973 // Assume that the string is not empty; we need this assumption later
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004974 if (length == 0) return s;
4975
4976 // Simpler handling of ascii strings.
4977 //
4978 // NOTE: This assumes that the upper/lower case of an ascii
4979 // character is also ascii. This is currently the case, but it
4980 // might break in the future if we implement more context and locale
4981 // dependent upper/lower conversions.
4982 SeqAsciiString* seq_ascii = TryGetSeqAsciiString(s);
4983 if (seq_ascii != NULL) {
4984 Object* o = Heap::AllocateRawAsciiString(length);
4985 if (o->IsFailure()) return o;
4986 SeqAsciiString* result = SeqAsciiString::cast(o);
4987 bool has_changed_character = ConvertTraits::ConvertAscii(
4988 result->GetChars(), seq_ascii->GetChars(), length);
4989 return has_changed_character ? result : s;
4990 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004991
4992 Object* answer = ConvertCaseHelper(s, length, length, mapping);
4993 if (answer->IsSmi()) {
4994 // Retry with correct length.
4995 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
4996 }
4997 return answer; // This may be a failure.
4998}
4999
5000
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005001static Object* Runtime_StringToLowerCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005002 return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005003}
5004
5005
5006static Object* Runtime_StringToUpperCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005007 return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005008}
5009
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005010
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005011static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
5012 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
5013}
5014
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005015
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005016static Object* Runtime_StringTrim(Arguments args) {
5017 NoHandleAllocation ha;
5018 ASSERT(args.length() == 3);
5019
5020 CONVERT_CHECKED(String, s, args[0]);
5021 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
5022 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
5023
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005024 s->TryFlatten();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005025 int length = s->length();
5026
5027 int left = 0;
5028 if (trimLeft) {
5029 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
5030 left++;
5031 }
5032 }
5033
5034 int right = length;
5035 if (trimRight) {
5036 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
5037 right--;
5038 }
5039 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005040 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005041}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005042
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005043
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005044template <typename schar, typename pchar>
5045void FindStringIndices(Vector<const schar> subject,
5046 Vector<const pchar> pattern,
5047 ZoneList<int>* indices,
5048 unsigned int limit) {
5049 ASSERT(limit > 0);
5050 // Collect indices of pattern in subject, and the end-of-string index.
5051 // Stop after finding at most limit values.
5052 StringSearchStrategy strategy =
5053 InitializeStringSearch(pattern, sizeof(schar) == 1);
5054 switch (strategy) {
5055 case SEARCH_FAIL: return;
5056 case SEARCH_SHORT: {
5057 int pattern_length = pattern.length();
5058 int index = 0;
5059 while (limit > 0) {
5060 index = SimpleIndexOf(subject, pattern, index);
5061 if (index < 0) return;
5062 indices->Add(index);
5063 index += pattern_length;
5064 limit--;
5065 }
5066 return;
5067 }
5068 case SEARCH_LONG: {
5069 int pattern_length = pattern.length();
5070 int index = 0;
5071 while (limit > 0) {
5072 index = ComplexIndexOf(subject, pattern, index);
5073 if (index < 0) return;
5074 indices->Add(index);
5075 index += pattern_length;
5076 limit--;
5077 }
5078 return;
5079 }
5080 default:
5081 UNREACHABLE();
5082 return;
5083 }
5084}
5085
5086template <typename schar>
5087inline void FindCharIndices(Vector<const schar> subject,
5088 const schar pattern_char,
5089 ZoneList<int>* indices,
5090 unsigned int limit) {
5091 // Collect indices of pattern_char in subject, and the end-of-string index.
5092 // Stop after finding at most limit values.
5093 int index = 0;
5094 while (limit > 0) {
5095 index = SingleCharIndexOf(subject, pattern_char, index);
5096 if (index < 0) return;
5097 indices->Add(index);
5098 index++;
5099 limit--;
5100 }
5101}
5102
5103
5104static Object* Runtime_StringSplit(Arguments args) {
5105 ASSERT(args.length() == 3);
5106 HandleScope handle_scope;
5107 CONVERT_ARG_CHECKED(String, subject, 0);
5108 CONVERT_ARG_CHECKED(String, pattern, 1);
5109 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
5110
5111 int subject_length = subject->length();
5112 int pattern_length = pattern->length();
5113 RUNTIME_ASSERT(pattern_length > 0);
5114
5115 // The limit can be very large (0xffffffffu), but since the pattern
5116 // isn't empty, we can never create more parts than ~half the length
5117 // of the subject.
5118
5119 if (!subject->IsFlat()) FlattenString(subject);
5120
5121 static const int kMaxInitialListCapacity = 16;
5122
5123 ZoneScope scope(DELETE_ON_EXIT);
5124
5125 // Find (up to limit) indices of separator and end-of-string in subject
5126 int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
5127 ZoneList<int> indices(initial_capacity);
5128 if (pattern_length == 1) {
5129 // Special case, go directly to fast single-character split.
5130 AssertNoAllocation nogc;
5131 uc16 pattern_char = pattern->Get(0);
5132 if (subject->IsTwoByteRepresentation()) {
5133 FindCharIndices(subject->ToUC16Vector(), pattern_char,
5134 &indices,
5135 limit);
5136 } else if (pattern_char <= String::kMaxAsciiCharCode) {
5137 FindCharIndices(subject->ToAsciiVector(),
5138 static_cast<char>(pattern_char),
5139 &indices,
5140 limit);
5141 }
5142 } else {
5143 if (!pattern->IsFlat()) FlattenString(pattern);
5144 AssertNoAllocation nogc;
5145 if (subject->IsAsciiRepresentation()) {
5146 Vector<const char> subject_vector = subject->ToAsciiVector();
5147 if (pattern->IsAsciiRepresentation()) {
5148 FindStringIndices(subject_vector,
5149 pattern->ToAsciiVector(),
5150 &indices,
5151 limit);
5152 } else {
5153 FindStringIndices(subject_vector,
5154 pattern->ToUC16Vector(),
5155 &indices,
5156 limit);
5157 }
5158 } else {
5159 Vector<const uc16> subject_vector = subject->ToUC16Vector();
5160 if (pattern->IsAsciiRepresentation()) {
5161 FindStringIndices(subject_vector,
5162 pattern->ToAsciiVector(),
5163 &indices,
5164 limit);
5165 } else {
5166 FindStringIndices(subject_vector,
5167 pattern->ToUC16Vector(),
5168 &indices,
5169 limit);
5170 }
5171 }
5172 }
5173 if (static_cast<uint32_t>(indices.length()) < limit) {
5174 indices.Add(subject_length);
5175 }
5176 // The list indices now contains the end of each part to create.
5177
5178
5179 // Create JSArray of substrings separated by separator.
5180 int part_count = indices.length();
5181
5182 Handle<JSArray> result = Factory::NewJSArray(part_count);
5183 result->set_length(Smi::FromInt(part_count));
5184
5185 ASSERT(result->HasFastElements());
5186
5187 if (part_count == 1 && indices.at(0) == subject_length) {
5188 FixedArray::cast(result->elements())->set(0, *subject);
5189 return *result;
5190 }
5191
5192 Handle<FixedArray> elements(FixedArray::cast(result->elements()));
5193 int part_start = 0;
5194 for (int i = 0; i < part_count; i++) {
5195 HandleScope local_loop_handle;
5196 int part_end = indices.at(i);
5197 Handle<String> substring =
5198 Factory::NewSubString(subject, part_start, part_end);
5199 elements->set(i, *substring);
5200 part_start = part_end + pattern_length;
5201 }
5202
5203 return *result;
5204}
5205
5206
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005207// Copies ascii characters to the given fixed array looking up
5208// one-char strings in the cache. Gives up on the first char that is
5209// not in the cache and fills the remainder with smi zeros. Returns
5210// the length of the successfully copied prefix.
5211static int CopyCachedAsciiCharsToArray(const char* chars,
5212 FixedArray* elements,
5213 int length) {
5214 AssertNoAllocation nogc;
5215 FixedArray* ascii_cache = Heap::single_character_string_cache();
5216 Object* undefined = Heap::undefined_value();
5217 int i;
5218 for (i = 0; i < length; ++i) {
5219 Object* value = ascii_cache->get(chars[i]);
5220 if (value == undefined) break;
5221 ASSERT(!Heap::InNewSpace(value));
5222 elements->set(i, value, SKIP_WRITE_BARRIER);
5223 }
5224 if (i < length) {
5225 ASSERT(Smi::FromInt(0) == 0);
5226 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
5227 }
5228#ifdef DEBUG
5229 for (int j = 0; j < length; ++j) {
5230 Object* element = elements->get(j);
5231 ASSERT(element == Smi::FromInt(0) ||
5232 (element->IsString() && String::cast(element)->LooksValid()));
5233 }
5234#endif
5235 return i;
5236}
5237
5238
5239// Converts a String to JSArray.
5240// For example, "foo" => ["f", "o", "o"].
5241static Object* Runtime_StringToArray(Arguments args) {
5242 HandleScope scope;
5243 ASSERT(args.length() == 1);
5244 CONVERT_ARG_CHECKED(String, s, 0);
5245
5246 s->TryFlatten();
5247 const int length = s->length();
5248
5249 Handle<FixedArray> elements;
5250 if (s->IsFlat() && s->IsAsciiRepresentation()) {
5251 Object* obj = Heap::AllocateUninitializedFixedArray(length);
5252 if (obj->IsFailure()) return obj;
5253 elements = Handle<FixedArray>(FixedArray::cast(obj));
5254
5255 Vector<const char> chars = s->ToAsciiVector();
5256 // Note, this will initialize all elements (not only the prefix)
5257 // to prevent GC from seeing partially initialized array.
5258 int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
5259 *elements,
5260 length);
5261
5262 for (int i = num_copied_from_cache; i < length; ++i) {
5263 elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
5264 }
5265 } else {
5266 elements = Factory::NewFixedArray(length);
5267 for (int i = 0; i < length; ++i) {
5268 elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
5269 }
5270 }
5271
5272#ifdef DEBUG
5273 for (int i = 0; i < length; ++i) {
5274 ASSERT(String::cast(elements->get(i))->length() == 1);
5275 }
5276#endif
5277
5278 return *Factory::NewJSArrayWithElements(elements);
5279}
5280
5281
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00005282bool Runtime::IsUpperCaseChar(uint16_t ch) {
5283 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
5284 int char_length = to_upper_mapping.get(ch, 0, chars);
5285 return char_length == 0;
5286}
5287
5288
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005289static Object* Runtime_NumberToString(Arguments args) {
5290 NoHandleAllocation ha;
5291 ASSERT(args.length() == 1);
5292
5293 Object* number = args[0];
5294 RUNTIME_ASSERT(number->IsNumber());
5295
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005296 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005297}
5298
5299
ager@chromium.org357bf652010-04-12 11:30:10 +00005300static Object* Runtime_NumberToStringSkipCache(Arguments args) {
5301 NoHandleAllocation ha;
5302 ASSERT(args.length() == 1);
5303
5304 Object* number = args[0];
5305 RUNTIME_ASSERT(number->IsNumber());
5306
5307 return Heap::NumberToString(number, false);
5308}
5309
5310
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005311static Object* Runtime_NumberToInteger(Arguments args) {
5312 NoHandleAllocation ha;
5313 ASSERT(args.length() == 1);
5314
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005315 CONVERT_DOUBLE_CHECKED(number, args[0]);
5316
5317 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5318 if (number > 0 && number <= Smi::kMaxValue) {
5319 return Smi::FromInt(static_cast<int>(number));
5320 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005321 return Heap::NumberFromDouble(DoubleToInteger(number));
5322}
5323
5324
5325static Object* Runtime_NumberToJSUint32(Arguments args) {
5326 NoHandleAllocation ha;
5327 ASSERT(args.length() == 1);
5328
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005329 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005330 return Heap::NumberFromUint32(number);
5331}
5332
5333
5334static Object* Runtime_NumberToJSInt32(Arguments args) {
5335 NoHandleAllocation ha;
5336 ASSERT(args.length() == 1);
5337
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005338 CONVERT_DOUBLE_CHECKED(number, args[0]);
5339
5340 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5341 if (number > 0 && number <= Smi::kMaxValue) {
5342 return Smi::FromInt(static_cast<int>(number));
5343 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005344 return Heap::NumberFromInt32(DoubleToInt32(number));
5345}
5346
5347
ager@chromium.org870a0b62008-11-04 11:43:05 +00005348// Converts a Number to a Smi, if possible. Returns NaN if the number is not
5349// a small integer.
5350static Object* Runtime_NumberToSmi(Arguments args) {
5351 NoHandleAllocation ha;
5352 ASSERT(args.length() == 1);
5353
5354 Object* obj = args[0];
5355 if (obj->IsSmi()) {
5356 return obj;
5357 }
5358 if (obj->IsHeapNumber()) {
5359 double value = HeapNumber::cast(obj)->value();
5360 int int_value = FastD2I(value);
5361 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
5362 return Smi::FromInt(int_value);
5363 }
5364 }
5365 return Heap::nan_value();
5366}
5367
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005368
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005369static Object* Runtime_NumberAdd(Arguments args) {
5370 NoHandleAllocation ha;
5371 ASSERT(args.length() == 2);
5372
5373 CONVERT_DOUBLE_CHECKED(x, args[0]);
5374 CONVERT_DOUBLE_CHECKED(y, args[1]);
5375 return Heap::AllocateHeapNumber(x + y);
5376}
5377
5378
5379static Object* Runtime_NumberSub(Arguments args) {
5380 NoHandleAllocation ha;
5381 ASSERT(args.length() == 2);
5382
5383 CONVERT_DOUBLE_CHECKED(x, args[0]);
5384 CONVERT_DOUBLE_CHECKED(y, args[1]);
5385 return Heap::AllocateHeapNumber(x - y);
5386}
5387
5388
5389static Object* Runtime_NumberMul(Arguments args) {
5390 NoHandleAllocation ha;
5391 ASSERT(args.length() == 2);
5392
5393 CONVERT_DOUBLE_CHECKED(x, args[0]);
5394 CONVERT_DOUBLE_CHECKED(y, args[1]);
5395 return Heap::AllocateHeapNumber(x * y);
5396}
5397
5398
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005399static Object* Runtime_NumberUnaryMinus(Arguments args) {
5400 NoHandleAllocation ha;
5401 ASSERT(args.length() == 1);
5402
5403 CONVERT_DOUBLE_CHECKED(x, args[0]);
5404 return Heap::AllocateHeapNumber(-x);
5405}
5406
5407
5408static Object* Runtime_NumberDiv(Arguments args) {
5409 NoHandleAllocation ha;
5410 ASSERT(args.length() == 2);
5411
5412 CONVERT_DOUBLE_CHECKED(x, args[0]);
5413 CONVERT_DOUBLE_CHECKED(y, args[1]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005414 return Heap::NumberFromDouble(x / y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005415}
5416
5417
5418static Object* Runtime_NumberMod(Arguments args) {
5419 NoHandleAllocation ha;
5420 ASSERT(args.length() == 2);
5421
5422 CONVERT_DOUBLE_CHECKED(x, args[0]);
5423 CONVERT_DOUBLE_CHECKED(y, args[1]);
5424
ager@chromium.org3811b432009-10-28 14:53:37 +00005425 x = modulo(x, y);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005426 // NumberFromDouble may return a Smi instead of a Number object
5427 return Heap::NumberFromDouble(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005428}
5429
5430
5431static Object* Runtime_StringAdd(Arguments args) {
5432 NoHandleAllocation ha;
5433 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005434 CONVERT_CHECKED(String, str1, args[0]);
5435 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005436 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00005437 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005438}
5439
5440
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005441template <typename sinkchar>
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005442static inline void StringBuilderConcatHelper(String* special,
5443 sinkchar* sink,
5444 FixedArray* fixed_array,
5445 int array_length) {
5446 int position = 0;
5447 for (int i = 0; i < array_length; i++) {
5448 Object* element = fixed_array->get(i);
5449 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005450 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005451 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005452 int pos;
5453 int len;
5454 if (encoded_slice > 0) {
5455 // Position and length encoded in one smi.
5456 pos = StringBuilderSubstringPosition::decode(encoded_slice);
5457 len = StringBuilderSubstringLength::decode(encoded_slice);
5458 } else {
5459 // Position and length encoded in two smis.
5460 Object* obj = fixed_array->get(++i);
5461 ASSERT(obj->IsSmi());
5462 pos = Smi::cast(obj)->value();
5463 len = -encoded_slice;
5464 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00005465 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00005466 sink + position,
5467 pos,
5468 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005469 position += len;
5470 } else {
5471 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005472 int element_length = string->length();
5473 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005474 position += element_length;
5475 }
5476 }
5477}
5478
5479
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005480static Object* Runtime_StringBuilderConcat(Arguments args) {
5481 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005482 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005483 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005484 if (!args[1]->IsSmi()) {
5485 Top::context()->mark_out_of_memory();
5486 return Failure::OutOfMemoryException();
5487 }
5488 int array_length = Smi::cast(args[1])->value();
5489 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005490
5491 // This assumption is used by the slice encoding in one or two smis.
5492 ASSERT(Smi::kMaxValue >= String::kMaxLength);
5493
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005494 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005495 if (!array->HasFastElements()) {
5496 return Top::Throw(Heap::illegal_argument_symbol());
5497 }
5498 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005499 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005500 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005501 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005502
5503 if (array_length == 0) {
5504 return Heap::empty_string();
5505 } else if (array_length == 1) {
5506 Object* first = fixed_array->get(0);
5507 if (first->IsString()) return first;
5508 }
5509
ager@chromium.org5ec48922009-05-05 07:25:34 +00005510 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005511 int position = 0;
5512 for (int i = 0; i < array_length; i++) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005513 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005514 Object* elt = fixed_array->get(i);
5515 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005516 // Smi encoding of position and length.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005517 int smi_value = Smi::cast(elt)->value();
5518 int pos;
5519 int len;
5520 if (smi_value > 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005521 // Position and length encoded in one smi.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005522 pos = StringBuilderSubstringPosition::decode(smi_value);
5523 len = StringBuilderSubstringLength::decode(smi_value);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005524 } else {
5525 // Position and length encoded in two smis.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005526 len = -smi_value;
5527 // Get the position and check that it is a positive smi.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005528 i++;
5529 if (i >= array_length) {
5530 return Top::Throw(Heap::illegal_argument_symbol());
5531 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005532 Object* next_smi = fixed_array->get(i);
5533 if (!next_smi->IsSmi()) {
5534 return Top::Throw(Heap::illegal_argument_symbol());
5535 }
5536 pos = Smi::cast(next_smi)->value();
5537 if (pos < 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005538 return Top::Throw(Heap::illegal_argument_symbol());
5539 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005540 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005541 ASSERT(pos >= 0);
5542 ASSERT(len >= 0);
5543 if (pos > special_length || len > special_length - pos) {
5544 return Top::Throw(Heap::illegal_argument_symbol());
5545 }
5546 increment = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005547 } else if (elt->IsString()) {
5548 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005549 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005550 increment = element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005551 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005552 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005553 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005554 } else {
5555 return Top::Throw(Heap::illegal_argument_symbol());
5556 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005557 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005558 Top::context()->mark_out_of_memory();
5559 return Failure::OutOfMemoryException();
5560 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005561 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005562 }
5563
5564 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005565 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005566
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005567 if (ascii) {
5568 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005569 if (object->IsFailure()) return object;
5570 SeqAsciiString* answer = SeqAsciiString::cast(object);
5571 StringBuilderConcatHelper(special,
5572 answer->GetChars(),
5573 fixed_array,
5574 array_length);
5575 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005576 } else {
5577 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005578 if (object->IsFailure()) return object;
5579 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
5580 StringBuilderConcatHelper(special,
5581 answer->GetChars(),
5582 fixed_array,
5583 array_length);
5584 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005585 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005586}
5587
5588
5589static Object* Runtime_NumberOr(Arguments args) {
5590 NoHandleAllocation ha;
5591 ASSERT(args.length() == 2);
5592
5593 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5594 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5595 return Heap::NumberFromInt32(x | y);
5596}
5597
5598
5599static Object* Runtime_NumberAnd(Arguments args) {
5600 NoHandleAllocation ha;
5601 ASSERT(args.length() == 2);
5602
5603 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5604 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5605 return Heap::NumberFromInt32(x & y);
5606}
5607
5608
5609static Object* Runtime_NumberXor(Arguments args) {
5610 NoHandleAllocation ha;
5611 ASSERT(args.length() == 2);
5612
5613 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5614 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5615 return Heap::NumberFromInt32(x ^ y);
5616}
5617
5618
5619static Object* Runtime_NumberNot(Arguments args) {
5620 NoHandleAllocation ha;
5621 ASSERT(args.length() == 1);
5622
5623 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5624 return Heap::NumberFromInt32(~x);
5625}
5626
5627
5628static Object* Runtime_NumberShl(Arguments args) {
5629 NoHandleAllocation ha;
5630 ASSERT(args.length() == 2);
5631
5632 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5633 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5634 return Heap::NumberFromInt32(x << (y & 0x1f));
5635}
5636
5637
5638static Object* Runtime_NumberShr(Arguments args) {
5639 NoHandleAllocation ha;
5640 ASSERT(args.length() == 2);
5641
5642 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
5643 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5644 return Heap::NumberFromUint32(x >> (y & 0x1f));
5645}
5646
5647
5648static Object* Runtime_NumberSar(Arguments args) {
5649 NoHandleAllocation ha;
5650 ASSERT(args.length() == 2);
5651
5652 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5653 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5654 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
5655}
5656
5657
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005658static Object* Runtime_NumberEquals(Arguments args) {
5659 NoHandleAllocation ha;
5660 ASSERT(args.length() == 2);
5661
5662 CONVERT_DOUBLE_CHECKED(x, args[0]);
5663 CONVERT_DOUBLE_CHECKED(y, args[1]);
5664 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
5665 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
5666 if (x == y) return Smi::FromInt(EQUAL);
5667 Object* result;
5668 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
5669 result = Smi::FromInt(EQUAL);
5670 } else {
5671 result = Smi::FromInt(NOT_EQUAL);
5672 }
5673 return result;
5674}
5675
5676
5677static Object* Runtime_StringEquals(Arguments args) {
5678 NoHandleAllocation ha;
5679 ASSERT(args.length() == 2);
5680
5681 CONVERT_CHECKED(String, x, args[0]);
5682 CONVERT_CHECKED(String, y, args[1]);
5683
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005684 bool not_equal = !x->Equals(y);
5685 // This is slightly convoluted because the value that signifies
5686 // equality is 0 and inequality is 1 so we have to negate the result
5687 // from String::Equals.
5688 ASSERT(not_equal == 0 || not_equal == 1);
5689 STATIC_CHECK(EQUAL == 0);
5690 STATIC_CHECK(NOT_EQUAL == 1);
5691 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005692}
5693
5694
5695static Object* Runtime_NumberCompare(Arguments args) {
5696 NoHandleAllocation ha;
5697 ASSERT(args.length() == 3);
5698
5699 CONVERT_DOUBLE_CHECKED(x, args[0]);
5700 CONVERT_DOUBLE_CHECKED(y, args[1]);
5701 if (isnan(x) || isnan(y)) return args[2];
5702 if (x == y) return Smi::FromInt(EQUAL);
5703 if (isless(x, y)) return Smi::FromInt(LESS);
5704 return Smi::FromInt(GREATER);
5705}
5706
5707
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005708// Compare two Smis as if they were converted to strings and then
5709// compared lexicographically.
5710static Object* Runtime_SmiLexicographicCompare(Arguments args) {
5711 NoHandleAllocation ha;
5712 ASSERT(args.length() == 2);
5713
5714 // Arrays for the individual characters of the two Smis. Smis are
5715 // 31 bit integers and 10 decimal digits are therefore enough.
5716 static int x_elms[10];
5717 static int y_elms[10];
5718
5719 // Extract the integer values from the Smis.
5720 CONVERT_CHECKED(Smi, x, args[0]);
5721 CONVERT_CHECKED(Smi, y, args[1]);
5722 int x_value = x->value();
5723 int y_value = y->value();
5724
5725 // If the integers are equal so are the string representations.
5726 if (x_value == y_value) return Smi::FromInt(EQUAL);
5727
5728 // If one of the integers are zero the normal integer order is the
5729 // same as the lexicographic order of the string representations.
5730 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
5731
ager@chromium.org32912102009-01-16 10:38:43 +00005732 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005733 // smallest because the char code of '-' is less than the char code
5734 // of any digit. Otherwise, we make both values positive.
5735 if (x_value < 0 || y_value < 0) {
5736 if (y_value >= 0) return Smi::FromInt(LESS);
5737 if (x_value >= 0) return Smi::FromInt(GREATER);
5738 x_value = -x_value;
5739 y_value = -y_value;
5740 }
5741
5742 // Convert the integers to arrays of their decimal digits.
5743 int x_index = 0;
5744 int y_index = 0;
5745 while (x_value > 0) {
5746 x_elms[x_index++] = x_value % 10;
5747 x_value /= 10;
5748 }
5749 while (y_value > 0) {
5750 y_elms[y_index++] = y_value % 10;
5751 y_value /= 10;
5752 }
5753
5754 // Loop through the arrays of decimal digits finding the first place
5755 // where they differ.
5756 while (--x_index >= 0 && --y_index >= 0) {
5757 int diff = x_elms[x_index] - y_elms[y_index];
5758 if (diff != 0) return Smi::FromInt(diff);
5759 }
5760
5761 // If one array is a suffix of the other array, the longest array is
5762 // the representation of the largest of the Smis in the
5763 // lexicographic ordering.
5764 return Smi::FromInt(x_index - y_index);
5765}
5766
5767
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005768static Object* StringInputBufferCompare(String* x, String* y) {
5769 static StringInputBuffer bufx;
5770 static StringInputBuffer bufy;
5771 bufx.Reset(x);
5772 bufy.Reset(y);
5773 while (bufx.has_more() && bufy.has_more()) {
5774 int d = bufx.GetNext() - bufy.GetNext();
5775 if (d < 0) return Smi::FromInt(LESS);
5776 else if (d > 0) return Smi::FromInt(GREATER);
5777 }
5778
5779 // x is (non-trivial) prefix of y:
5780 if (bufy.has_more()) return Smi::FromInt(LESS);
5781 // y is prefix of x:
5782 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
5783}
5784
5785
5786static Object* FlatStringCompare(String* x, String* y) {
5787 ASSERT(x->IsFlat());
5788 ASSERT(y->IsFlat());
5789 Object* equal_prefix_result = Smi::FromInt(EQUAL);
5790 int prefix_length = x->length();
5791 if (y->length() < prefix_length) {
5792 prefix_length = y->length();
5793 equal_prefix_result = Smi::FromInt(GREATER);
5794 } else if (y->length() > prefix_length) {
5795 equal_prefix_result = Smi::FromInt(LESS);
5796 }
5797 int r;
5798 if (x->IsAsciiRepresentation()) {
5799 Vector<const char> x_chars = x->ToAsciiVector();
5800 if (y->IsAsciiRepresentation()) {
5801 Vector<const char> y_chars = y->ToAsciiVector();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005802 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005803 } else {
5804 Vector<const uc16> y_chars = y->ToUC16Vector();
5805 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5806 }
5807 } else {
5808 Vector<const uc16> x_chars = x->ToUC16Vector();
5809 if (y->IsAsciiRepresentation()) {
5810 Vector<const char> y_chars = y->ToAsciiVector();
5811 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5812 } else {
5813 Vector<const uc16> y_chars = y->ToUC16Vector();
5814 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5815 }
5816 }
5817 Object* result;
5818 if (r == 0) {
5819 result = equal_prefix_result;
5820 } else {
5821 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
5822 }
5823 ASSERT(result == StringInputBufferCompare(x, y));
5824 return result;
5825}
5826
5827
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005828static Object* Runtime_StringCompare(Arguments args) {
5829 NoHandleAllocation ha;
5830 ASSERT(args.length() == 2);
5831
5832 CONVERT_CHECKED(String, x, args[0]);
5833 CONVERT_CHECKED(String, y, args[1]);
5834
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005835 Counters::string_compare_runtime.Increment();
5836
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005837 // A few fast case tests before we flatten.
5838 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005839 if (y->length() == 0) {
5840 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005841 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005842 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005843 return Smi::FromInt(LESS);
5844 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005845
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005846 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005847 if (d < 0) return Smi::FromInt(LESS);
5848 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005849
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005850 Object* obj = Heap::PrepareForCompare(x);
5851 if (obj->IsFailure()) return obj;
5852 obj = Heap::PrepareForCompare(y);
5853 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005854
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005855 return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
5856 : StringInputBufferCompare(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005857}
5858
5859
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005860static Object* Runtime_Math_acos(Arguments args) {
5861 NoHandleAllocation ha;
5862 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005863 Counters::math_acos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005864
5865 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005866 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005867}
5868
5869
5870static Object* Runtime_Math_asin(Arguments args) {
5871 NoHandleAllocation ha;
5872 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005873 Counters::math_asin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005874
5875 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005876 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005877}
5878
5879
5880static Object* Runtime_Math_atan(Arguments args) {
5881 NoHandleAllocation ha;
5882 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005883 Counters::math_atan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005884
5885 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005886 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005887}
5888
5889
5890static Object* Runtime_Math_atan2(Arguments args) {
5891 NoHandleAllocation ha;
5892 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005893 Counters::math_atan2.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005894
5895 CONVERT_DOUBLE_CHECKED(x, args[0]);
5896 CONVERT_DOUBLE_CHECKED(y, args[1]);
5897 double result;
5898 if (isinf(x) && isinf(y)) {
5899 // Make sure that the result in case of two infinite arguments
5900 // is a multiple of Pi / 4. The sign of the result is determined
5901 // by the first argument (x) and the sign of the second argument
5902 // determines the multiplier: one or three.
5903 static double kPiDividedBy4 = 0.78539816339744830962;
5904 int multiplier = (x < 0) ? -1 : 1;
5905 if (y < 0) multiplier *= 3;
5906 result = multiplier * kPiDividedBy4;
5907 } else {
5908 result = atan2(x, y);
5909 }
5910 return Heap::AllocateHeapNumber(result);
5911}
5912
5913
5914static Object* Runtime_Math_ceil(Arguments args) {
5915 NoHandleAllocation ha;
5916 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005917 Counters::math_ceil.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005918
5919 CONVERT_DOUBLE_CHECKED(x, args[0]);
5920 return Heap::NumberFromDouble(ceiling(x));
5921}
5922
5923
5924static Object* Runtime_Math_cos(Arguments args) {
5925 NoHandleAllocation ha;
5926 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005927 Counters::math_cos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005928
5929 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005930 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005931}
5932
5933
5934static Object* Runtime_Math_exp(Arguments args) {
5935 NoHandleAllocation ha;
5936 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005937 Counters::math_exp.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005938
5939 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005940 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005941}
5942
5943
5944static Object* Runtime_Math_floor(Arguments args) {
5945 NoHandleAllocation ha;
5946 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005947 Counters::math_floor.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005948
5949 CONVERT_DOUBLE_CHECKED(x, args[0]);
5950 return Heap::NumberFromDouble(floor(x));
5951}
5952
5953
5954static Object* Runtime_Math_log(Arguments args) {
5955 NoHandleAllocation ha;
5956 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005957 Counters::math_log.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005958
5959 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005960 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005961}
5962
5963
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005964// Helper function to compute x^y, where y is known to be an
5965// integer. Uses binary decomposition to limit the number of
5966// multiplications; see the discussion in "Hacker's Delight" by Henry
5967// S. Warren, Jr., figure 11-6, page 213.
5968static double powi(double x, int y) {
5969 ASSERT(y != kMinInt);
5970 unsigned n = (y < 0) ? -y : y;
5971 double m = x;
5972 double p = 1;
5973 while (true) {
5974 if ((n & 1) != 0) p *= m;
5975 n >>= 1;
5976 if (n == 0) {
5977 if (y < 0) {
5978 // Unfortunately, we have to be careful when p has reached
5979 // infinity in the computation, because sometimes the higher
5980 // internal precision in the pow() implementation would have
5981 // given us a finite p. This happens very rarely.
5982 double result = 1.0 / p;
5983 return (result == 0 && isinf(p))
5984 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
5985 : result;
5986 } else {
5987 return p;
5988 }
5989 }
5990 m *= m;
5991 }
5992}
5993
5994
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005995static Object* Runtime_Math_pow(Arguments args) {
5996 NoHandleAllocation ha;
5997 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005998 Counters::math_pow.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005999
6000 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006001
6002 // If the second argument is a smi, it is much faster to call the
6003 // custom powi() function than the generic pow().
6004 if (args[1]->IsSmi()) {
6005 int y = Smi::cast(args[1])->value();
6006 return Heap::AllocateHeapNumber(powi(x, y));
6007 }
6008
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006009 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00006010
6011 if (!isinf(x)) {
6012 if (y == 0.5) {
6013 // It's not uncommon to use Math.pow(x, 0.5) to compute the
6014 // square root of a number. To speed up such computations, we
6015 // explictly check for this case and use the sqrt() function
6016 // which is faster than pow().
6017 return Heap::AllocateHeapNumber(sqrt(x));
6018 } else if (y == -0.5) {
6019 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
6020 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
6021 }
6022 }
6023
6024 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006025 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006026 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6027 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006028 } else {
6029 return Heap::AllocateHeapNumber(pow(x, y));
6030 }
6031}
6032
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006033// Fast version of Math.pow if we know that y is not an integer and
6034// y is not -0.5 or 0.5. Used as slowcase from codegen.
6035static Object* Runtime_Math_pow_cfunction(Arguments args) {
6036 NoHandleAllocation ha;
6037 ASSERT(args.length() == 2);
6038 CONVERT_DOUBLE_CHECKED(x, args[0]);
6039 CONVERT_DOUBLE_CHECKED(y, args[1]);
6040 if (y == 0) {
6041 return Smi::FromInt(1);
6042 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6043 return Heap::nan_value();
6044 } else {
6045 return Heap::AllocateHeapNumber(pow(x, y));
6046 }
6047}
6048
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006049
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006050static Object* Runtime_RoundNumber(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006051 NoHandleAllocation ha;
6052 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006053 Counters::math_round.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006054
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006055 if (!args[0]->IsHeapNumber()) {
6056 // Must be smi. Return the argument unchanged for all the other types
6057 // to make fuzz-natives test happy.
6058 return args[0];
6059 }
6060
6061 HeapNumber* number = reinterpret_cast<HeapNumber*>(args[0]);
6062
6063 double value = number->value();
6064 int exponent = number->get_exponent();
6065 int sign = number->get_sign();
6066
6067 // We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
6068 // should be rounded to 2^30, which is not smi.
6069 if (!sign && exponent <= kSmiValueSize - 3) {
6070 return Smi::FromInt(static_cast<int>(value + 0.5));
6071 }
6072
6073 // If the magnitude is big enough, there's no place for fraction part. If we
6074 // try to add 0.5 to this number, 1.0 will be added instead.
6075 if (exponent >= 52) {
6076 return number;
6077 }
6078
6079 if (sign && value >= -0.5) return Heap::minus_zero_value();
6080
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006081 // Do not call NumberFromDouble() to avoid extra checks.
6082 return Heap::AllocateHeapNumber(floor(value + 0.5));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006083}
6084
6085
6086static Object* Runtime_Math_sin(Arguments args) {
6087 NoHandleAllocation ha;
6088 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006089 Counters::math_sin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006090
6091 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006092 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006093}
6094
6095
6096static Object* Runtime_Math_sqrt(Arguments args) {
6097 NoHandleAllocation ha;
6098 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006099 Counters::math_sqrt.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006100
6101 CONVERT_DOUBLE_CHECKED(x, args[0]);
6102 return Heap::AllocateHeapNumber(sqrt(x));
6103}
6104
6105
6106static Object* Runtime_Math_tan(Arguments args) {
6107 NoHandleAllocation ha;
6108 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006109 Counters::math_tan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006110
6111 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006112 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006113}
6114
6115
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006116static int MakeDay(int year, int month, int day) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006117 static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
6118 181, 212, 243, 273, 304, 334};
6119 static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
6120 182, 213, 244, 274, 305, 335};
6121
6122 year += month / 12;
6123 month %= 12;
6124 if (month < 0) {
6125 year--;
6126 month += 12;
6127 }
6128
6129 ASSERT(month >= 0);
6130 ASSERT(month < 12);
6131
6132 // year_delta is an arbitrary number such that:
6133 // a) year_delta = -1 (mod 400)
6134 // b) year + year_delta > 0 for years in the range defined by
6135 // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
6136 // Jan 1 1970. This is required so that we don't run into integer
6137 // division of negative numbers.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006138 // c) there shouldn't be an overflow for 32-bit integers in the following
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006139 // operations.
6140 static const int year_delta = 399999;
6141 static const int base_day = 365 * (1970 + year_delta) +
6142 (1970 + year_delta) / 4 -
6143 (1970 + year_delta) / 100 +
6144 (1970 + year_delta) / 400;
6145
6146 int year1 = year + year_delta;
6147 int day_from_year = 365 * year1 +
6148 year1 / 4 -
6149 year1 / 100 +
6150 year1 / 400 -
6151 base_day;
6152
6153 if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006154 return day_from_year + day_from_month[month] + day - 1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006155 }
6156
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006157 return day_from_year + day_from_month_leap[month] + day - 1;
6158}
6159
6160
6161static Object* Runtime_DateMakeDay(Arguments args) {
6162 NoHandleAllocation ha;
6163 ASSERT(args.length() == 3);
6164
6165 CONVERT_SMI_CHECKED(year, args[0]);
6166 CONVERT_SMI_CHECKED(month, args[1]);
6167 CONVERT_SMI_CHECKED(date, args[2]);
6168
6169 return Smi::FromInt(MakeDay(year, month, date));
6170}
6171
6172
6173static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
6174static const int kDaysIn4Years = 4 * 365 + 1;
6175static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
6176static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
6177static const int kDays1970to2000 = 30 * 365 + 7;
6178static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
6179 kDays1970to2000;
6180static const int kYearsOffset = 400000;
6181
6182static const char kDayInYear[] = {
6183 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6184 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6185 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6186 22, 23, 24, 25, 26, 27, 28,
6187 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6188 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6189 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6190 22, 23, 24, 25, 26, 27, 28, 29, 30,
6191 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6192 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6193 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6194 22, 23, 24, 25, 26, 27, 28, 29, 30,
6195 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6196 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6197 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6198 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6199 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6200 22, 23, 24, 25, 26, 27, 28, 29, 30,
6201 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6202 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6203 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6204 22, 23, 24, 25, 26, 27, 28, 29, 30,
6205 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6206 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6207
6208 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6209 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6210 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6211 22, 23, 24, 25, 26, 27, 28,
6212 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6213 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6214 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6215 22, 23, 24, 25, 26, 27, 28, 29, 30,
6216 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6217 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6218 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6219 22, 23, 24, 25, 26, 27, 28, 29, 30,
6220 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6221 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6222 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6223 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6224 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6225 22, 23, 24, 25, 26, 27, 28, 29, 30,
6226 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6227 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6228 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6229 22, 23, 24, 25, 26, 27, 28, 29, 30,
6230 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6231 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6232
6233 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6234 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6235 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6236 22, 23, 24, 25, 26, 27, 28, 29,
6237 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6238 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6239 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6240 22, 23, 24, 25, 26, 27, 28, 29, 30,
6241 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6242 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6243 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6244 22, 23, 24, 25, 26, 27, 28, 29, 30,
6245 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6246 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6247 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6248 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6249 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6250 22, 23, 24, 25, 26, 27, 28, 29, 30,
6251 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6252 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6253 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6254 22, 23, 24, 25, 26, 27, 28, 29, 30,
6255 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6256 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6257
6258 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6259 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6260 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6261 22, 23, 24, 25, 26, 27, 28,
6262 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6263 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6264 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6265 22, 23, 24, 25, 26, 27, 28, 29, 30,
6266 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6267 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6268 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6269 22, 23, 24, 25, 26, 27, 28, 29, 30,
6270 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6271 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6272 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6273 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6274 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6275 22, 23, 24, 25, 26, 27, 28, 29, 30,
6276 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6277 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6278 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6279 22, 23, 24, 25, 26, 27, 28, 29, 30,
6280 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6281 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
6282
6283static const char kMonthInYear[] = {
6284 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,
6285 0, 0, 0, 0, 0, 0,
6286 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,
6287 1, 1, 1,
6288 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,
6289 2, 2, 2, 2, 2, 2,
6290 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,
6291 3, 3, 3, 3, 3,
6292 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,
6293 4, 4, 4, 4, 4, 4,
6294 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,
6295 5, 5, 5, 5, 5,
6296 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,
6297 6, 6, 6, 6, 6, 6,
6298 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,
6299 7, 7, 7, 7, 7, 7,
6300 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,
6301 8, 8, 8, 8, 8,
6302 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,
6303 9, 9, 9, 9, 9, 9,
6304 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6305 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6306 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6307 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6308
6309 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,
6310 0, 0, 0, 0, 0, 0,
6311 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,
6312 1, 1, 1,
6313 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,
6314 2, 2, 2, 2, 2, 2,
6315 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,
6316 3, 3, 3, 3, 3,
6317 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,
6318 4, 4, 4, 4, 4, 4,
6319 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,
6320 5, 5, 5, 5, 5,
6321 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,
6322 6, 6, 6, 6, 6, 6,
6323 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,
6324 7, 7, 7, 7, 7, 7,
6325 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,
6326 8, 8, 8, 8, 8,
6327 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,
6328 9, 9, 9, 9, 9, 9,
6329 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6330 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6331 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6332 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6333
6334 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,
6335 0, 0, 0, 0, 0, 0,
6336 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,
6337 1, 1, 1, 1,
6338 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,
6339 2, 2, 2, 2, 2, 2,
6340 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,
6341 3, 3, 3, 3, 3,
6342 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,
6343 4, 4, 4, 4, 4, 4,
6344 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,
6345 5, 5, 5, 5, 5,
6346 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,
6347 6, 6, 6, 6, 6, 6,
6348 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,
6349 7, 7, 7, 7, 7, 7,
6350 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,
6351 8, 8, 8, 8, 8,
6352 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,
6353 9, 9, 9, 9, 9, 9,
6354 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6355 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6356 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6357 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6358
6359 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,
6360 0, 0, 0, 0, 0, 0,
6361 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,
6362 1, 1, 1,
6363 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,
6364 2, 2, 2, 2, 2, 2,
6365 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,
6366 3, 3, 3, 3, 3,
6367 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,
6368 4, 4, 4, 4, 4, 4,
6369 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,
6370 5, 5, 5, 5, 5,
6371 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,
6372 6, 6, 6, 6, 6, 6,
6373 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,
6374 7, 7, 7, 7, 7, 7,
6375 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,
6376 8, 8, 8, 8, 8,
6377 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,
6378 9, 9, 9, 9, 9, 9,
6379 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6380 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6381 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6382 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
6383
6384
6385// This function works for dates from 1970 to 2099.
6386static inline void DateYMDFromTimeAfter1970(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006387 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006388#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006389 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006390#endif
6391
6392 year = 1970 + (4 * date + 2) / kDaysIn4Years;
6393 date %= kDaysIn4Years;
6394
6395 month = kMonthInYear[date];
6396 day = kDayInYear[date];
6397
6398 ASSERT(MakeDay(year, month, day) == save_date);
6399}
6400
6401
6402static inline void DateYMDFromTimeSlow(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006403 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006404#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006405 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006406#endif
6407
6408 date += kDaysOffset;
6409 year = 400 * (date / kDaysIn400Years) - kYearsOffset;
6410 date %= kDaysIn400Years;
6411
6412 ASSERT(MakeDay(year, 0, 1) + date == save_date);
6413
6414 date--;
6415 int yd1 = date / kDaysIn100Years;
6416 date %= kDaysIn100Years;
6417 year += 100 * yd1;
6418
6419 date++;
6420 int yd2 = date / kDaysIn4Years;
6421 date %= kDaysIn4Years;
6422 year += 4 * yd2;
6423
6424 date--;
6425 int yd3 = date / 365;
6426 date %= 365;
6427 year += yd3;
6428
6429 bool is_leap = (!yd1 || yd2) && !yd3;
6430
6431 ASSERT(date >= -1);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006432 ASSERT(is_leap || (date >= 0));
6433 ASSERT((date < 365) || (is_leap && (date < 366)));
6434 ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0))));
6435 ASSERT(is_leap || ((MakeDay(year, 0, 1) + date) == save_date));
6436 ASSERT(!is_leap || ((MakeDay(year, 0, 1) + date + 1) == save_date));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006437
6438 if (is_leap) {
6439 day = kDayInYear[2*365 + 1 + date];
6440 month = kMonthInYear[2*365 + 1 + date];
6441 } else {
6442 day = kDayInYear[date];
6443 month = kMonthInYear[date];
6444 }
6445
6446 ASSERT(MakeDay(year, month, day) == save_date);
6447}
6448
6449
6450static inline void DateYMDFromTime(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006451 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006452 if (date >= 0 && date < 32 * kDaysIn4Years) {
6453 DateYMDFromTimeAfter1970(date, year, month, day);
6454 } else {
6455 DateYMDFromTimeSlow(date, year, month, day);
6456 }
6457}
6458
6459
6460static Object* Runtime_DateYMDFromTime(Arguments args) {
6461 NoHandleAllocation ha;
6462 ASSERT(args.length() == 2);
6463
6464 CONVERT_DOUBLE_CHECKED(t, args[0]);
6465 CONVERT_CHECKED(JSArray, res_array, args[1]);
6466
6467 int year, month, day;
6468 DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
6469
6470 res_array->SetElement(0, Smi::FromInt(year));
6471 res_array->SetElement(1, Smi::FromInt(month));
6472 res_array->SetElement(2, Smi::FromInt(day));
6473
6474 return Heap::undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006475}
6476
6477
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006478static Object* Runtime_NewArgumentsFast(Arguments args) {
6479 NoHandleAllocation ha;
6480 ASSERT(args.length() == 3);
6481
6482 JSFunction* callee = JSFunction::cast(args[0]);
6483 Object** parameters = reinterpret_cast<Object**>(args[1]);
6484 const int length = Smi::cast(args[2])->value();
6485
6486 Object* result = Heap::AllocateArgumentsObject(callee, length);
6487 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006488 // Allocate the elements if needed.
6489 if (length > 0) {
6490 // Allocate the fixed array.
6491 Object* obj = Heap::AllocateRawFixedArray(length);
6492 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006493
6494 AssertNoAllocation no_gc;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006495 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
6496 FixedArray* array = FixedArray::cast(obj);
6497 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006498
6499 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006500 for (int i = 0; i < length; i++) {
6501 array->set(i, *--parameters, mode);
6502 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006503 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006504 }
6505 return result;
6506}
6507
6508
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006509static Object* Runtime_NewClosure(Arguments args) {
6510 HandleScope scope;
6511 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00006512 CONVERT_ARG_CHECKED(Context, context, 0);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006513 CONVERT_ARG_CHECKED(SharedFunctionInfo, shared, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006514
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00006515 PretenureFlag pretenure = (context->global_context() == *context)
6516 ? TENURED // Allocate global closures in old space.
6517 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006518 Handle<JSFunction> result =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006519 Factory::NewFunctionFromSharedFunctionInfo(shared, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006520 return *result;
6521}
6522
6523
ager@chromium.org5c838252010-02-19 08:53:10 +00006524static Code* ComputeConstructStub(Handle<JSFunction> function) {
6525 Handle<Object> prototype = Factory::null_value();
6526 if (function->has_instance_prototype()) {
6527 prototype = Handle<Object>(function->instance_prototype());
6528 }
6529 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006530 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00006531 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006532 if (code->IsFailure()) {
6533 return Builtins::builtin(Builtins::JSConstructStubGeneric);
6534 }
6535 return Code::cast(code);
6536 }
6537
ager@chromium.org5c838252010-02-19 08:53:10 +00006538 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006539}
6540
6541
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006542static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006543 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006544 ASSERT(args.length() == 1);
6545
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006546 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006547
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006548 // If the constructor isn't a proper function we throw a type error.
6549 if (!constructor->IsJSFunction()) {
6550 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6551 Handle<Object> type_error =
6552 Factory::NewTypeError("not_constructor", arguments);
6553 return Top::Throw(*type_error);
6554 }
6555
6556 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006557
6558 // If function should not have prototype, construction is not allowed. In this
6559 // case generated code bailouts here, since function has no initial_map.
6560 if (!function->should_have_prototype()) {
6561 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6562 Handle<Object> type_error =
6563 Factory::NewTypeError("not_constructor", arguments);
6564 return Top::Throw(*type_error);
6565 }
6566
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006567#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006568 // Handle stepping into constructors if step into is active.
6569 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006570 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006571 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006572#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006573
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006574 if (function->has_initial_map()) {
6575 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006576 // The 'Function' function ignores the receiver object when
6577 // called using 'new' and creates a new JSFunction object that
6578 // is returned. The receiver object is only used for error
6579 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006580 // JSFunction. Factory::NewJSObject() should not be used to
6581 // allocate JSFunctions since it does not properly initialize
6582 // the shared part of the function. Since the receiver is
6583 // ignored anyway, we use the global object as the receiver
6584 // instead of a new JSFunction object. This way, errors are
6585 // reported the same way whether or not 'Function' is called
6586 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006587 return Top::context()->global();
6588 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006589 }
6590
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006591 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006592 Handle<SharedFunctionInfo> shared(function->shared());
6593 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006594
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006595 bool first_allocation = !function->has_initial_map();
6596 Handle<JSObject> result = Factory::NewJSObject(function);
6597 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006598 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00006599 ComputeConstructStub(Handle<JSFunction>(function)));
6600 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006601 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006602
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006603 Counters::constructed_objects.Increment();
6604 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006605
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006606 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006607}
6608
6609
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006610static Object* Runtime_LazyCompile(Arguments args) {
6611 HandleScope scope;
6612 ASSERT(args.length() == 1);
6613
6614 Handle<JSFunction> function = args.at<JSFunction>(0);
6615#ifdef DEBUG
6616 if (FLAG_trace_lazy) {
6617 PrintF("[lazy: ");
6618 function->shared()->name()->Print();
6619 PrintF("]\n");
6620 }
6621#endif
6622
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006623 // Compile the target function. Here we compile using CompileLazyInLoop in
6624 // order to get the optimized version. This helps code like delta-blue
6625 // that calls performance-critical routines through constructors. A
6626 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
6627 // direct call. Since the in-loop tracking takes place through CallICs
6628 // this means that things called through constructors are never known to
6629 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006630 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006631 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006632 return Failure::Exception();
6633 }
6634
6635 return function->code();
6636}
6637
6638
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006639static Object* Runtime_GetFunctionDelegate(Arguments args) {
6640 HandleScope scope;
6641 ASSERT(args.length() == 1);
6642 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6643 return *Execution::GetFunctionDelegate(args.at<Object>(0));
6644}
6645
6646
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00006647static Object* Runtime_GetConstructorDelegate(Arguments args) {
6648 HandleScope scope;
6649 ASSERT(args.length() == 1);
6650 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6651 return *Execution::GetConstructorDelegate(args.at<Object>(0));
6652}
6653
6654
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006655static Object* Runtime_NewContext(Arguments args) {
6656 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00006657 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006658
kasper.lund7276f142008-07-30 08:49:36 +00006659 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006660 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
6661 Object* result = Heap::AllocateFunctionContext(length, function);
6662 if (result->IsFailure()) return result;
6663
6664 Top::set_context(Context::cast(result));
6665
kasper.lund7276f142008-07-30 08:49:36 +00006666 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006667}
6668
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006669static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006670 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006671 Object* js_object = object;
6672 if (!js_object->IsJSObject()) {
6673 js_object = js_object->ToObject();
6674 if (js_object->IsFailure()) {
6675 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006676 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006677 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006678 Handle<Object> result =
6679 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
6680 return Top::Throw(*result);
6681 }
6682 }
6683
6684 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006685 Heap::AllocateWithContext(Top::context(),
6686 JSObject::cast(js_object),
6687 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006688 if (result->IsFailure()) return result;
6689
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006690 Context* context = Context::cast(result);
6691 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006692
kasper.lund7276f142008-07-30 08:49:36 +00006693 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006694}
6695
6696
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006697static Object* Runtime_PushContext(Arguments args) {
6698 NoHandleAllocation ha;
6699 ASSERT(args.length() == 1);
6700 return PushContextHelper(args[0], false);
6701}
6702
6703
6704static Object* Runtime_PushCatchContext(Arguments args) {
6705 NoHandleAllocation ha;
6706 ASSERT(args.length() == 1);
6707 return PushContextHelper(args[0], true);
6708}
6709
6710
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006711static Object* Runtime_LookupContext(Arguments args) {
6712 HandleScope scope;
6713 ASSERT(args.length() == 2);
6714
6715 CONVERT_ARG_CHECKED(Context, context, 0);
6716 CONVERT_ARG_CHECKED(String, name, 1);
6717
6718 int index;
6719 PropertyAttributes attributes;
6720 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006721 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006722 context->Lookup(name, flags, &index, &attributes);
6723
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006724 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006725 ASSERT(holder->IsJSObject());
6726 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006727 }
6728
6729 // No intermediate context found. Use global object by default.
6730 return Top::context()->global();
6731}
6732
6733
ager@chromium.orga1645e22009-09-09 19:27:10 +00006734// A mechanism to return a pair of Object pointers in registers (if possible).
6735// How this is achieved is calling convention-dependent.
6736// All currently supported x86 compiles uses calling conventions that are cdecl
6737// variants where a 64-bit value is returned in two 32-bit registers
6738// (edx:eax on ia32, r1:r0 on ARM).
6739// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
6740// In Win64 calling convention, a struct of two pointers is returned in memory,
6741// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006742#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006743struct ObjectPair {
6744 Object* x;
6745 Object* y;
6746};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006747
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006748static inline ObjectPair MakePair(Object* x, Object* y) {
6749 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006750 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
6751 // In Win64 they are assigned to a hidden first argument.
6752 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006753}
6754#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006755typedef uint64_t ObjectPair;
6756static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006757 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006758 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006759}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006760#endif
6761
6762
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006763static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006764 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
6765 USE(attributes);
6766 return x->IsTheHole() ? Heap::undefined_value() : x;
6767}
6768
6769
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006770static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
6771 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006772 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006773 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006774 JSFunction* context_extension_function =
6775 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006776 // If the holder isn't a context extension object, we just return it
6777 // as the receiver. This allows arguments objects to be used as
6778 // receivers, but only if they are put in the context scope chain
6779 // explicitly via a with-statement.
6780 Object* constructor = holder->map()->constructor();
6781 if (constructor != context_extension_function) return holder;
6782 // Fall back to using the global object as the receiver if the
6783 // property turns out to be a local variable allocated in a context
6784 // extension object - introduced via eval.
6785 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006786}
6787
6788
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006789static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006790 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00006791 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006792
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006793 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00006794 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006795 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006796 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006797 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006798
6799 int index;
6800 PropertyAttributes attributes;
6801 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006802 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006803 context->Lookup(name, flags, &index, &attributes);
6804
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006805 // If the index is non-negative, the slot has been found in a local
6806 // variable or a parameter. Read it from the context object or the
6807 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006808 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006809 // If the "property" we were looking for is a local variable or an
6810 // argument in a context, the receiver is the global object; see
6811 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
6812 JSObject* receiver = Top::context()->global()->global_receiver();
6813 Object* value = (holder->IsContext())
6814 ? Context::cast(*holder)->get(index)
6815 : JSObject::cast(*holder)->GetElement(index);
6816 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006817 }
6818
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006819 // If the holder is found, we read the property from it.
6820 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006821 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006822 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006823 JSObject* receiver;
6824 if (object->IsGlobalObject()) {
6825 receiver = GlobalObject::cast(object)->global_receiver();
6826 } else if (context->is_exception_holder(*holder)) {
6827 receiver = Top::context()->global()->global_receiver();
6828 } else {
6829 receiver = ComputeReceiverForNonGlobal(object);
6830 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006831 // No need to unhole the value here. This is taken care of by the
6832 // GetProperty function.
6833 Object* value = object->GetProperty(*name);
6834 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006835 }
6836
6837 if (throw_error) {
6838 // The property doesn't exist - throw exception.
6839 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006840 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006841 return MakePair(Top::Throw(*reference_error), NULL);
6842 } else {
6843 // The property doesn't exist - return undefined
6844 return MakePair(Heap::undefined_value(), Heap::undefined_value());
6845 }
6846}
6847
6848
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006849static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006850 return LoadContextSlotHelper(args, true);
6851}
6852
6853
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006854static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006855 return LoadContextSlotHelper(args, false);
6856}
6857
6858
6859static Object* Runtime_StoreContextSlot(Arguments args) {
6860 HandleScope scope;
6861 ASSERT(args.length() == 3);
6862
6863 Handle<Object> value(args[0]);
6864 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006865 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006866
6867 int index;
6868 PropertyAttributes attributes;
6869 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006870 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006871 context->Lookup(name, flags, &index, &attributes);
6872
6873 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006874 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006875 // Ignore if read_only variable.
6876 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006877 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006878 }
6879 } else {
6880 ASSERT((attributes & READ_ONLY) == 0);
6881 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006882 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006883 USE(result);
6884 ASSERT(!result->IsFailure());
6885 }
6886 return *value;
6887 }
6888
6889 // Slow case: The property is not in a FixedArray context.
6890 // It is either in an JSObject extension context or it was not found.
6891 Handle<JSObject> context_ext;
6892
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006893 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006894 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006895 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006896 } else {
6897 // The property was not found. It needs to be stored in the global context.
6898 ASSERT(attributes == ABSENT);
6899 attributes = NONE;
6900 context_ext = Handle<JSObject>(Top::context()->global());
6901 }
6902
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006903 // Set the property, but ignore if read_only variable on the context
6904 // extension object itself.
6905 if ((attributes & READ_ONLY) == 0 ||
6906 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006907 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
6908 if (set.is_null()) {
6909 // Failure::Exception is converted to a null handle in the
6910 // handle-based methods such as SetProperty. We therefore need
6911 // to convert null handles back to exceptions.
6912 ASSERT(Top::has_pending_exception());
6913 return Failure::Exception();
6914 }
6915 }
6916 return *value;
6917}
6918
6919
6920static Object* Runtime_Throw(Arguments args) {
6921 HandleScope scope;
6922 ASSERT(args.length() == 1);
6923
6924 return Top::Throw(args[0]);
6925}
6926
6927
6928static Object* Runtime_ReThrow(Arguments args) {
6929 HandleScope scope;
6930 ASSERT(args.length() == 1);
6931
6932 return Top::ReThrow(args[0]);
6933}
6934
6935
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006936static Object* Runtime_PromoteScheduledException(Arguments args) {
6937 ASSERT_EQ(0, args.length());
6938 return Top::PromoteScheduledException();
6939}
6940
6941
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006942static Object* Runtime_ThrowReferenceError(Arguments args) {
6943 HandleScope scope;
6944 ASSERT(args.length() == 1);
6945
6946 Handle<Object> name(args[0]);
6947 Handle<Object> reference_error =
6948 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
6949 return Top::Throw(*reference_error);
6950}
6951
6952
6953static Object* Runtime_StackOverflow(Arguments args) {
6954 NoHandleAllocation na;
6955 return Top::StackOverflow();
6956}
6957
6958
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006959static Object* Runtime_StackGuard(Arguments args) {
6960 ASSERT(args.length() == 1);
6961
6962 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006963 if (StackGuard::IsStackOverflow()) {
6964 return Runtime_StackOverflow(args);
6965 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006966
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006967 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006968}
6969
6970
6971// NOTE: These PrintXXX functions are defined for all builds (not just
6972// DEBUG builds) because we may want to be able to trace function
6973// calls in all modes.
6974static void PrintString(String* str) {
6975 // not uncommon to have empty strings
6976 if (str->length() > 0) {
6977 SmartPointer<char> s =
6978 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
6979 PrintF("%s", *s);
6980 }
6981}
6982
6983
6984static void PrintObject(Object* obj) {
6985 if (obj->IsSmi()) {
6986 PrintF("%d", Smi::cast(obj)->value());
6987 } else if (obj->IsString() || obj->IsSymbol()) {
6988 PrintString(String::cast(obj));
6989 } else if (obj->IsNumber()) {
6990 PrintF("%g", obj->Number());
6991 } else if (obj->IsFailure()) {
6992 PrintF("<failure>");
6993 } else if (obj->IsUndefined()) {
6994 PrintF("<undefined>");
6995 } else if (obj->IsNull()) {
6996 PrintF("<null>");
6997 } else if (obj->IsTrue()) {
6998 PrintF("<true>");
6999 } else if (obj->IsFalse()) {
7000 PrintF("<false>");
7001 } else {
7002 PrintF("%p", obj);
7003 }
7004}
7005
7006
7007static int StackSize() {
7008 int n = 0;
7009 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
7010 return n;
7011}
7012
7013
7014static void PrintTransition(Object* result) {
7015 // indentation
7016 { const int nmax = 80;
7017 int n = StackSize();
7018 if (n <= nmax)
7019 PrintF("%4d:%*s", n, n, "");
7020 else
7021 PrintF("%4d:%*s", n, nmax, "...");
7022 }
7023
7024 if (result == NULL) {
7025 // constructor calls
7026 JavaScriptFrameIterator it;
7027 JavaScriptFrame* frame = it.frame();
7028 if (frame->IsConstructor()) PrintF("new ");
7029 // function name
7030 Object* fun = frame->function();
7031 if (fun->IsJSFunction()) {
7032 PrintObject(JSFunction::cast(fun)->shared()->name());
7033 } else {
7034 PrintObject(fun);
7035 }
7036 // function arguments
7037 // (we are intentionally only printing the actually
7038 // supplied parameters, not all parameters required)
7039 PrintF("(this=");
7040 PrintObject(frame->receiver());
7041 const int length = frame->GetProvidedParametersCount();
7042 for (int i = 0; i < length; i++) {
7043 PrintF(", ");
7044 PrintObject(frame->GetParameter(i));
7045 }
7046 PrintF(") {\n");
7047
7048 } else {
7049 // function result
7050 PrintF("} -> ");
7051 PrintObject(result);
7052 PrintF("\n");
7053 }
7054}
7055
7056
7057static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007058 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007059 NoHandleAllocation ha;
7060 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007061 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007062}
7063
7064
7065static Object* Runtime_TraceExit(Arguments args) {
7066 NoHandleAllocation ha;
7067 PrintTransition(args[0]);
7068 return args[0]; // return TOS
7069}
7070
7071
7072static Object* Runtime_DebugPrint(Arguments args) {
7073 NoHandleAllocation ha;
7074 ASSERT(args.length() == 1);
7075
7076#ifdef DEBUG
7077 if (args[0]->IsString()) {
7078 // If we have a string, assume it's a code "marker"
7079 // and print some interesting cpu debugging info.
7080 JavaScriptFrameIterator it;
7081 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007082 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
7083 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007084 } else {
7085 PrintF("DebugPrint: ");
7086 }
7087 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007088 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007089 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007090 HeapObject::cast(args[0])->map()->Print();
7091 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007092#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007093 // ShortPrint is available in release mode. Print is not.
7094 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007095#endif
7096 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00007097 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007098
7099 return args[0]; // return TOS
7100}
7101
7102
7103static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007104 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007105 NoHandleAllocation ha;
7106 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007107 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007108}
7109
7110
mads.s.ager31e71382008-08-13 09:32:07 +00007111static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007112 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007113 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007114
7115 // According to ECMA-262, section 15.9.1, page 117, the precision of
7116 // the number in a Date object representing a particular instant in
7117 // time is milliseconds. Therefore, we floor the result of getting
7118 // the OS time.
7119 double millis = floor(OS::TimeCurrentMillis());
7120 return Heap::NumberFromDouble(millis);
7121}
7122
7123
7124static Object* Runtime_DateParseString(Arguments args) {
7125 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007126 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007127
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007128 CONVERT_ARG_CHECKED(String, str, 0);
7129 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007130
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007131 CONVERT_ARG_CHECKED(JSArray, output, 1);
7132 RUNTIME_ASSERT(output->HasFastElements());
7133
7134 AssertNoAllocation no_allocation;
7135
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007136 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007137 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
7138 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007139 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007140 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007141 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007142 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007143 result = DateParser::Parse(str->ToUC16Vector(), output_array);
7144 }
7145
7146 if (result) {
7147 return *output;
7148 } else {
7149 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007150 }
7151}
7152
7153
7154static Object* Runtime_DateLocalTimezone(Arguments args) {
7155 NoHandleAllocation ha;
7156 ASSERT(args.length() == 1);
7157
7158 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007159 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007160 return Heap::AllocateStringFromUtf8(CStrVector(zone));
7161}
7162
7163
7164static Object* Runtime_DateLocalTimeOffset(Arguments args) {
7165 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007166 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007167
7168 return Heap::NumberFromDouble(OS::LocalTimeOffset());
7169}
7170
7171
7172static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
7173 NoHandleAllocation ha;
7174 ASSERT(args.length() == 1);
7175
7176 CONVERT_DOUBLE_CHECKED(x, args[0]);
7177 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
7178}
7179
7180
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007181static Object* Runtime_GlobalReceiver(Arguments args) {
7182 ASSERT(args.length() == 1);
7183 Object* global = args[0];
7184 if (!global->IsJSGlobalObject()) return Heap::null_value();
7185 return JSGlobalObject::cast(global)->global_receiver();
7186}
7187
7188
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007189static Object* Runtime_CompileString(Arguments args) {
7190 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007191 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007192 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007193 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007194
ager@chromium.org381abbb2009-02-25 13:23:22 +00007195 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007196 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007197 Compiler::ValidationState validate = (is_json->IsTrue())
7198 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007199 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
7200 context,
7201 true,
7202 validate);
7203 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007204 Handle<JSFunction> fun =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007205 Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007206 return *fun;
7207}
7208
7209
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007210static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
7211 ASSERT(args.length() == 3);
7212 if (!args[0]->IsJSFunction()) {
7213 return MakePair(Top::ThrowIllegalOperation(), NULL);
7214 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007215
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007216 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007217 Handle<JSFunction> callee = args.at<JSFunction>(0);
7218 Handle<Object> receiver; // Will be overwritten.
7219
7220 // Compute the calling context.
7221 Handle<Context> context = Handle<Context>(Top::context());
7222#ifdef DEBUG
7223 // Make sure Top::context() agrees with the old code that traversed
7224 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007225 StackFrameLocator locator;
7226 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007227 ASSERT(Context::cast(frame->context()) == *context);
7228#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007229
7230 // Find where the 'eval' symbol is bound. It is unaliased only if
7231 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007232 int index = -1;
7233 PropertyAttributes attributes = ABSENT;
7234 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007235 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
7236 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007237 // Stop search when eval is found or when the global context is
7238 // reached.
7239 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007240 if (context->is_function_context()) {
7241 context = Handle<Context>(Context::cast(context->closure()->context()));
7242 } else {
7243 context = Handle<Context>(context->previous());
7244 }
7245 }
7246
iposva@chromium.org245aa852009-02-10 00:49:54 +00007247 // If eval could not be resolved, it has been deleted and we need to
7248 // throw a reference error.
7249 if (attributes == ABSENT) {
7250 Handle<Object> name = Factory::eval_symbol();
7251 Handle<Object> reference_error =
7252 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007253 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007254 }
7255
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007256 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007257 // 'eval' is not bound in the global context. Just call the function
7258 // with the given arguments. This is not necessarily the global eval.
7259 if (receiver->IsContext()) {
7260 context = Handle<Context>::cast(receiver);
7261 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007262 } else if (receiver->IsJSContextExtensionObject()) {
7263 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007264 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007265 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007266 }
7267
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007268 // 'eval' is bound in the global context, but it may have been overwritten.
7269 // Compare it to the builtin 'GlobalEval' function to make sure.
7270 if (*callee != Top::global_context()->global_eval_fun() ||
7271 !args[1]->IsString()) {
7272 return MakePair(*callee, Top::context()->global()->global_receiver());
7273 }
7274
7275 // Deal with a normal eval call with a string argument. Compile it
7276 // and return the compiled function bound in the local context.
7277 Handle<String> source = args.at<String>(1);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007278 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007279 source,
7280 Handle<Context>(Top::context()),
7281 Top::context()->IsGlobalContext(),
7282 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007283 if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
7284 callee = Factory::NewFunctionFromSharedFunctionInfo(
7285 shared,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007286 Handle<Context>(Top::context()),
7287 NOT_TENURED);
7288 return MakePair(*callee, args[2]);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007289}
7290
7291
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007292static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
7293 // This utility adjusts the property attributes for newly created Function
7294 // object ("new Function(...)") by changing the map.
7295 // All it does is changing the prototype property to enumerable
7296 // as specified in ECMA262, 15.3.5.2.
7297 HandleScope scope;
7298 ASSERT(args.length() == 1);
7299 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7300 ASSERT(func->map()->instance_type() ==
7301 Top::function_instance_map()->instance_type());
7302 ASSERT(func->map()->instance_size() ==
7303 Top::function_instance_map()->instance_size());
7304 func->set_map(*Top::function_instance_map());
7305 return *func;
7306}
7307
7308
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007309// Push an array unto an array of arrays if it is not already in the
7310// array. Returns true if the element was pushed on the stack and
7311// false otherwise.
7312static Object* Runtime_PushIfAbsent(Arguments args) {
7313 ASSERT(args.length() == 2);
7314 CONVERT_CHECKED(JSArray, array, args[0]);
7315 CONVERT_CHECKED(JSArray, element, args[1]);
7316 RUNTIME_ASSERT(array->HasFastElements());
7317 int length = Smi::cast(array->length())->value();
7318 FixedArray* elements = FixedArray::cast(array->elements());
7319 for (int i = 0; i < length; i++) {
7320 if (elements->get(i) == element) return Heap::false_value();
7321 }
7322 Object* obj = array->SetFastElement(length, element);
7323 if (obj->IsFailure()) return obj;
7324 return Heap::true_value();
7325}
7326
7327
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007328/**
7329 * A simple visitor visits every element of Array's.
7330 * The backend storage can be a fixed array for fast elements case,
7331 * or a dictionary for sparse array. Since Dictionary is a subtype
7332 * of FixedArray, the class can be used by both fast and slow cases.
7333 * The second parameter of the constructor, fast_elements, specifies
7334 * whether the storage is a FixedArray or Dictionary.
7335 *
7336 * An index limit is used to deal with the situation that a result array
7337 * length overflows 32-bit non-negative integer.
7338 */
7339class ArrayConcatVisitor {
7340 public:
7341 ArrayConcatVisitor(Handle<FixedArray> storage,
7342 uint32_t index_limit,
7343 bool fast_elements) :
7344 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007345 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007346
7347 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007348 if (i >= index_limit_ - index_offset_) return;
7349 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007350
7351 if (fast_elements_) {
7352 ASSERT(index < static_cast<uint32_t>(storage_->length()));
7353 storage_->set(index, *elm);
7354
7355 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007356 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
7357 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007358 Factory::DictionaryAtNumberPut(dict, index, elm);
7359 if (!result.is_identical_to(dict))
7360 storage_ = result;
7361 }
7362 }
7363
7364 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007365 if (index_limit_ - index_offset_ < delta) {
7366 index_offset_ = index_limit_;
7367 } else {
7368 index_offset_ += delta;
7369 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007370 }
7371
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007372 Handle<FixedArray> storage() { return storage_; }
7373
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007374 private:
7375 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007376 // Limit on the accepted indices. Elements with indices larger than the
7377 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007378 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007379 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007380 uint32_t index_offset_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007381 bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007382};
7383
7384
ager@chromium.org3811b432009-10-28 14:53:37 +00007385template<class ExternalArrayClass, class ElementType>
7386static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
7387 bool elements_are_ints,
7388 bool elements_are_guaranteed_smis,
7389 uint32_t range,
7390 ArrayConcatVisitor* visitor) {
7391 Handle<ExternalArrayClass> array(
7392 ExternalArrayClass::cast(receiver->elements()));
7393 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
7394
7395 if (visitor != NULL) {
7396 if (elements_are_ints) {
7397 if (elements_are_guaranteed_smis) {
7398 for (uint32_t j = 0; j < len; j++) {
7399 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
7400 visitor->visit(j, e);
7401 }
7402 } else {
7403 for (uint32_t j = 0; j < len; j++) {
7404 int64_t val = static_cast<int64_t>(array->get(j));
7405 if (Smi::IsValid(static_cast<intptr_t>(val))) {
7406 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
7407 visitor->visit(j, e);
7408 } else {
7409 Handle<Object> e(
7410 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
7411 visitor->visit(j, e);
7412 }
7413 }
7414 }
7415 } else {
7416 for (uint32_t j = 0; j < len; j++) {
7417 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
7418 visitor->visit(j, e);
7419 }
7420 }
7421 }
7422
7423 return len;
7424}
7425
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007426/**
7427 * A helper function that visits elements of a JSObject. Only elements
7428 * whose index between 0 and range (exclusive) are visited.
7429 *
7430 * If the third parameter, visitor, is not NULL, the visitor is called
7431 * with parameters, 'visitor_index_offset + element index' and the element.
7432 *
7433 * It returns the number of visisted elements.
7434 */
7435static uint32_t IterateElements(Handle<JSObject> receiver,
7436 uint32_t range,
7437 ArrayConcatVisitor* visitor) {
7438 uint32_t num_of_elements = 0;
7439
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007440 switch (receiver->GetElementsKind()) {
7441 case JSObject::FAST_ELEMENTS: {
7442 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
7443 uint32_t len = elements->length();
7444 if (range < len) {
7445 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007446 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007447
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007448 for (uint32_t j = 0; j < len; j++) {
7449 Handle<Object> e(elements->get(j));
7450 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007451 num_of_elements++;
7452 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007453 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007454 }
7455 }
7456 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007457 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007458 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007459 case JSObject::PIXEL_ELEMENTS: {
7460 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
7461 uint32_t len = pixels->length();
7462 if (range < len) {
7463 len = range;
7464 }
7465
7466 for (uint32_t j = 0; j < len; j++) {
7467 num_of_elements++;
7468 if (visitor != NULL) {
7469 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
7470 visitor->visit(j, e);
7471 }
7472 }
7473 break;
7474 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007475 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
7476 num_of_elements =
7477 IterateExternalArrayElements<ExternalByteArray, int8_t>(
7478 receiver, true, true, range, visitor);
7479 break;
7480 }
7481 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7482 num_of_elements =
7483 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
7484 receiver, true, true, range, visitor);
7485 break;
7486 }
7487 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
7488 num_of_elements =
7489 IterateExternalArrayElements<ExternalShortArray, int16_t>(
7490 receiver, true, true, range, visitor);
7491 break;
7492 }
7493 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7494 num_of_elements =
7495 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
7496 receiver, true, true, range, visitor);
7497 break;
7498 }
7499 case JSObject::EXTERNAL_INT_ELEMENTS: {
7500 num_of_elements =
7501 IterateExternalArrayElements<ExternalIntArray, int32_t>(
7502 receiver, true, false, range, visitor);
7503 break;
7504 }
7505 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7506 num_of_elements =
7507 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
7508 receiver, true, false, range, visitor);
7509 break;
7510 }
7511 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
7512 num_of_elements =
7513 IterateExternalArrayElements<ExternalFloatArray, float>(
7514 receiver, false, false, range, visitor);
7515 break;
7516 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007517 case JSObject::DICTIONARY_ELEMENTS: {
7518 Handle<NumberDictionary> dict(receiver->element_dictionary());
7519 uint32_t capacity = dict->Capacity();
7520 for (uint32_t j = 0; j < capacity; j++) {
7521 Handle<Object> k(dict->KeyAt(j));
7522 if (dict->IsKey(*k)) {
7523 ASSERT(k->IsNumber());
7524 uint32_t index = static_cast<uint32_t>(k->Number());
7525 if (index < range) {
7526 num_of_elements++;
7527 if (visitor) {
7528 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
7529 }
7530 }
7531 }
7532 }
7533 break;
7534 }
7535 default:
7536 UNREACHABLE();
7537 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007538 }
7539
7540 return num_of_elements;
7541}
7542
7543
7544/**
7545 * A helper function that visits elements of an Array object, and elements
7546 * on its prototypes.
7547 *
7548 * Elements on prototypes are visited first, and only elements whose indices
7549 * less than Array length are visited.
7550 *
7551 * If a ArrayConcatVisitor object is given, the visitor is called with
7552 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007553 *
7554 * The returned number of elements is an upper bound on the actual number
7555 * of elements added. If the same element occurs in more than one object
7556 * in the array's prototype chain, it will be counted more than once, but
7557 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007558 */
7559static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
7560 ArrayConcatVisitor* visitor) {
7561 uint32_t range = static_cast<uint32_t>(array->length()->Number());
7562 Handle<Object> obj = array;
7563
7564 static const int kEstimatedPrototypes = 3;
7565 List< Handle<JSObject> > objects(kEstimatedPrototypes);
7566
7567 // Visit prototype first. If an element on the prototype is shadowed by
7568 // the inheritor using the same index, the ArrayConcatVisitor visits
7569 // the prototype element before the shadowing element.
7570 // The visitor can simply overwrite the old value by new value using
7571 // the same index. This follows Array::concat semantics.
7572 while (!obj->IsNull()) {
7573 objects.Add(Handle<JSObject>::cast(obj));
7574 obj = Handle<Object>(obj->GetPrototype());
7575 }
7576
7577 uint32_t nof_elements = 0;
7578 for (int i = objects.length() - 1; i >= 0; i--) {
7579 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007580 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007581 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007582
7583 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
7584 nof_elements = JSObject::kMaxElementCount;
7585 } else {
7586 nof_elements += encountered_elements;
7587 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007588 }
7589
7590 return nof_elements;
7591}
7592
7593
7594/**
7595 * A helper function of Runtime_ArrayConcat.
7596 *
7597 * The first argument is an Array of arrays and objects. It is the
7598 * same as the arguments array of Array::concat JS function.
7599 *
7600 * If an argument is an Array object, the function visits array
7601 * elements. If an argument is not an Array object, the function
7602 * visits the object as if it is an one-element array.
7603 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007604 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007605 * non-negative number is used as new length. For example, if one
7606 * array length is 2^32 - 1, second array length is 1, the
7607 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007608 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
7609 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007610 */
7611static uint32_t IterateArguments(Handle<JSArray> arguments,
7612 ArrayConcatVisitor* visitor) {
7613 uint32_t visited_elements = 0;
7614 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7615
7616 for (uint32_t i = 0; i < num_of_args; i++) {
7617 Handle<Object> obj(arguments->GetElement(i));
7618 if (obj->IsJSArray()) {
7619 Handle<JSArray> array = Handle<JSArray>::cast(obj);
7620 uint32_t len = static_cast<uint32_t>(array->length()->Number());
7621 uint32_t nof_elements =
7622 IterateArrayAndPrototypeElements(array, visitor);
7623 // Total elements of array and its prototype chain can be more than
7624 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007625 // the array length number of elements. We use the length as an estimate
7626 // for the actual number of elements added.
7627 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
7628 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
7629 visited_elements = JSArray::kMaxElementCount;
7630 } else {
7631 visited_elements += added_elements;
7632 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007633 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007634 } else {
7635 if (visitor) {
7636 visitor->visit(0, obj);
7637 visitor->increase_index_offset(1);
7638 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007639 if (visited_elements < JSArray::kMaxElementCount) {
7640 visited_elements++;
7641 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007642 }
7643 }
7644 return visited_elements;
7645}
7646
7647
7648/**
7649 * Array::concat implementation.
7650 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007651 * TODO(lrn): Fix non-compliance for very large concatenations and update to
7652 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007653 */
7654static Object* Runtime_ArrayConcat(Arguments args) {
7655 ASSERT(args.length() == 1);
7656 HandleScope handle_scope;
7657
7658 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
7659 Handle<JSArray> arguments(arg_arrays);
7660
7661 // Pass 1: estimate the number of elements of the result
7662 // (it could be more than real numbers if prototype has elements).
7663 uint32_t result_length = 0;
7664 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7665
7666 { AssertNoAllocation nogc;
7667 for (uint32_t i = 0; i < num_of_args; i++) {
7668 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007669 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007670 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007671 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007672 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
7673 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007674 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007675 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007676 if (JSObject::kMaxElementCount - result_length < length_estimate) {
7677 result_length = JSObject::kMaxElementCount;
7678 break;
7679 }
7680 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007681 }
7682 }
7683
7684 // Allocate an empty array, will set length and content later.
7685 Handle<JSArray> result = Factory::NewJSArray(0);
7686
7687 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
7688 // If estimated number of elements is more than half of length, a
7689 // fixed array (fast case) is more time and space-efficient than a
7690 // dictionary.
7691 bool fast_case = (estimate_nof_elements * 2) >= result_length;
7692
7693 Handle<FixedArray> storage;
7694 if (fast_case) {
7695 // The backing storage array must have non-existing elements to
7696 // preserve holes across concat operations.
7697 storage = Factory::NewFixedArrayWithHoles(result_length);
7698
7699 } else {
7700 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
7701 uint32_t at_least_space_for = estimate_nof_elements +
7702 (estimate_nof_elements >> 2);
7703 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007704 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007705 }
7706
7707 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
7708
7709 ArrayConcatVisitor visitor(storage, result_length, fast_case);
7710
7711 IterateArguments(arguments, &visitor);
7712
7713 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007714 // Please note the storage might have changed in the visitor.
7715 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007716
7717 return *result;
7718}
7719
7720
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007721// This will not allocate (flatten the string), but it may run
7722// very slowly for very deeply nested ConsStrings. For debugging use only.
7723static Object* Runtime_GlobalPrint(Arguments args) {
7724 NoHandleAllocation ha;
7725 ASSERT(args.length() == 1);
7726
7727 CONVERT_CHECKED(String, string, args[0]);
7728 StringInputBuffer buffer(string);
7729 while (buffer.has_more()) {
7730 uint16_t character = buffer.GetNext();
7731 PrintF("%c", character);
7732 }
7733 return string;
7734}
7735
ager@chromium.org5ec48922009-05-05 07:25:34 +00007736// Moves all own elements of an object, that are below a limit, to positions
7737// starting at zero. All undefined values are placed after non-undefined values,
7738// and are followed by non-existing element. Does not change the length
7739// property.
7740// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007741static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007742 ASSERT(args.length() == 2);
7743 CONVERT_CHECKED(JSObject, object, args[0]);
7744 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
7745 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007746}
7747
7748
7749// Move contents of argument 0 (an array) to argument 1 (an array)
7750static Object* Runtime_MoveArrayContents(Arguments args) {
7751 ASSERT(args.length() == 2);
7752 CONVERT_CHECKED(JSArray, from, args[0]);
7753 CONVERT_CHECKED(JSArray, to, args[1]);
7754 to->SetContent(FixedArray::cast(from->elements()));
7755 to->set_length(from->length());
7756 from->SetContent(Heap::empty_fixed_array());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007757 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007758 return to;
7759}
7760
7761
7762// How many elements does this array have?
7763static Object* Runtime_EstimateNumberOfElements(Arguments args) {
7764 ASSERT(args.length() == 1);
7765 CONVERT_CHECKED(JSArray, array, args[0]);
7766 HeapObject* elements = array->elements();
7767 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007768 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007769 } else {
7770 return array->length();
7771 }
7772}
7773
7774
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007775static Object* Runtime_SwapElements(Arguments args) {
7776 HandleScope handle_scope;
7777
7778 ASSERT_EQ(3, args.length());
7779
7780 Handle<Object> object = args.at<Object>(0);
7781 Handle<Object> key1 = args.at<Object>(1);
7782 Handle<Object> key2 = args.at<Object>(2);
7783
7784 uint32_t index1, index2;
7785 // That must be the most common case.
7786 if (object->IsJSObject()
7787 && Array::IndexFromObject(*key1, &index1)
7788 && Array::IndexFromObject(*key2, &index2)) {
7789 Handle<JSObject> jsobject = Handle<JSObject>::cast(object);
7790 Handle<Object> tmp1 = GetElement(jsobject, index1);
7791 Handle<Object> tmp2 = GetElement(jsobject, index2);
7792
7793 SetElement(jsobject, index1, tmp2);
7794 SetElement(jsobject, index2, tmp1);
7795 } else {
7796 Handle<Object> tmp1 = GetProperty(object, key1);
7797 Handle<Object> tmp2 = GetProperty(object, key2);
7798
7799 SetProperty(object, key1, tmp2, NONE);
7800 SetProperty(object, key2, tmp1, NONE);
7801 }
7802
7803 return Heap::undefined_value();
7804}
7805
7806
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007807// Returns an array that tells you where in the [0, length) interval an array
7808// might have elements. Can either return keys or intervals. Keys can have
7809// gaps in (undefined). Intervals can also span over some undefined keys.
7810static Object* Runtime_GetArrayKeys(Arguments args) {
7811 ASSERT(args.length() == 2);
7812 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007813 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007814 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007815 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007816 // Create an array and get all the keys into it, then remove all the
7817 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007818 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007819 int keys_length = keys->length();
7820 for (int i = 0; i < keys_length; i++) {
7821 Object* key = keys->get(i);
7822 uint32_t index;
7823 if (!Array::IndexFromObject(key, &index) || index >= length) {
7824 // Zap invalid keys.
7825 keys->set_undefined(i);
7826 }
7827 }
7828 return *Factory::NewJSArrayWithElements(keys);
7829 } else {
7830 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
7831 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007832 single_interval->set(0, Smi::FromInt(-1));
ager@chromium.org5ec48922009-05-05 07:25:34 +00007833 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
7834 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007835 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00007836 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007837 single_interval->set(1, *length_object);
7838 return *Factory::NewJSArrayWithElements(single_interval);
7839 }
7840}
7841
7842
7843// DefineAccessor takes an optional final argument which is the
7844// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
7845// to the way accessors are implemented, it is set for both the getter
7846// and setter on the first call to DefineAccessor and ignored on
7847// subsequent calls.
7848static Object* Runtime_DefineAccessor(Arguments args) {
7849 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
7850 // Compute attributes.
7851 PropertyAttributes attributes = NONE;
7852 if (args.length() == 5) {
7853 CONVERT_CHECKED(Smi, attrs, args[4]);
7854 int value = attrs->value();
7855 // Only attribute bits should be set.
7856 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
7857 attributes = static_cast<PropertyAttributes>(value);
7858 }
7859
7860 CONVERT_CHECKED(JSObject, obj, args[0]);
7861 CONVERT_CHECKED(String, name, args[1]);
7862 CONVERT_CHECKED(Smi, flag, args[2]);
7863 CONVERT_CHECKED(JSFunction, fun, args[3]);
7864 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
7865}
7866
7867
7868static Object* Runtime_LookupAccessor(Arguments args) {
7869 ASSERT(args.length() == 3);
7870 CONVERT_CHECKED(JSObject, obj, args[0]);
7871 CONVERT_CHECKED(String, name, args[1]);
7872 CONVERT_CHECKED(Smi, flag, args[2]);
7873 return obj->LookupAccessor(name, flag->value() == 0);
7874}
7875
7876
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007877#ifdef ENABLE_DEBUGGER_SUPPORT
7878static Object* Runtime_DebugBreak(Arguments args) {
7879 ASSERT(args.length() == 0);
7880 return Execution::DebugBreakHelper();
7881}
7882
7883
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007884// Helper functions for wrapping and unwrapping stack frame ids.
7885static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007886 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007887 return Smi::FromInt(id >> 2);
7888}
7889
7890
7891static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
7892 return static_cast<StackFrame::Id>(wrapped->value() << 2);
7893}
7894
7895
7896// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00007897// args[0]: debug event listener function to set or null or undefined for
7898// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007899// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00007900static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007901 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007902 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
7903 args[0]->IsUndefined() ||
7904 args[0]->IsNull());
7905 Handle<Object> callback = args.at<Object>(0);
7906 Handle<Object> data = args.at<Object>(1);
7907 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007908
7909 return Heap::undefined_value();
7910}
7911
7912
7913static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007914 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007915 StackGuard::DebugBreak();
7916 return Heap::undefined_value();
7917}
7918
7919
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007920static Object* DebugLookupResultValue(Object* receiver, String* name,
7921 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00007922 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007923 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007924 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007925 case NORMAL:
7926 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007927 if (value->IsTheHole()) {
7928 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007929 }
7930 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007931 case FIELD:
7932 value =
7933 JSObject::cast(
7934 result->holder())->FastPropertyAt(result->GetFieldIndex());
7935 if (value->IsTheHole()) {
7936 return Heap::undefined_value();
7937 }
7938 return value;
7939 case CONSTANT_FUNCTION:
7940 return result->GetConstantFunction();
7941 case CALLBACKS: {
7942 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007943 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00007944 value = receiver->GetPropertyWithCallback(
7945 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00007946 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007947 value = Top::pending_exception();
7948 Top::clear_pending_exception();
7949 if (caught_exception != NULL) {
7950 *caught_exception = true;
7951 }
7952 }
7953 return value;
7954 } else {
7955 return Heap::undefined_value();
7956 }
7957 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007958 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007959 case MAP_TRANSITION:
7960 case CONSTANT_TRANSITION:
7961 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007962 return Heap::undefined_value();
7963 default:
7964 UNREACHABLE();
7965 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007966 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007967 return Heap::undefined_value();
7968}
7969
7970
ager@chromium.org32912102009-01-16 10:38:43 +00007971// Get debugger related details for an object property.
7972// args[0]: object holding property
7973// args[1]: name of the property
7974//
7975// The array returned contains the following information:
7976// 0: Property value
7977// 1: Property details
7978// 2: Property value is exception
7979// 3: Getter function if defined
7980// 4: Setter function if defined
7981// Items 2-4 are only filled if the property has either a getter or a setter
7982// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007983static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007984 HandleScope scope;
7985
7986 ASSERT(args.length() == 2);
7987
7988 CONVERT_ARG_CHECKED(JSObject, obj, 0);
7989 CONVERT_ARG_CHECKED(String, name, 1);
7990
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00007991 // Make sure to set the current context to the context before the debugger was
7992 // entered (if the debugger is entered). The reason for switching context here
7993 // is that for some property lookups (accessors and interceptors) callbacks
7994 // into the embedding application can occour, and the embedding application
7995 // could have the assumption that its own global context is the current
7996 // context and not some internal debugger context.
7997 SaveContext save;
7998 if (Debug::InDebugger()) {
7999 Top::set_context(*Debug::debugger_entry()->GetContext());
8000 }
8001
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008002 // Skip the global proxy as it has no properties and always delegates to the
8003 // real global object.
8004 if (obj->IsJSGlobalProxy()) {
8005 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
8006 }
8007
8008
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008009 // Check if the name is trivially convertible to an index and get the element
8010 // if so.
8011 uint32_t index;
8012 if (name->AsArrayIndex(&index)) {
8013 Handle<FixedArray> details = Factory::NewFixedArray(2);
8014 details->set(0, Runtime::GetElementOrCharAt(obj, index));
8015 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
8016 return *Factory::NewJSArrayWithElements(details);
8017 }
8018
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008019 // Find the number of objects making up this.
8020 int length = LocalPrototypeChainLength(*obj);
8021
8022 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008023 Handle<JSObject> jsproto = obj;
8024 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008025 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008026 jsproto->LocalLookup(*name, &result);
8027 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008028 // LookupResult is not GC safe as it holds raw object pointers.
8029 // GC can happen later in this code so put the required fields into
8030 // local variables using handles when required for later use.
8031 PropertyType result_type = result.type();
8032 Handle<Object> result_callback_obj;
8033 if (result_type == CALLBACKS) {
8034 result_callback_obj = Handle<Object>(result.GetCallbackObject());
8035 }
8036 Smi* property_details = result.GetPropertyDetails().AsSmi();
8037 // DebugLookupResultValue can cause GC so details from LookupResult needs
8038 // to be copied to handles before this.
8039 bool caught_exception = false;
8040 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
8041 &caught_exception);
8042 if (raw_value->IsFailure()) return raw_value;
8043 Handle<Object> value(raw_value);
8044
8045 // If the callback object is a fixed array then it contains JavaScript
8046 // getter and/or setter.
8047 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
8048 result_callback_obj->IsFixedArray();
8049 Handle<FixedArray> details =
8050 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
8051 details->set(0, *value);
8052 details->set(1, property_details);
8053 if (hasJavaScriptAccessors) {
8054 details->set(2,
8055 caught_exception ? Heap::true_value()
8056 : Heap::false_value());
8057 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
8058 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
8059 }
8060
8061 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008062 }
8063 if (i < length - 1) {
8064 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
8065 }
8066 }
8067
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008068 return Heap::undefined_value();
8069}
8070
8071
8072static Object* Runtime_DebugGetProperty(Arguments args) {
8073 HandleScope scope;
8074
8075 ASSERT(args.length() == 2);
8076
8077 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8078 CONVERT_ARG_CHECKED(String, name, 1);
8079
8080 LookupResult result;
8081 obj->Lookup(*name, &result);
8082 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008083 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008084 }
8085 return Heap::undefined_value();
8086}
8087
8088
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008089// Return the property type calculated from the property details.
8090// args[0]: smi with property details.
8091static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
8092 ASSERT(args.length() == 1);
8093 CONVERT_CHECKED(Smi, details, args[0]);
8094 PropertyType type = PropertyDetails(details).type();
8095 return Smi::FromInt(static_cast<int>(type));
8096}
8097
8098
8099// Return the property attribute calculated from the property details.
8100// args[0]: smi with property details.
8101static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
8102 ASSERT(args.length() == 1);
8103 CONVERT_CHECKED(Smi, details, args[0]);
8104 PropertyAttributes attributes = PropertyDetails(details).attributes();
8105 return Smi::FromInt(static_cast<int>(attributes));
8106}
8107
8108
8109// Return the property insertion index calculated from the property details.
8110// args[0]: smi with property details.
8111static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
8112 ASSERT(args.length() == 1);
8113 CONVERT_CHECKED(Smi, details, args[0]);
8114 int index = PropertyDetails(details).index();
8115 return Smi::FromInt(index);
8116}
8117
8118
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008119// Return property value from named interceptor.
8120// args[0]: object
8121// args[1]: property name
8122static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
8123 HandleScope scope;
8124 ASSERT(args.length() == 2);
8125 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8126 RUNTIME_ASSERT(obj->HasNamedInterceptor());
8127 CONVERT_ARG_CHECKED(String, name, 1);
8128
8129 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008130 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008131}
8132
8133
8134// Return element value from indexed interceptor.
8135// args[0]: object
8136// args[1]: index
8137static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
8138 HandleScope scope;
8139 ASSERT(args.length() == 2);
8140 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8141 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
8142 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
8143
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008144 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008145}
8146
8147
8148static Object* Runtime_CheckExecutionState(Arguments args) {
8149 ASSERT(args.length() >= 1);
8150 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00008151 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008152 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008153 return Top::Throw(Heap::illegal_execution_state_symbol());
8154 }
8155
8156 return Heap::true_value();
8157}
8158
8159
8160static Object* Runtime_GetFrameCount(Arguments args) {
8161 HandleScope scope;
8162 ASSERT(args.length() == 1);
8163
8164 // Check arguments.
8165 Object* result = Runtime_CheckExecutionState(args);
8166 if (result->IsFailure()) return result;
8167
8168 // Count all frames which are relevant to debugging stack trace.
8169 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008170 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008171 if (id == StackFrame::NO_ID) {
8172 // If there is no JavaScript stack frame count is 0.
8173 return Smi::FromInt(0);
8174 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008175 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
8176 return Smi::FromInt(n);
8177}
8178
8179
8180static const int kFrameDetailsFrameIdIndex = 0;
8181static const int kFrameDetailsReceiverIndex = 1;
8182static const int kFrameDetailsFunctionIndex = 2;
8183static const int kFrameDetailsArgumentCountIndex = 3;
8184static const int kFrameDetailsLocalCountIndex = 4;
8185static const int kFrameDetailsSourcePositionIndex = 5;
8186static const int kFrameDetailsConstructCallIndex = 6;
8187static const int kFrameDetailsDebuggerFrameIndex = 7;
8188static const int kFrameDetailsFirstDynamicIndex = 8;
8189
8190// Return an array with frame details
8191// args[0]: number: break id
8192// args[1]: number: frame index
8193//
8194// The array returned contains the following information:
8195// 0: Frame id
8196// 1: Receiver
8197// 2: Function
8198// 3: Argument count
8199// 4: Local count
8200// 5: Source position
8201// 6: Constructor call
8202// 7: Debugger frame
8203// Arguments name, value
8204// Locals name, value
8205static Object* Runtime_GetFrameDetails(Arguments args) {
8206 HandleScope scope;
8207 ASSERT(args.length() == 2);
8208
8209 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008210 Object* check = Runtime_CheckExecutionState(args);
8211 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008212 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8213
8214 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008215 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008216 if (id == StackFrame::NO_ID) {
8217 // If there are no JavaScript stack frames return undefined.
8218 return Heap::undefined_value();
8219 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008220 int count = 0;
8221 JavaScriptFrameIterator it(id);
8222 for (; !it.done(); it.Advance()) {
8223 if (count == index) break;
8224 count++;
8225 }
8226 if (it.done()) return Heap::undefined_value();
8227
8228 // Traverse the saved contexts chain to find the active context for the
8229 // selected frame.
8230 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008231 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008232 save = save->prev();
8233 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008234 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008235
8236 // Get the frame id.
8237 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
8238
8239 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008240 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008241
8242 // Check for constructor frame.
8243 bool constructor = it.frame()->IsConstructor();
8244
8245 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008246 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008247 ScopeInfo<> info(*code);
8248
8249 // Get the context.
8250 Handle<Context> context(Context::cast(it.frame()->context()));
8251
8252 // Get the locals names and values into a temporary array.
8253 //
8254 // TODO(1240907): Hide compiler-introduced stack variables
8255 // (e.g. .result)? For users of the debugger, they will probably be
8256 // confusing.
8257 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
8258 for (int i = 0; i < info.NumberOfLocals(); i++) {
8259 // Name of the local.
8260 locals->set(i * 2, *info.LocalName(i));
8261
8262 // Fetch the value of the local - either from the stack or from a
8263 // heap-allocated context.
8264 if (i < info.number_of_stack_slots()) {
8265 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
8266 } else {
8267 Handle<String> name = info.LocalName(i);
8268 // Traverse the context chain to the function context as all local
8269 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008270 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008271 context = Handle<Context>(context->previous());
8272 }
8273 ASSERT(context->is_function_context());
8274 locals->set(i * 2 + 1,
8275 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
8276 NULL)));
8277 }
8278 }
8279
8280 // Now advance to the arguments adapter frame (if any). If contains all
8281 // the provided parameters and
8282
8283 // Now advance to the arguments adapter frame (if any). It contains all
8284 // the provided parameters whereas the function frame always have the number
8285 // of arguments matching the functions parameters. The rest of the
8286 // information (except for what is collected above) is the same.
8287 it.AdvanceToArgumentsFrame();
8288
8289 // Find the number of arguments to fill. At least fill the number of
8290 // parameters for the function and fill more if more parameters are provided.
8291 int argument_count = info.number_of_parameters();
8292 if (argument_count < it.frame()->GetProvidedParametersCount()) {
8293 argument_count = it.frame()->GetProvidedParametersCount();
8294 }
8295
8296 // Calculate the size of the result.
8297 int details_size = kFrameDetailsFirstDynamicIndex +
8298 2 * (argument_count + info.NumberOfLocals());
8299 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8300
8301 // Add the frame id.
8302 details->set(kFrameDetailsFrameIdIndex, *frame_id);
8303
8304 // Add the function (same as in function frame).
8305 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
8306
8307 // Add the arguments count.
8308 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
8309
8310 // Add the locals count
8311 details->set(kFrameDetailsLocalCountIndex,
8312 Smi::FromInt(info.NumberOfLocals()));
8313
8314 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00008315 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008316 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
8317 } else {
8318 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
8319 }
8320
8321 // Add the constructor information.
8322 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
8323
8324 // Add information on whether this frame is invoked in the debugger context.
8325 details->set(kFrameDetailsDebuggerFrameIndex,
8326 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
8327
8328 // Fill the dynamic part.
8329 int details_index = kFrameDetailsFirstDynamicIndex;
8330
8331 // Add arguments name and value.
8332 for (int i = 0; i < argument_count; i++) {
8333 // Name of the argument.
8334 if (i < info.number_of_parameters()) {
8335 details->set(details_index++, *info.parameter_name(i));
8336 } else {
8337 details->set(details_index++, Heap::undefined_value());
8338 }
8339
8340 // Parameter value.
8341 if (i < it.frame()->GetProvidedParametersCount()) {
8342 details->set(details_index++, it.frame()->GetParameter(i));
8343 } else {
8344 details->set(details_index++, Heap::undefined_value());
8345 }
8346 }
8347
8348 // Add locals name and value from the temporary copy from the function frame.
8349 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
8350 details->set(details_index++, locals->get(i));
8351 }
8352
8353 // Add the receiver (same as in function frame).
8354 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
8355 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
8356 Handle<Object> receiver(it.frame()->receiver());
8357 if (!receiver->IsJSObject()) {
8358 // If the receiver is NOT a JSObject we have hit an optimization
8359 // where a value object is not converted into a wrapped JS objects.
8360 // To hide this optimization from the debugger, we wrap the receiver
8361 // by creating correct wrapper object based on the calling frame's
8362 // global context.
8363 it.Advance();
8364 Handle<Context> calling_frames_global_context(
8365 Context::cast(Context::cast(it.frame()->context())->global_context()));
8366 receiver = Factory::ToObject(receiver, calling_frames_global_context);
8367 }
8368 details->set(kFrameDetailsReceiverIndex, *receiver);
8369
8370 ASSERT_EQ(details_size, details_index);
8371 return *Factory::NewJSArrayWithElements(details);
8372}
8373
8374
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008375// Copy all the context locals into an object used to materialize a scope.
8376static void CopyContextLocalsToScopeObject(Handle<Code> code,
8377 ScopeInfo<>& scope_info,
8378 Handle<Context> context,
8379 Handle<JSObject> scope_object) {
8380 // Fill all context locals to the context extension.
8381 for (int i = Context::MIN_CONTEXT_SLOTS;
8382 i < scope_info.number_of_context_slots();
8383 i++) {
8384 int context_index =
8385 ScopeInfo<>::ContextSlotIndex(*code,
8386 *scope_info.context_slot_name(i),
8387 NULL);
8388
8389 // Don't include the arguments shadow (.arguments) context variable.
8390 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
8391 SetProperty(scope_object,
8392 scope_info.context_slot_name(i),
8393 Handle<Object>(context->get(context_index)), NONE);
8394 }
8395 }
8396}
8397
8398
8399// Create a plain JSObject which materializes the local scope for the specified
8400// frame.
8401static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
8402 Handle<JSFunction> function(JSFunction::cast(frame->function()));
8403 Handle<Code> code(function->code());
8404 ScopeInfo<> scope_info(*code);
8405
8406 // Allocate and initialize a JSObject with all the arguments, stack locals
8407 // heap locals and extension properties of the debugged function.
8408 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
8409
8410 // First fill all parameters.
8411 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8412 SetProperty(local_scope,
8413 scope_info.parameter_name(i),
8414 Handle<Object>(frame->GetParameter(i)), NONE);
8415 }
8416
8417 // Second fill all stack locals.
8418 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
8419 SetProperty(local_scope,
8420 scope_info.stack_slot_name(i),
8421 Handle<Object>(frame->GetExpression(i)), NONE);
8422 }
8423
8424 // Third fill all context locals.
8425 Handle<Context> frame_context(Context::cast(frame->context()));
8426 Handle<Context> function_context(frame_context->fcontext());
8427 CopyContextLocalsToScopeObject(code, scope_info,
8428 function_context, local_scope);
8429
8430 // Finally copy any properties from the function context extension. This will
8431 // be variables introduced by eval.
8432 if (function_context->closure() == *function) {
8433 if (function_context->has_extension() &&
8434 !function_context->IsGlobalContext()) {
8435 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008436 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008437 for (int i = 0; i < keys->length(); i++) {
8438 // Names of variables introduced by eval are strings.
8439 ASSERT(keys->get(i)->IsString());
8440 Handle<String> key(String::cast(keys->get(i)));
8441 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
8442 }
8443 }
8444 }
8445 return local_scope;
8446}
8447
8448
8449// Create a plain JSObject which materializes the closure content for the
8450// context.
8451static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
8452 ASSERT(context->is_function_context());
8453
8454 Handle<Code> code(context->closure()->code());
8455 ScopeInfo<> scope_info(*code);
8456
8457 // Allocate and initialize a JSObject with all the content of theis function
8458 // closure.
8459 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
8460
8461 // Check whether the arguments shadow object exists.
8462 int arguments_shadow_index =
8463 ScopeInfo<>::ContextSlotIndex(*code,
8464 Heap::arguments_shadow_symbol(),
8465 NULL);
8466 if (arguments_shadow_index >= 0) {
8467 // In this case all the arguments are available in the arguments shadow
8468 // object.
8469 Handle<JSObject> arguments_shadow(
8470 JSObject::cast(context->get(arguments_shadow_index)));
8471 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8472 SetProperty(closure_scope,
8473 scope_info.parameter_name(i),
8474 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
8475 }
8476 }
8477
8478 // Fill all context locals to the context extension.
8479 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
8480
8481 // Finally copy any properties from the function context extension. This will
8482 // be variables introduced by eval.
8483 if (context->has_extension()) {
8484 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008485 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008486 for (int i = 0; i < keys->length(); i++) {
8487 // Names of variables introduced by eval are strings.
8488 ASSERT(keys->get(i)->IsString());
8489 Handle<String> key(String::cast(keys->get(i)));
8490 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
8491 }
8492 }
8493
8494 return closure_scope;
8495}
8496
8497
8498// Iterate over the actual scopes visible from a stack frame. All scopes are
8499// backed by an actual context except the local scope, which is inserted
8500// "artifically" in the context chain.
8501class ScopeIterator {
8502 public:
8503 enum ScopeType {
8504 ScopeTypeGlobal = 0,
8505 ScopeTypeLocal,
8506 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00008507 ScopeTypeClosure,
8508 // Every catch block contains an implicit with block (its parameter is
8509 // a JSContextExtensionObject) that extends current scope with a variable
8510 // holding exception object. Such with blocks are treated as scopes of their
8511 // own type.
8512 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008513 };
8514
8515 explicit ScopeIterator(JavaScriptFrame* frame)
8516 : frame_(frame),
8517 function_(JSFunction::cast(frame->function())),
8518 context_(Context::cast(frame->context())),
8519 local_done_(false),
8520 at_local_(false) {
8521
8522 // Check whether the first scope is actually a local scope.
8523 if (context_->IsGlobalContext()) {
8524 // If there is a stack slot for .result then this local scope has been
8525 // created for evaluating top level code and it is not a real local scope.
8526 // Checking for the existence of .result seems fragile, but the scope info
8527 // saved with the code object does not otherwise have that information.
8528 Handle<Code> code(function_->code());
8529 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
8530 at_local_ = index < 0;
8531 } else if (context_->is_function_context()) {
8532 at_local_ = true;
8533 }
8534 }
8535
8536 // More scopes?
8537 bool Done() { return context_.is_null(); }
8538
8539 // Move to the next scope.
8540 void Next() {
8541 // If at a local scope mark the local scope as passed.
8542 if (at_local_) {
8543 at_local_ = false;
8544 local_done_ = true;
8545
8546 // If the current context is not associated with the local scope the
8547 // current context is the next real scope, so don't move to the next
8548 // context in this case.
8549 if (context_->closure() != *function_) {
8550 return;
8551 }
8552 }
8553
8554 // The global scope is always the last in the chain.
8555 if (context_->IsGlobalContext()) {
8556 context_ = Handle<Context>();
8557 return;
8558 }
8559
8560 // Move to the next context.
8561 if (context_->is_function_context()) {
8562 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
8563 } else {
8564 context_ = Handle<Context>(context_->previous());
8565 }
8566
8567 // If passing the local scope indicate that the current scope is now the
8568 // local scope.
8569 if (!local_done_ &&
8570 (context_->IsGlobalContext() || (context_->is_function_context()))) {
8571 at_local_ = true;
8572 }
8573 }
8574
8575 // Return the type of the current scope.
8576 int Type() {
8577 if (at_local_) {
8578 return ScopeTypeLocal;
8579 }
8580 if (context_->IsGlobalContext()) {
8581 ASSERT(context_->global()->IsGlobalObject());
8582 return ScopeTypeGlobal;
8583 }
8584 if (context_->is_function_context()) {
8585 return ScopeTypeClosure;
8586 }
8587 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00008588 // Current scope is either an explicit with statement or a with statement
8589 // implicitely generated for a catch block.
8590 // If the extension object here is a JSContextExtensionObject then
8591 // current with statement is one frome a catch block otherwise it's a
8592 // regular with statement.
8593 if (context_->extension()->IsJSContextExtensionObject()) {
8594 return ScopeTypeCatch;
8595 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008596 return ScopeTypeWith;
8597 }
8598
8599 // Return the JavaScript object with the content of the current scope.
8600 Handle<JSObject> ScopeObject() {
8601 switch (Type()) {
8602 case ScopeIterator::ScopeTypeGlobal:
8603 return Handle<JSObject>(CurrentContext()->global());
8604 break;
8605 case ScopeIterator::ScopeTypeLocal:
8606 // Materialize the content of the local scope into a JSObject.
8607 return MaterializeLocalScope(frame_);
8608 break;
8609 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00008610 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008611 // Return the with object.
8612 return Handle<JSObject>(CurrentContext()->extension());
8613 break;
8614 case ScopeIterator::ScopeTypeClosure:
8615 // Materialize the content of the closure scope into a JSObject.
8616 return MaterializeClosure(CurrentContext());
8617 break;
8618 }
8619 UNREACHABLE();
8620 return Handle<JSObject>();
8621 }
8622
8623 // Return the context for this scope. For the local context there might not
8624 // be an actual context.
8625 Handle<Context> CurrentContext() {
8626 if (at_local_ && context_->closure() != *function_) {
8627 return Handle<Context>();
8628 }
8629 return context_;
8630 }
8631
8632#ifdef DEBUG
8633 // Debug print of the content of the current scope.
8634 void DebugPrint() {
8635 switch (Type()) {
8636 case ScopeIterator::ScopeTypeGlobal:
8637 PrintF("Global:\n");
8638 CurrentContext()->Print();
8639 break;
8640
8641 case ScopeIterator::ScopeTypeLocal: {
8642 PrintF("Local:\n");
8643 Handle<Code> code(function_->code());
8644 ScopeInfo<> scope_info(*code);
8645 scope_info.Print();
8646 if (!CurrentContext().is_null()) {
8647 CurrentContext()->Print();
8648 if (CurrentContext()->has_extension()) {
8649 Handle<JSObject> extension =
8650 Handle<JSObject>(CurrentContext()->extension());
8651 if (extension->IsJSContextExtensionObject()) {
8652 extension->Print();
8653 }
8654 }
8655 }
8656 break;
8657 }
8658
8659 case ScopeIterator::ScopeTypeWith: {
8660 PrintF("With:\n");
8661 Handle<JSObject> extension =
8662 Handle<JSObject>(CurrentContext()->extension());
8663 extension->Print();
8664 break;
8665 }
8666
ager@chromium.orga1645e22009-09-09 19:27:10 +00008667 case ScopeIterator::ScopeTypeCatch: {
8668 PrintF("Catch:\n");
8669 Handle<JSObject> extension =
8670 Handle<JSObject>(CurrentContext()->extension());
8671 extension->Print();
8672 break;
8673 }
8674
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008675 case ScopeIterator::ScopeTypeClosure: {
8676 PrintF("Closure:\n");
8677 CurrentContext()->Print();
8678 if (CurrentContext()->has_extension()) {
8679 Handle<JSObject> extension =
8680 Handle<JSObject>(CurrentContext()->extension());
8681 if (extension->IsJSContextExtensionObject()) {
8682 extension->Print();
8683 }
8684 }
8685 break;
8686 }
8687
8688 default:
8689 UNREACHABLE();
8690 }
8691 PrintF("\n");
8692 }
8693#endif
8694
8695 private:
8696 JavaScriptFrame* frame_;
8697 Handle<JSFunction> function_;
8698 Handle<Context> context_;
8699 bool local_done_;
8700 bool at_local_;
8701
8702 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
8703};
8704
8705
8706static Object* Runtime_GetScopeCount(Arguments args) {
8707 HandleScope scope;
8708 ASSERT(args.length() == 2);
8709
8710 // Check arguments.
8711 Object* check = Runtime_CheckExecutionState(args);
8712 if (check->IsFailure()) return check;
8713 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8714
8715 // Get the frame where the debugging is performed.
8716 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8717 JavaScriptFrameIterator it(id);
8718 JavaScriptFrame* frame = it.frame();
8719
8720 // Count the visible scopes.
8721 int n = 0;
8722 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8723 n++;
8724 }
8725
8726 return Smi::FromInt(n);
8727}
8728
8729
8730static const int kScopeDetailsTypeIndex = 0;
8731static const int kScopeDetailsObjectIndex = 1;
8732static const int kScopeDetailsSize = 2;
8733
8734// Return an array with scope details
8735// args[0]: number: break id
8736// args[1]: number: frame index
8737// args[2]: number: scope index
8738//
8739// The array returned contains the following information:
8740// 0: Scope type
8741// 1: Scope object
8742static Object* Runtime_GetScopeDetails(Arguments args) {
8743 HandleScope scope;
8744 ASSERT(args.length() == 3);
8745
8746 // Check arguments.
8747 Object* check = Runtime_CheckExecutionState(args);
8748 if (check->IsFailure()) return check;
8749 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8750 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
8751
8752 // Get the frame where the debugging is performed.
8753 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8754 JavaScriptFrameIterator frame_it(id);
8755 JavaScriptFrame* frame = frame_it.frame();
8756
8757 // Find the requested scope.
8758 int n = 0;
8759 ScopeIterator it(frame);
8760 for (; !it.Done() && n < index; it.Next()) {
8761 n++;
8762 }
8763 if (it.Done()) {
8764 return Heap::undefined_value();
8765 }
8766
8767 // Calculate the size of the result.
8768 int details_size = kScopeDetailsSize;
8769 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8770
8771 // Fill in scope details.
8772 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
8773 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
8774
8775 return *Factory::NewJSArrayWithElements(details);
8776}
8777
8778
8779static Object* Runtime_DebugPrintScopes(Arguments args) {
8780 HandleScope scope;
8781 ASSERT(args.length() == 0);
8782
8783#ifdef DEBUG
8784 // Print the scopes for the top frame.
8785 StackFrameLocator locator;
8786 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
8787 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8788 it.DebugPrint();
8789 }
8790#endif
8791 return Heap::undefined_value();
8792}
8793
8794
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008795static Object* Runtime_GetCFrames(Arguments args) {
8796 HandleScope scope;
8797 ASSERT(args.length() == 1);
8798 Object* result = Runtime_CheckExecutionState(args);
8799 if (result->IsFailure()) return result;
8800
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008801#if V8_HOST_ARCH_64_BIT
8802 UNIMPLEMENTED();
8803 return Heap::undefined_value();
8804#else
8805
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008806 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008807 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
8808 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008809 if (frames_count == OS::kStackWalkError) {
8810 return Heap::undefined_value();
8811 }
8812
8813 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
8814 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
8815 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
8816 for (int i = 0; i < frames_count; i++) {
8817 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
8818 frame_value->SetProperty(
8819 *address_str,
8820 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
8821 NONE);
8822
8823 // Get the stack walk text for this frame.
8824 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008825 int frame_text_length = StrLength(frames[i].text);
8826 if (frame_text_length > 0) {
8827 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008828 frame_text = Factory::NewStringFromAscii(str);
8829 }
8830
8831 if (!frame_text.is_null()) {
8832 frame_value->SetProperty(*text_str, *frame_text, NONE);
8833 }
8834
8835 frames_array->set(i, *frame_value);
8836 }
8837 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008838#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008839}
8840
8841
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00008842static Object* Runtime_GetThreadCount(Arguments args) {
8843 HandleScope scope;
8844 ASSERT(args.length() == 1);
8845
8846 // Check arguments.
8847 Object* result = Runtime_CheckExecutionState(args);
8848 if (result->IsFailure()) return result;
8849
8850 // Count all archived V8 threads.
8851 int n = 0;
8852 for (ThreadState* thread = ThreadState::FirstInUse();
8853 thread != NULL;
8854 thread = thread->Next()) {
8855 n++;
8856 }
8857
8858 // Total number of threads is current thread and archived threads.
8859 return Smi::FromInt(n + 1);
8860}
8861
8862
8863static const int kThreadDetailsCurrentThreadIndex = 0;
8864static const int kThreadDetailsThreadIdIndex = 1;
8865static const int kThreadDetailsSize = 2;
8866
8867// Return an array with thread details
8868// args[0]: number: break id
8869// args[1]: number: thread index
8870//
8871// The array returned contains the following information:
8872// 0: Is current thread?
8873// 1: Thread id
8874static Object* Runtime_GetThreadDetails(Arguments args) {
8875 HandleScope scope;
8876 ASSERT(args.length() == 2);
8877
8878 // Check arguments.
8879 Object* check = Runtime_CheckExecutionState(args);
8880 if (check->IsFailure()) return check;
8881 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8882
8883 // Allocate array for result.
8884 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
8885
8886 // Thread index 0 is current thread.
8887 if (index == 0) {
8888 // Fill the details.
8889 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
8890 details->set(kThreadDetailsThreadIdIndex,
8891 Smi::FromInt(ThreadManager::CurrentId()));
8892 } else {
8893 // Find the thread with the requested index.
8894 int n = 1;
8895 ThreadState* thread = ThreadState::FirstInUse();
8896 while (index != n && thread != NULL) {
8897 thread = thread->Next();
8898 n++;
8899 }
8900 if (thread == NULL) {
8901 return Heap::undefined_value();
8902 }
8903
8904 // Fill the details.
8905 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
8906 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
8907 }
8908
8909 // Convert to JS array and return.
8910 return *Factory::NewJSArrayWithElements(details);
8911}
8912
8913
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008914static Object* Runtime_GetBreakLocations(Arguments args) {
8915 HandleScope scope;
8916 ASSERT(args.length() == 1);
8917
ager@chromium.org5aa501c2009-06-23 07:57:28 +00008918 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
8919 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008920 // Find the number of break points
8921 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
8922 if (break_locations->IsUndefined()) return Heap::undefined_value();
8923 // Return array as JS array
8924 return *Factory::NewJSArrayWithElements(
8925 Handle<FixedArray>::cast(break_locations));
8926}
8927
8928
8929// Set a break point in a function
8930// args[0]: function
8931// args[1]: number: break source position (within the function source)
8932// args[2]: number: break point object
8933static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
8934 HandleScope scope;
8935 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00008936 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
8937 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008938 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
8939 RUNTIME_ASSERT(source_position >= 0);
8940 Handle<Object> break_point_object_arg = args.at<Object>(2);
8941
8942 // Set break point.
8943 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
8944
8945 return Heap::undefined_value();
8946}
8947
8948
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00008949Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
8950 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008951 // Iterate the heap looking for SharedFunctionInfo generated from the
8952 // script. The inner most SharedFunctionInfo containing the source position
8953 // for the requested break point is found.
8954 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
8955 // which is found is not compiled it is compiled and the heap is iterated
8956 // again as the compilation might create inner functions from the newly
8957 // compiled function and the actual requested break point might be in one of
8958 // these functions.
8959 bool done = false;
8960 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00008961 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008962 Handle<SharedFunctionInfo> target;
8963 // The current candidate for the last function in script:
8964 Handle<SharedFunctionInfo> last;
8965 while (!done) {
8966 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008967 for (HeapObject* obj = iterator.next();
8968 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008969 if (obj->IsSharedFunctionInfo()) {
8970 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
8971 if (shared->script() == *script) {
8972 // If the SharedFunctionInfo found has the requested script data and
8973 // contains the source position it is a candidate.
8974 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00008975 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008976 start_position = shared->start_position();
8977 }
8978 if (start_position <= position &&
8979 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00008980 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008981 // candidate this is the new candidate.
8982 if (target.is_null()) {
8983 target_start_position = start_position;
8984 target = shared;
8985 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00008986 if (target_start_position == start_position &&
8987 shared->end_position() == target->end_position()) {
8988 // If a top-level function contain only one function
8989 // declartion the source for the top-level and the function is
8990 // the same. In that case prefer the non top-level function.
8991 if (!shared->is_toplevel()) {
8992 target_start_position = start_position;
8993 target = shared;
8994 }
8995 } else if (target_start_position <= start_position &&
8996 shared->end_position() <= target->end_position()) {
8997 // This containment check includes equality as a function inside
8998 // a top-level function can share either start or end position
8999 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009000 target_start_position = start_position;
9001 target = shared;
9002 }
9003 }
9004 }
9005
9006 // Keep track of the last function in the script.
9007 if (last.is_null() ||
9008 shared->end_position() > last->start_position()) {
9009 last = shared;
9010 }
9011 }
9012 }
9013 }
9014
9015 // Make sure some candidate is selected.
9016 if (target.is_null()) {
9017 if (!last.is_null()) {
9018 // Position after the last function - use last.
9019 target = last;
9020 } else {
9021 // Unable to find function - possibly script without any function.
9022 return Heap::undefined_value();
9023 }
9024 }
9025
9026 // If the candidate found is compiled we are done. NOTE: when lazy
9027 // compilation of inner functions is introduced some additional checking
9028 // needs to be done here to compile inner functions.
9029 done = target->is_compiled();
9030 if (!done) {
9031 // If the candidate is not compiled compile it to reveal any inner
9032 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009033 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009034 }
9035 }
9036
9037 return *target;
9038}
9039
9040
9041// Change the state of a break point in a script. NOTE: Regarding performance
9042// see the NOTE for GetScriptFromScriptData.
9043// args[0]: script to set break point in
9044// args[1]: number: break source position (within the script source)
9045// args[2]: number: break point object
9046static Object* Runtime_SetScriptBreakPoint(Arguments args) {
9047 HandleScope scope;
9048 ASSERT(args.length() == 3);
9049 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
9050 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9051 RUNTIME_ASSERT(source_position >= 0);
9052 Handle<Object> break_point_object_arg = args.at<Object>(2);
9053
9054 // Get the script from the script wrapper.
9055 RUNTIME_ASSERT(wrapper->value()->IsScript());
9056 Handle<Script> script(Script::cast(wrapper->value()));
9057
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009058 Object* result = Runtime::FindSharedFunctionInfoInScript(
9059 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009060 if (!result->IsUndefined()) {
9061 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
9062 // Find position within function. The script position might be before the
9063 // source position of the first function.
9064 int position;
9065 if (shared->start_position() > source_position) {
9066 position = 0;
9067 } else {
9068 position = source_position - shared->start_position();
9069 }
9070 Debug::SetBreakPoint(shared, position, break_point_object_arg);
9071 }
9072 return Heap::undefined_value();
9073}
9074
9075
9076// Clear a break point
9077// args[0]: number: break point object
9078static Object* Runtime_ClearBreakPoint(Arguments args) {
9079 HandleScope scope;
9080 ASSERT(args.length() == 1);
9081 Handle<Object> break_point_object_arg = args.at<Object>(0);
9082
9083 // Clear break point.
9084 Debug::ClearBreakPoint(break_point_object_arg);
9085
9086 return Heap::undefined_value();
9087}
9088
9089
9090// Change the state of break on exceptions
9091// args[0]: boolean indicating uncaught exceptions
9092// args[1]: boolean indicating on/off
9093static Object* Runtime_ChangeBreakOnException(Arguments args) {
9094 HandleScope scope;
9095 ASSERT(args.length() == 2);
9096 ASSERT(args[0]->IsNumber());
9097 ASSERT(args[1]->IsBoolean());
9098
9099 // Update break point state
9100 ExceptionBreakType type =
9101 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
9102 bool enable = args[1]->ToBoolean()->IsTrue();
9103 Debug::ChangeBreakOnException(type, enable);
9104 return Heap::undefined_value();
9105}
9106
9107
9108// Prepare for stepping
9109// args[0]: break id for checking execution state
9110// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00009111// args[2]: number of times to perform the step, for step out it is the number
9112// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009113static Object* Runtime_PrepareStep(Arguments args) {
9114 HandleScope scope;
9115 ASSERT(args.length() == 3);
9116 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009117 Object* check = Runtime_CheckExecutionState(args);
9118 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009119 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
9120 return Top::Throw(Heap::illegal_argument_symbol());
9121 }
9122
9123 // Get the step action and check validity.
9124 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
9125 if (step_action != StepIn &&
9126 step_action != StepNext &&
9127 step_action != StepOut &&
9128 step_action != StepInMin &&
9129 step_action != StepMin) {
9130 return Top::Throw(Heap::illegal_argument_symbol());
9131 }
9132
9133 // Get the number of steps.
9134 int step_count = NumberToInt32(args[2]);
9135 if (step_count < 1) {
9136 return Top::Throw(Heap::illegal_argument_symbol());
9137 }
9138
ager@chromium.orga1645e22009-09-09 19:27:10 +00009139 // Clear all current stepping setup.
9140 Debug::ClearStepping();
9141
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009142 // Prepare step.
9143 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
9144 return Heap::undefined_value();
9145}
9146
9147
9148// Clear all stepping set by PrepareStep.
9149static Object* Runtime_ClearStepping(Arguments args) {
9150 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009151 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009152 Debug::ClearStepping();
9153 return Heap::undefined_value();
9154}
9155
9156
9157// Creates a copy of the with context chain. The copy of the context chain is
9158// is linked to the function context supplied.
9159static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
9160 Handle<Context> function_context) {
9161 // At the bottom of the chain. Return the function context to link to.
9162 if (context_chain->is_function_context()) {
9163 return function_context;
9164 }
9165
9166 // Recursively copy the with contexts.
9167 Handle<Context> previous(context_chain->previous());
9168 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
9169 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009170 CopyWithContextChain(function_context, previous),
9171 extension,
9172 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009173}
9174
9175
9176// Helper function to find or create the arguments object for
9177// Runtime_DebugEvaluate.
9178static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
9179 Handle<JSFunction> function,
9180 Handle<Code> code,
9181 const ScopeInfo<>* sinfo,
9182 Handle<Context> function_context) {
9183 // Try to find the value of 'arguments' to pass as parameter. If it is not
9184 // found (that is the debugged function does not reference 'arguments' and
9185 // does not support eval) then create an 'arguments' object.
9186 int index;
9187 if (sinfo->number_of_stack_slots() > 0) {
9188 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
9189 if (index != -1) {
9190 return Handle<Object>(frame->GetExpression(index));
9191 }
9192 }
9193
9194 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
9195 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
9196 NULL);
9197 if (index != -1) {
9198 return Handle<Object>(function_context->get(index));
9199 }
9200 }
9201
9202 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009203 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
9204 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009205
9206 AssertNoAllocation no_gc;
9207 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009208 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009209 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009210 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009211 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009212 return arguments;
9213}
9214
9215
9216// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00009217// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009218// extension part has all the parameters and locals of the function on the
9219// stack frame. A function which calls eval with the code to evaluate is then
9220// compiled in this context and called in this context. As this context
9221// replaces the context of the function on the stack frame a new (empty)
9222// function is created as well to be used as the closure for the context.
9223// This function and the context acts as replacements for the function on the
9224// stack frame presenting the same view of the values of parameters and
9225// local variables as if the piece of JavaScript was evaluated at the point
9226// where the function on the stack frame is currently stopped.
9227static Object* Runtime_DebugEvaluate(Arguments args) {
9228 HandleScope scope;
9229
9230 // Check the execution state and decode arguments frame and source to be
9231 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009232 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009233 Object* check_result = Runtime_CheckExecutionState(args);
9234 if (check_result->IsFailure()) return check_result;
9235 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9236 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009237 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
9238
9239 // Handle the processing of break.
9240 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009241
9242 // Get the frame where the debugging is performed.
9243 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9244 JavaScriptFrameIterator it(id);
9245 JavaScriptFrame* frame = it.frame();
9246 Handle<JSFunction> function(JSFunction::cast(frame->function()));
9247 Handle<Code> code(function->code());
9248 ScopeInfo<> sinfo(*code);
9249
9250 // Traverse the saved contexts chain to find the active context for the
9251 // selected frame.
9252 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009253 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009254 save = save->prev();
9255 }
9256 ASSERT(save != NULL);
9257 SaveContext savex;
9258 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009259
9260 // Create the (empty) function replacing the function on the stack frame for
9261 // the purpose of evaluating in the context created below. It is important
9262 // that this function does not describe any parameters and local variables
9263 // in the context. If it does then this will cause problems with the lookup
9264 // in Context::Lookup, where context slots for parameters and local variables
9265 // are looked at before the extension object.
9266 Handle<JSFunction> go_between =
9267 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
9268 go_between->set_context(function->context());
9269#ifdef DEBUG
9270 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
9271 ASSERT(go_between_sinfo.number_of_parameters() == 0);
9272 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
9273#endif
9274
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009275 // Materialize the content of the local scope into a JSObject.
9276 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009277
9278 // Allocate a new context for the debug evaluation and set the extension
9279 // object build.
9280 Handle<Context> context =
9281 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009282 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009283 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009284 Handle<Context> frame_context(Context::cast(frame->context()));
9285 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009286 context = CopyWithContextChain(frame_context, context);
9287
9288 // Wrap the evaluation statement in a new function compiled in the newly
9289 // created context. The function has one parameter which has to be called
9290 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00009291 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009292 // function(arguments,__source__) {return eval(__source__);}
9293 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00009294 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009295 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009296 Handle<String> function_source =
9297 Factory::NewStringFromAscii(Vector<const char>(source_str,
9298 source_str_length));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009299 Handle<SharedFunctionInfo> shared =
ager@chromium.org381abbb2009-02-25 13:23:22 +00009300 Compiler::CompileEval(function_source,
9301 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00009302 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00009303 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009304 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009305 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009306 Factory::NewFunctionFromSharedFunctionInfo(shared, context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009307
9308 // Invoke the result of the compilation to get the evaluation function.
9309 bool has_pending_exception;
9310 Handle<Object> receiver(frame->receiver());
9311 Handle<Object> evaluation_function =
9312 Execution::Call(compiled_function, receiver, 0, NULL,
9313 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009314 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009315
9316 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
9317 function_context);
9318
9319 // Invoke the evaluation function and return the result.
9320 const int argc = 2;
9321 Object** argv[argc] = { arguments.location(),
9322 Handle<Object>::cast(source).location() };
9323 Handle<Object> result =
9324 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
9325 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009326 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009327
9328 // Skip the global proxy as it has no properties and always delegates to the
9329 // real global object.
9330 if (result->IsJSGlobalProxy()) {
9331 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
9332 }
9333
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009334 return *result;
9335}
9336
9337
9338static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
9339 HandleScope scope;
9340
9341 // Check the execution state and decode arguments frame and source to be
9342 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009343 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009344 Object* check_result = Runtime_CheckExecutionState(args);
9345 if (check_result->IsFailure()) return check_result;
9346 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009347 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
9348
9349 // Handle the processing of break.
9350 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009351
9352 // Enter the top context from before the debugger was invoked.
9353 SaveContext save;
9354 SaveContext* top = &save;
9355 while (top != NULL && *top->context() == *Debug::debug_context()) {
9356 top = top->prev();
9357 }
9358 if (top != NULL) {
9359 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009360 }
9361
9362 // Get the global context now set to the top context from before the
9363 // debugger was invoked.
9364 Handle<Context> context = Top::global_context();
9365
9366 // Compile the source to be evaluated.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009367 Handle<SharedFunctionInfo> shared =
9368 Compiler::CompileEval(source,
9369 context,
9370 true,
9371 Compiler::DONT_VALIDATE_JSON);
9372 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009373 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009374 Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
9375 context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009376
9377 // Invoke the result of the compilation to get the evaluation function.
9378 bool has_pending_exception;
9379 Handle<Object> receiver = Top::global();
9380 Handle<Object> result =
9381 Execution::Call(compiled_function, receiver, 0, NULL,
9382 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009383 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009384 return *result;
9385}
9386
9387
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009388static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
9389 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009390 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009391
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009392 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009393 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009394
9395 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009396 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00009397 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
9398 // Get the script wrapper in a local handle before calling GetScriptWrapper,
9399 // because using
9400 // instances->set(i, *GetScriptWrapper(script))
9401 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
9402 // already have deferenced the instances handle.
9403 Handle<JSValue> wrapper = GetScriptWrapper(script);
9404 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009405 }
9406
9407 // Return result as a JS array.
9408 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
9409 Handle<JSArray>::cast(result)->SetContent(*instances);
9410 return *result;
9411}
9412
9413
9414// Helper function used by Runtime_DebugReferencedBy below.
9415static int DebugReferencedBy(JSObject* target,
9416 Object* instance_filter, int max_references,
9417 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009418 JSFunction* arguments_function) {
9419 NoHandleAllocation ha;
9420 AssertNoAllocation no_alloc;
9421
9422 // Iterate the heap.
9423 int count = 0;
9424 JSObject* last = NULL;
9425 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009426 HeapObject* heap_obj = NULL;
9427 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009428 (max_references == 0 || count < max_references)) {
9429 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009430 if (heap_obj->IsJSObject()) {
9431 // Skip context extension objects and argument arrays as these are
9432 // checked in the context of functions using them.
9433 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00009434 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009435 obj->map()->constructor() == arguments_function) {
9436 continue;
9437 }
9438
9439 // Check if the JS object has a reference to the object looked for.
9440 if (obj->ReferencesObject(target)) {
9441 // Check instance filter if supplied. This is normally used to avoid
9442 // references from mirror objects (see Runtime_IsInPrototypeChain).
9443 if (!instance_filter->IsUndefined()) {
9444 Object* V = obj;
9445 while (true) {
9446 Object* prototype = V->GetPrototype();
9447 if (prototype->IsNull()) {
9448 break;
9449 }
9450 if (instance_filter == prototype) {
9451 obj = NULL; // Don't add this object.
9452 break;
9453 }
9454 V = prototype;
9455 }
9456 }
9457
9458 if (obj != NULL) {
9459 // Valid reference found add to instance array if supplied an update
9460 // count.
9461 if (instances != NULL && count < instances_size) {
9462 instances->set(count, obj);
9463 }
9464 last = obj;
9465 count++;
9466 }
9467 }
9468 }
9469 }
9470
9471 // Check for circular reference only. This can happen when the object is only
9472 // referenced from mirrors and has a circular reference in which case the
9473 // object is not really alive and would have been garbage collected if not
9474 // referenced from the mirror.
9475 if (count == 1 && last == target) {
9476 count = 0;
9477 }
9478
9479 // Return the number of referencing objects found.
9480 return count;
9481}
9482
9483
9484// Scan the heap for objects with direct references to an object
9485// args[0]: the object to find references to
9486// args[1]: constructor function for instances to exclude (Mirror)
9487// args[2]: the the maximum number of objects to return
9488static Object* Runtime_DebugReferencedBy(Arguments args) {
9489 ASSERT(args.length() == 3);
9490
9491 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009492 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009493
9494 // Check parameters.
9495 CONVERT_CHECKED(JSObject, target, args[0]);
9496 Object* instance_filter = args[1];
9497 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
9498 instance_filter->IsJSObject());
9499 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
9500 RUNTIME_ASSERT(max_references >= 0);
9501
9502 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009503 JSObject* arguments_boilerplate =
9504 Top::context()->global_context()->arguments_boilerplate();
9505 JSFunction* arguments_function =
9506 JSFunction::cast(arguments_boilerplate->map()->constructor());
9507
9508 // Get the number of referencing objects.
9509 int count;
9510 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009511 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009512
9513 // Allocate an array to hold the result.
9514 Object* object = Heap::AllocateFixedArray(count);
9515 if (object->IsFailure()) return object;
9516 FixedArray* instances = FixedArray::cast(object);
9517
9518 // Fill the referencing objects.
9519 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009520 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009521
9522 // Return result as JS array.
9523 Object* result =
9524 Heap::AllocateJSObject(
9525 Top::context()->global_context()->array_function());
9526 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9527 return result;
9528}
9529
9530
9531// Helper function used by Runtime_DebugConstructedBy below.
9532static int DebugConstructedBy(JSFunction* constructor, int max_references,
9533 FixedArray* instances, int instances_size) {
9534 AssertNoAllocation no_alloc;
9535
9536 // Iterate the heap.
9537 int count = 0;
9538 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009539 HeapObject* heap_obj = NULL;
9540 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009541 (max_references == 0 || count < max_references)) {
9542 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009543 if (heap_obj->IsJSObject()) {
9544 JSObject* obj = JSObject::cast(heap_obj);
9545 if (obj->map()->constructor() == constructor) {
9546 // Valid reference found add to instance array if supplied an update
9547 // count.
9548 if (instances != NULL && count < instances_size) {
9549 instances->set(count, obj);
9550 }
9551 count++;
9552 }
9553 }
9554 }
9555
9556 // Return the number of referencing objects found.
9557 return count;
9558}
9559
9560
9561// Scan the heap for objects constructed by a specific function.
9562// args[0]: the constructor to find instances of
9563// args[1]: the the maximum number of objects to return
9564static Object* Runtime_DebugConstructedBy(Arguments args) {
9565 ASSERT(args.length() == 2);
9566
9567 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009568 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009569
9570 // Check parameters.
9571 CONVERT_CHECKED(JSFunction, constructor, args[0]);
9572 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
9573 RUNTIME_ASSERT(max_references >= 0);
9574
9575 // Get the number of referencing objects.
9576 int count;
9577 count = DebugConstructedBy(constructor, max_references, NULL, 0);
9578
9579 // Allocate an array to hold the result.
9580 Object* object = Heap::AllocateFixedArray(count);
9581 if (object->IsFailure()) return object;
9582 FixedArray* instances = FixedArray::cast(object);
9583
9584 // Fill the referencing objects.
9585 count = DebugConstructedBy(constructor, max_references, instances, count);
9586
9587 // Return result as JS array.
9588 Object* result =
9589 Heap::AllocateJSObject(
9590 Top::context()->global_context()->array_function());
9591 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9592 return result;
9593}
9594
9595
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009596// Find the effective prototype object as returned by __proto__.
9597// args[0]: the object to find the prototype for.
9598static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009599 ASSERT(args.length() == 1);
9600
9601 CONVERT_CHECKED(JSObject, obj, args[0]);
9602
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009603 // Use the __proto__ accessor.
9604 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009605}
9606
9607
9608static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009609 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009610 CPU::DebugBreak();
9611 return Heap::undefined_value();
9612}
9613
9614
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009615static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009616#ifdef DEBUG
9617 HandleScope scope;
9618 ASSERT(args.length() == 1);
9619 // Get the function and make sure it is compiled.
9620 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009621 Handle<SharedFunctionInfo> shared(func->shared());
9622 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009623 return Failure::Exception();
9624 }
9625 func->code()->PrintLn();
9626#endif // DEBUG
9627 return Heap::undefined_value();
9628}
ager@chromium.org9085a012009-05-11 19:22:57 +00009629
9630
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009631static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
9632#ifdef DEBUG
9633 HandleScope scope;
9634 ASSERT(args.length() == 1);
9635 // Get the function and make sure it is compiled.
9636 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009637 Handle<SharedFunctionInfo> shared(func->shared());
9638 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009639 return Failure::Exception();
9640 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009641 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009642#endif // DEBUG
9643 return Heap::undefined_value();
9644}
9645
9646
ager@chromium.org9085a012009-05-11 19:22:57 +00009647static Object* Runtime_FunctionGetInferredName(Arguments args) {
9648 NoHandleAllocation ha;
9649 ASSERT(args.length() == 1);
9650
9651 CONVERT_CHECKED(JSFunction, f, args[0]);
9652 return f->shared()->inferred_name();
9653}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009654
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009655
9656static int FindSharedFunctionInfosForScript(Script* script,
9657 FixedArray* buffer) {
9658 AssertNoAllocation no_allocations;
9659
9660 int counter = 0;
9661 int buffer_size = buffer->length();
9662 HeapIterator iterator;
9663 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
9664 ASSERT(obj != NULL);
9665 if (!obj->IsSharedFunctionInfo()) {
9666 continue;
9667 }
9668 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
9669 if (shared->script() != script) {
9670 continue;
9671 }
9672 if (counter < buffer_size) {
9673 buffer->set(counter, shared);
9674 }
9675 counter++;
9676 }
9677 return counter;
9678}
9679
9680// For a script finds all SharedFunctionInfo's in the heap that points
9681// to this script. Returns JSArray of SharedFunctionInfo wrapped
9682// in OpaqueReferences.
9683static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
9684 Arguments args) {
9685 ASSERT(args.length() == 1);
9686 HandleScope scope;
9687 CONVERT_CHECKED(JSValue, script_value, args[0]);
9688
9689 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
9690
9691 const int kBufferSize = 32;
9692
9693 Handle<FixedArray> array;
9694 array = Factory::NewFixedArray(kBufferSize);
9695 int number = FindSharedFunctionInfosForScript(*script, *array);
9696 if (number > kBufferSize) {
9697 array = Factory::NewFixedArray(number);
9698 FindSharedFunctionInfosForScript(*script, *array);
9699 }
9700
9701 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
9702 result->set_length(Smi::FromInt(number));
9703
9704 LiveEdit::WrapSharedFunctionInfos(result);
9705
9706 return *result;
9707}
9708
9709// For a script calculates compilation information about all its functions.
9710// The script source is explicitly specified by the second argument.
9711// The source of the actual script is not used, however it is important that
9712// all generated code keeps references to this particular instance of script.
9713// Returns a JSArray of compilation infos. The array is ordered so that
9714// each function with all its descendant is always stored in a continues range
9715// with the function itself going first. The root function is a script function.
9716static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
9717 ASSERT(args.length() == 2);
9718 HandleScope scope;
9719 CONVERT_CHECKED(JSValue, script, args[0]);
9720 CONVERT_ARG_CHECKED(String, source, 1);
9721 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
9722
9723 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
9724
9725 if (Top::has_pending_exception()) {
9726 return Failure::Exception();
9727 }
9728
9729 return result;
9730}
9731
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009732// Changes the source of the script to a new_source.
9733// If old_script_name is provided (i.e. is a String), also creates a copy of
9734// the script with its original source and sends notification to debugger.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009735static Object* Runtime_LiveEditReplaceScript(Arguments args) {
9736 ASSERT(args.length() == 3);
9737 HandleScope scope;
9738 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
9739 CONVERT_ARG_CHECKED(String, new_source, 1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009740 Handle<Object> old_script_name(args[2]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009741
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009742 CONVERT_CHECKED(Script, original_script_pointer,
9743 original_script_value->value());
9744 Handle<Script> original_script(original_script_pointer);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009745
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009746 Object* old_script = LiveEdit::ChangeScriptSource(original_script,
9747 new_source,
9748 old_script_name);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009749
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009750 if (old_script->IsScript()) {
9751 Handle<Script> script_handle(Script::cast(old_script));
9752 return *(GetScriptWrapper(script_handle));
9753 } else {
9754 return Heap::null_value();
9755 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009756}
9757
9758// Replaces code of SharedFunctionInfo with a new one.
9759static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
9760 ASSERT(args.length() == 2);
9761 HandleScope scope;
9762 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
9763 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
9764
9765 LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
9766
9767 return Heap::undefined_value();
9768}
9769
9770// Connects SharedFunctionInfo to another script.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009771static Object* Runtime_LiveEditFunctionSetScript(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009772 ASSERT(args.length() == 2);
9773 HandleScope scope;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009774 Handle<Object> function_object(args[0]);
9775 Handle<Object> script_object(args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009776
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009777 if (function_object->IsJSValue()) {
9778 Handle<JSValue> function_wrapper = Handle<JSValue>::cast(function_object);
9779 if (script_object->IsJSValue()) {
9780 CONVERT_CHECKED(Script, script, JSValue::cast(*script_object)->value());
9781 script_object = Handle<Object>(script);
9782 }
9783
9784 LiveEdit::SetFunctionScript(function_wrapper, script_object);
9785 } else {
9786 // Just ignore this. We may not have a SharedFunctionInfo for some functions
9787 // and we check it in this function.
9788 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009789
9790 return Heap::undefined_value();
9791}
9792
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009793
9794// In a code of a parent function replaces original function as embedded object
9795// with a substitution one.
9796static Object* Runtime_LiveEditReplaceRefToNestedFunction(Arguments args) {
9797 ASSERT(args.length() == 3);
9798 HandleScope scope;
9799
9800 CONVERT_ARG_CHECKED(JSValue, parent_wrapper, 0);
9801 CONVERT_ARG_CHECKED(JSValue, orig_wrapper, 1);
9802 CONVERT_ARG_CHECKED(JSValue, subst_wrapper, 2);
9803
9804 LiveEdit::ReplaceRefToNestedFunction(parent_wrapper, orig_wrapper,
9805 subst_wrapper);
9806
9807 return Heap::undefined_value();
9808}
9809
9810
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009811// Updates positions of a shared function info (first parameter) according
9812// to script source change. Text change is described in second parameter as
9813// array of groups of 3 numbers:
9814// (change_begin, change_end, change_end_new_position).
9815// Each group describes a change in text; groups are sorted by change_begin.
9816static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
9817 ASSERT(args.length() == 2);
9818 HandleScope scope;
9819 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
9820 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
9821
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009822 LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009823
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009824 return Heap::undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009825}
9826
9827
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009828// For array of SharedFunctionInfo's (each wrapped in JSValue)
9829// checks that none of them have activations on stacks (of any thread).
9830// Returns array of the same length with corresponding results of
9831// LiveEdit::FunctionPatchabilityStatus type.
ager@chromium.org357bf652010-04-12 11:30:10 +00009832static Object* Runtime_LiveEditCheckAndDropActivations(Arguments args) {
9833 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009834 HandleScope scope;
9835 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +00009836 CONVERT_BOOLEAN_CHECKED(do_drop, args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009837
ager@chromium.org357bf652010-04-12 11:30:10 +00009838 return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009839}
9840
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009841// Compares 2 strings line-by-line and returns diff in form of JSArray of
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00009842// triplets (pos1, pos1_end, pos2_end) describing list of diff chunks.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009843static Object* Runtime_LiveEditCompareStringsLinewise(Arguments args) {
9844 ASSERT(args.length() == 2);
9845 HandleScope scope;
9846 CONVERT_ARG_CHECKED(String, s1, 0);
9847 CONVERT_ARG_CHECKED(String, s2, 1);
9848
9849 return *LiveEdit::CompareStringsLinewise(s1, s2);
9850}
9851
9852
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009853
fschneider@chromium.org086aac62010-03-17 13:18:24 +00009854// A testing entry. Returns statement position which is the closest to
9855// source_position.
9856static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
9857 ASSERT(args.length() == 2);
9858 HandleScope scope;
9859 CONVERT_ARG_CHECKED(JSFunction, function, 0);
9860 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9861
9862 Handle<Code> code(function->code());
9863
9864 RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
9865 int closest_pc = 0;
9866 int distance = kMaxInt;
9867 while (!it.done()) {
9868 int statement_position = static_cast<int>(it.rinfo()->data());
9869 // Check if this break point is closer that what was previously found.
9870 if (source_position <= statement_position &&
9871 statement_position - source_position < distance) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00009872 closest_pc =
9873 static_cast<int>(it.rinfo()->pc() - code->instruction_start());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00009874 distance = statement_position - source_position;
9875 // Check whether we can't get any closer.
9876 if (distance == 0) break;
9877 }
9878 it.next();
9879 }
9880
9881 return Smi::FromInt(closest_pc);
9882}
9883
9884
ager@chromium.org357bf652010-04-12 11:30:10 +00009885// Calls specified function with or without entering the debugger.
9886// This is used in unit tests to run code as if debugger is entered or simply
9887// to have a stack with C++ frame in the middle.
9888static Object* Runtime_ExecuteInDebugContext(Arguments args) {
9889 ASSERT(args.length() == 2);
9890 HandleScope scope;
9891 CONVERT_ARG_CHECKED(JSFunction, function, 0);
9892 CONVERT_BOOLEAN_CHECKED(without_debugger, args[1]);
9893
9894 Handle<Object> result;
9895 bool pending_exception;
9896 {
9897 if (without_debugger) {
9898 result = Execution::Call(function, Top::global(), 0, NULL,
9899 &pending_exception);
9900 } else {
9901 EnterDebugger enter_debugger;
9902 result = Execution::Call(function, Top::global(), 0, NULL,
9903 &pending_exception);
9904 }
9905 }
9906 if (!pending_exception) {
9907 return *result;
9908 } else {
9909 return Failure::Exception();
9910 }
9911}
9912
9913
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009914#endif // ENABLE_DEBUGGER_SUPPORT
9915
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009916#ifdef ENABLE_LOGGING_AND_PROFILING
9917
9918static Object* Runtime_ProfilerResume(Arguments args) {
9919 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +00009920 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009921
9922 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +00009923 CONVERT_CHECKED(Smi, smi_tag, args[1]);
9924 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009925 return Heap::undefined_value();
9926}
9927
9928
9929static Object* Runtime_ProfilerPause(Arguments args) {
9930 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +00009931 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009932
9933 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +00009934 CONVERT_CHECKED(Smi, smi_tag, args[1]);
9935 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009936 return Heap::undefined_value();
9937}
9938
9939#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009940
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009941// Finds the script object from the script data. NOTE: This operation uses
9942// heap traversal to find the function generated for the source position
9943// for the requested break point. For lazily compiled functions several heap
9944// traversals might be required rendering this operation as a rather slow
9945// operation. However for setting break points which is normally done through
9946// some kind of user interaction the performance is not crucial.
9947static Handle<Object> Runtime_GetScriptFromScriptName(
9948 Handle<String> script_name) {
9949 // Scan the heap for Script objects to find the script with the requested
9950 // script data.
9951 Handle<Script> script;
9952 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009953 HeapObject* obj = NULL;
9954 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009955 // If a script is found check if it has the script data requested.
9956 if (obj->IsScript()) {
9957 if (Script::cast(obj)->name()->IsString()) {
9958 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
9959 script = Handle<Script>(Script::cast(obj));
9960 }
9961 }
9962 }
9963 }
9964
9965 // If no script with the requested script data is found return undefined.
9966 if (script.is_null()) return Factory::undefined_value();
9967
9968 // Return the script found.
9969 return GetScriptWrapper(script);
9970}
9971
9972
9973// Get the script object from script data. NOTE: Regarding performance
9974// see the NOTE for GetScriptFromScriptData.
9975// args[0]: script data for the script to find the source for
9976static Object* Runtime_GetScript(Arguments args) {
9977 HandleScope scope;
9978
9979 ASSERT(args.length() == 1);
9980
9981 CONVERT_CHECKED(String, script_name, args[0]);
9982
9983 // Find the requested script.
9984 Handle<Object> result =
9985 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
9986 return *result;
9987}
9988
9989
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009990// Determines whether the given stack frame should be displayed in
9991// a stack trace. The caller is the error constructor that asked
9992// for the stack trace to be collected. The first time a construct
9993// call to this function is encountered it is skipped. The seen_caller
9994// in/out parameter is used to remember if the caller has been seen
9995// yet.
9996static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
9997 bool* seen_caller) {
9998 // Only display JS frames.
9999 if (!raw_frame->is_java_script())
10000 return false;
10001 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
10002 Object* raw_fun = frame->function();
10003 // Not sure when this can happen but skip it just in case.
10004 if (!raw_fun->IsJSFunction())
10005 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010006 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010007 *seen_caller = true;
10008 return false;
10009 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010010 // Skip all frames until we've seen the caller. Also, skip the most
10011 // obvious builtin calls. Some builtin calls (such as Number.ADD
10012 // which is invoked using 'call') are very difficult to recognize
10013 // so we're leaving them in for now.
10014 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010015}
10016
10017
10018// Collect the raw data for a stack trace. Returns an array of three
10019// element segments each containing a receiver, function and native
10020// code offset.
10021static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010022 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010023 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010024 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
10025
10026 HandleScope scope;
10027
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000010028 limit = Max(limit, 0); // Ensure that limit is not negative.
10029 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010030 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010031
10032 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010033 // If the caller parameter is a function we skip frames until we're
10034 // under it before starting to collect.
10035 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010036 int cursor = 0;
10037 int frames_seen = 0;
10038 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010039 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010040 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010041 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010042 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010043 Object* recv = frame->receiver();
10044 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010045 Address pc = frame->pc();
10046 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010047 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010048 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010049 if (cursor + 2 < elements->length()) {
10050 elements->set(cursor++, recv);
10051 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010052 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010053 } else {
10054 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010055 Handle<Object> recv_handle(recv);
10056 Handle<Object> fun_handle(fun);
10057 SetElement(result, cursor++, recv_handle);
10058 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010059 SetElement(result, cursor++, Handle<Smi>(offset));
10060 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010061 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010062 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010063 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010064
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010065 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010066 return *result;
10067}
10068
10069
ager@chromium.org3811b432009-10-28 14:53:37 +000010070// Returns V8 version as a string.
10071static Object* Runtime_GetV8Version(Arguments args) {
10072 ASSERT_EQ(args.length(), 0);
10073
10074 NoHandleAllocation ha;
10075
10076 const char* version_string = v8::V8::GetVersion();
10077
10078 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
10079}
10080
10081
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010082static Object* Runtime_Abort(Arguments args) {
10083 ASSERT(args.length() == 2);
10084 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
10085 Smi::cast(args[1])->value());
10086 Top::PrintStack();
10087 OS::Abort();
10088 UNREACHABLE();
10089 return NULL;
10090}
10091
10092
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010093static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
10094 ASSERT(args.length() == 0);
10095 HandleScope::DeleteExtensions();
10096 return Heap::undefined_value();
10097}
10098
10099
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010100static Object* CacheMiss(FixedArray* cache_obj, int index, Object* key_obj) {
10101 ASSERT(index % 2 == 0); // index of the key
10102 ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
10103 ASSERT(index < cache_obj->length());
10104
10105 HandleScope scope;
10106
10107 Handle<FixedArray> cache(cache_obj);
10108 Handle<Object> key(key_obj);
10109 Handle<JSFunction> factory(JSFunction::cast(
10110 cache->get(JSFunctionResultCache::kFactoryIndex)));
10111 // TODO(antonm): consider passing a receiver when constructing a cache.
10112 Handle<Object> receiver(Top::global_context()->global());
10113
10114 Handle<Object> value;
10115 {
10116 // This handle is nor shared, nor used later, so it's safe.
10117 Object** argv[] = { key.location() };
10118 bool pending_exception = false;
10119 value = Execution::Call(factory,
10120 receiver,
10121 1,
10122 argv,
10123 &pending_exception);
10124 if (pending_exception) return Failure::Exception();
10125 }
10126
10127 cache->set(index, *key);
10128 cache->set(index + 1, *value);
10129 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(index));
10130
10131 return *value;
10132}
10133
10134
10135static Object* Runtime_GetFromCache(Arguments args) {
10136 // This is only called from codegen, so checks might be more lax.
10137 CONVERT_CHECKED(FixedArray, cache, args[0]);
10138 Object* key = args[1];
10139
10140 const int finger_index =
10141 Smi::cast(cache->get(JSFunctionResultCache::kFingerIndex))->value();
10142
10143 Object* o = cache->get(finger_index);
10144 if (o == key) {
10145 // The fastest case: hit the same place again.
10146 return cache->get(finger_index + 1);
10147 }
10148
10149 for (int i = finger_index - 2;
10150 i >= JSFunctionResultCache::kEntriesIndex;
10151 i -= 2) {
10152 o = cache->get(i);
10153 if (o == key) {
10154 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10155 return cache->get(i + 1);
10156 }
10157 }
10158
10159 const int size =
10160 Smi::cast(cache->get(JSFunctionResultCache::kCacheSizeIndex))->value();
10161 ASSERT(size <= cache->length());
10162
10163 for (int i = size - 2; i > finger_index; i -= 2) {
10164 o = cache->get(i);
10165 if (o == key) {
10166 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10167 return cache->get(i + 1);
10168 }
10169 }
10170
10171 // Cache miss. If we have spare room, put new data into it, otherwise
10172 // evict post finger entry which must be least recently used.
10173 if (size < cache->length()) {
10174 cache->set(JSFunctionResultCache::kCacheSizeIndex, Smi::FromInt(size + 2));
10175 return CacheMiss(cache, size, key);
10176 } else {
antonm@chromium.org397e23c2010-04-21 12:00:05 +000010177 int target_index = finger_index + JSFunctionResultCache::kEntrySize;
10178 if (target_index == cache->length()) {
10179 target_index = JSFunctionResultCache::kEntriesIndex;
10180 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010181 return CacheMiss(cache, target_index, key);
10182 }
10183}
10184
kasper.lund44510672008-07-25 07:37:58 +000010185#ifdef DEBUG
10186// ListNatives is ONLY used by the fuzz-natives.js in debug mode
10187// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010188static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +000010189 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010190 HandleScope scope;
10191 Handle<JSArray> result = Factory::NewJSArray(0);
10192 int index = 0;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010193 bool inline_runtime_functions = false;
ager@chromium.orga1645e22009-09-09 19:27:10 +000010194#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010195 { \
10196 HandleScope inner; \
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010197 Handle<String> name; \
10198 /* Inline runtime functions have an underscore in front of the name. */ \
10199 if (inline_runtime_functions) { \
10200 name = Factory::NewStringFromAscii( \
10201 Vector<const char>("_" #Name, StrLength("_" #Name))); \
10202 } else { \
10203 name = Factory::NewStringFromAscii( \
10204 Vector<const char>(#Name, StrLength(#Name))); \
10205 } \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010206 Handle<JSArray> pair = Factory::NewJSArray(0); \
10207 SetElement(pair, 0, name); \
10208 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
10209 SetElement(result, index++, pair); \
10210 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010211 inline_runtime_functions = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010212 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010213 inline_runtime_functions = true;
10214 INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010215#undef ADD_ENTRY
10216 return *result;
10217}
kasper.lund44510672008-07-25 07:37:58 +000010218#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010219
10220
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010221static Object* Runtime_Log(Arguments args) {
10222 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +000010223 CONVERT_CHECKED(String, format, args[0]);
10224 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010225 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010226 Logger::LogRuntime(chars, elms);
10227 return Heap::undefined_value();
10228}
10229
10230
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010231static Object* Runtime_IS_VAR(Arguments args) {
10232 UNREACHABLE(); // implemented as macro in the parser
10233 return NULL;
10234}
10235
10236
10237// ----------------------------------------------------------------------------
10238// Implementation of Runtime
10239
ager@chromium.orga1645e22009-09-09 19:27:10 +000010240#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010241 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +000010242 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010243
10244static Runtime::Function Runtime_functions[] = {
10245 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010246 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010247};
10248
10249#undef F
10250
10251
10252Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
10253 ASSERT(0 <= fid && fid < kNofFunctions);
10254 return &Runtime_functions[fid];
10255}
10256
10257
10258Runtime::Function* Runtime::FunctionForName(const char* name) {
10259 for (Function* f = Runtime_functions; f->name != NULL; f++) {
10260 if (strcmp(f->name, name) == 0) {
10261 return f;
10262 }
10263 }
10264 return NULL;
10265}
10266
10267
10268void Runtime::PerformGC(Object* result) {
10269 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010270 if (failure->IsRetryAfterGC()) {
10271 // Try to do a garbage collection; ignore it if it fails. The C
10272 // entry stub will throw an out-of-memory exception in that case.
10273 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
10274 } else {
10275 // Handle last resort GC and make sure to allow future allocations
10276 // to grow the heap without causing GCs (if possible).
10277 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +000010278 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010279 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010280}
10281
10282
10283} } // namespace v8::internal