blob: 88786e82473eada4915a7260b4e2f633d0dda729 [file] [log] [blame]
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001// Copyright 2006-2009 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include <stdlib.h>
29
30#include "v8.h"
31
32#include "accessors.h"
33#include "api.h"
34#include "arguments.h"
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000035#include "codegen.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000036#include "compiler.h"
37#include "cpu.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000038#include "dateparser-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000039#include "debug.h"
40#include "execution.h"
41#include "jsregexp.h"
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000042#include "liveedit.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000043#include "parser.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000044#include "platform.h"
45#include "runtime.h"
46#include "scopeinfo.h"
ager@chromium.org7c537e22008-10-16 08:43:32 +000047#include "smart-pointer.h"
ager@chromium.org18ad94b2009-09-02 08:22:29 +000048#include "stub-cache.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000049#include "v8threads.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000050
kasperl@chromium.org71affb52009-05-26 05:44:31 +000051namespace v8 {
52namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000053
54
ager@chromium.org3e875802009-06-29 08:26:34 +000055#define RUNTIME_ASSERT(value) \
56 if (!(value)) return Top::ThrowIllegalOperation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000057
58// Cast the given object to a value of the specified type and store
59// it in a variable with the given name. If the object is not of the
60// expected type call IllegalOperation and return.
61#define CONVERT_CHECKED(Type, name, obj) \
62 RUNTIME_ASSERT(obj->Is##Type()); \
63 Type* name = Type::cast(obj);
64
65#define CONVERT_ARG_CHECKED(Type, name, index) \
66 RUNTIME_ASSERT(args[index]->Is##Type()); \
67 Handle<Type> name = args.at<Type>(index);
68
kasper.lundbd3ec4e2008-07-09 11:06:54 +000069// Cast the given object to a boolean and store it in a variable with
70// the given name. If the object is not a boolean call IllegalOperation
71// and return.
72#define CONVERT_BOOLEAN_CHECKED(name, obj) \
73 RUNTIME_ASSERT(obj->IsBoolean()); \
74 bool name = (obj)->IsTrue();
75
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000076// Cast the given object to a Smi and store its value in an int variable
77// with the given name. If the object is not a Smi call IllegalOperation
78// and return.
79#define CONVERT_SMI_CHECKED(name, obj) \
80 RUNTIME_ASSERT(obj->IsSmi()); \
81 int name = Smi::cast(obj)->value();
82
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000083// Cast the given object to a double and store it in a variable with
84// the given name. If the object is not a number (as opposed to
85// the number not-a-number) call IllegalOperation and return.
86#define CONVERT_DOUBLE_CHECKED(name, obj) \
87 RUNTIME_ASSERT(obj->IsNumber()); \
88 double name = (obj)->Number();
89
90// Call the specified converter on the object *comand store the result in
91// a variable of the specified type with the given name. If the
92// object is not a Number call IllegalOperation and return.
93#define CONVERT_NUMBER_CHECKED(type, name, Type, obj) \
94 RUNTIME_ASSERT(obj->IsNumber()); \
95 type name = NumberTo##Type(obj);
96
97// Non-reentrant string buffer for efficient general use in this file.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000098static StaticResource<StringInputBuffer> runtime_string_input_buffer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000099
100
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000101static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
102 StackLimitCheck check;
103 if (check.HasOverflowed()) return Top::StackOverflow();
104
105 Object* result = Heap::CopyJSObject(boilerplate);
106 if (result->IsFailure()) return result;
107 JSObject* copy = JSObject::cast(result);
108
109 // Deep copy local properties.
110 if (copy->HasFastProperties()) {
111 FixedArray* properties = copy->properties();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000112 for (int i = 0; i < properties->length(); i++) {
113 Object* value = properties->get(i);
114 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000115 JSObject* js_object = JSObject::cast(value);
116 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000117 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000118 properties->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000119 }
120 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000121 int nof = copy->map()->inobject_properties();
122 for (int i = 0; i < nof; i++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000123 Object* value = copy->InObjectPropertyAt(i);
124 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000125 JSObject* js_object = JSObject::cast(value);
126 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000127 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000128 copy->InObjectPropertyAtPut(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000129 }
130 }
131 } else {
132 result = Heap::AllocateFixedArray(copy->NumberOfLocalProperties(NONE));
133 if (result->IsFailure()) return result;
134 FixedArray* names = FixedArray::cast(result);
135 copy->GetLocalPropertyNames(names, 0);
136 for (int i = 0; i < names->length(); i++) {
137 ASSERT(names->get(i)->IsString());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000138 String* key_string = String::cast(names->get(i));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000139 PropertyAttributes attributes =
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000140 copy->GetLocalPropertyAttribute(key_string);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000141 // Only deep copy fields from the object literal expression.
142 // In particular, don't try to copy the length attribute of
143 // an array.
144 if (attributes != NONE) continue;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000145 Object* value = copy->GetProperty(key_string, &attributes);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000146 ASSERT(!value->IsFailure());
147 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000148 JSObject* js_object = JSObject::cast(value);
149 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000150 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000151 result = copy->SetProperty(key_string, result, NONE);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000152 if (result->IsFailure()) return result;
153 }
154 }
155 }
156
157 // Deep copy local elements.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000158 // Pixel elements cannot be created using an object literal.
ager@chromium.org3811b432009-10-28 14:53:37 +0000159 ASSERT(!copy->HasPixelElements() && !copy->HasExternalArrayElements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000160 switch (copy->GetElementsKind()) {
161 case JSObject::FAST_ELEMENTS: {
162 FixedArray* elements = FixedArray::cast(copy->elements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000163 for (int i = 0; i < elements->length(); i++) {
164 Object* value = elements->get(i);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000165 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000166 JSObject* js_object = JSObject::cast(value);
167 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000168 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000169 elements->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000170 }
171 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000172 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000173 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000174 case JSObject::DICTIONARY_ELEMENTS: {
175 NumberDictionary* element_dictionary = copy->element_dictionary();
176 int capacity = element_dictionary->Capacity();
177 for (int i = 0; i < capacity; i++) {
178 Object* k = element_dictionary->KeyAt(i);
179 if (element_dictionary->IsKey(k)) {
180 Object* value = element_dictionary->ValueAt(i);
181 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000182 JSObject* js_object = JSObject::cast(value);
183 result = DeepCopyBoilerplate(js_object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000184 if (result->IsFailure()) return result;
185 element_dictionary->ValueAtPut(i, result);
186 }
187 }
188 }
189 break;
190 }
191 default:
192 UNREACHABLE();
193 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000194 }
195 return copy;
196}
197
198
199static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
200 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
201 return DeepCopyBoilerplate(boilerplate);
202}
203
204
205static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000206 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000207 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000208}
209
210
ager@chromium.org236ad962008-09-25 09:45:57 +0000211static Handle<Map> ComputeObjectLiteralMap(
212 Handle<Context> context,
213 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000214 bool* is_result_from_cache) {
ager@chromium.org32912102009-01-16 10:38:43 +0000215 int number_of_properties = constant_properties->length() / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000216 if (FLAG_canonicalize_object_literal_maps) {
217 // First find prefix of consecutive symbol keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000218 int number_of_symbol_keys = 0;
219 while ((number_of_symbol_keys < number_of_properties) &&
220 (constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
221 number_of_symbol_keys++;
222 }
223 // Based on the number of prefix symbols key we decide whether
224 // to use the map cache in the global context.
225 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000226 if ((number_of_symbol_keys == number_of_properties) &&
227 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000228 // Create the fixed array with the key.
229 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
230 for (int i = 0; i < number_of_symbol_keys; i++) {
231 keys->set(i, constant_properties->get(i*2));
232 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000233 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000234 return Factory::ObjectLiteralMapFromCache(context, keys);
235 }
236 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000237 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000238 return Factory::CopyMap(
239 Handle<Map>(context->object_function()->initial_map()),
240 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000241}
242
243
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000244static Handle<Object> CreateLiteralBoilerplate(
245 Handle<FixedArray> literals,
246 Handle<FixedArray> constant_properties);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000247
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000248
249static Handle<Object> CreateObjectLiteralBoilerplate(
250 Handle<FixedArray> literals,
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000251 Handle<FixedArray> constant_properties,
252 bool should_have_fast_elements) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000253 // Get the global context from the literals array. This is the
254 // context in which the function was created and we use the object
255 // function from this context to create the object literal. We do
256 // not use the object function from the current global context
257 // because this might be the object function from another context
258 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000259 Handle<Context> context =
260 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
261
262 bool is_result_from_cache;
263 Handle<Map> map = ComputeObjectLiteralMap(context,
264 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000265 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000266
ager@chromium.org236ad962008-09-25 09:45:57 +0000267 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000268
269 // Normalize the elements of the boilerplate to save space if needed.
270 if (!should_have_fast_elements) NormalizeElements(boilerplate);
271
ager@chromium.org32912102009-01-16 10:38:43 +0000272 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000273 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000274 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000275 length / 2,
ager@chromium.org236ad962008-09-25 09:45:57 +0000276 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000277 for (int index = 0; index < length; index +=2) {
278 Handle<Object> key(constant_properties->get(index+0));
279 Handle<Object> value(constant_properties->get(index+1));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000280 if (value->IsFixedArray()) {
281 // The value contains the constant_properties of a
282 // simple object literal.
283 Handle<FixedArray> array = Handle<FixedArray>::cast(value);
284 value = CreateLiteralBoilerplate(literals, array);
285 if (value.is_null()) return value;
286 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000287 Handle<Object> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000288 uint32_t element_index = 0;
289 if (key->IsSymbol()) {
290 // If key is a symbol it is not an array element.
291 Handle<String> name(String::cast(*key));
292 ASSERT(!name->AsArrayIndex(&element_index));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000293 result = SetProperty(boilerplate, name, value, NONE);
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000294 } else if (key->ToArrayIndex(&element_index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000295 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000296 result = SetElement(boilerplate, element_index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000297 } else {
298 // Non-uint32 number.
299 ASSERT(key->IsNumber());
300 double num = key->Number();
301 char arr[100];
302 Vector<char> buffer(arr, ARRAY_SIZE(arr));
303 const char* str = DoubleToCString(num, buffer);
304 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000305 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000306 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000307 // If setting the property on the boilerplate throws an
308 // exception, the exception is converted to an empty handle in
309 // the handle based operations. In that case, we need to
310 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000311 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000312 }
313 }
314
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000315 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000316}
317
318
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000319static Handle<Object> CreateArrayLiteralBoilerplate(
320 Handle<FixedArray> literals,
321 Handle<FixedArray> elements) {
322 // Create the JSArray.
323 Handle<JSFunction> constructor(
324 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
325 Handle<Object> object = Factory::NewJSObject(constructor);
326
327 Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
328
329 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
330 for (int i = 0; i < content->length(); i++) {
331 if (content->get(i)->IsFixedArray()) {
332 // The value contains the constant_properties of a
333 // simple object literal.
334 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
335 Handle<Object> result =
336 CreateLiteralBoilerplate(literals, fa);
337 if (result.is_null()) return result;
338 content->set(i, *result);
339 }
340 }
341
342 // Set the elements.
343 Handle<JSArray>::cast(object)->SetContent(*content);
344 return object;
345}
346
347
348static Handle<Object> CreateLiteralBoilerplate(
349 Handle<FixedArray> literals,
350 Handle<FixedArray> array) {
351 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
352 switch (CompileTimeValue::GetType(array)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000353 case CompileTimeValue::OBJECT_LITERAL_FAST_ELEMENTS:
354 return CreateObjectLiteralBoilerplate(literals, elements, true);
355 case CompileTimeValue::OBJECT_LITERAL_SLOW_ELEMENTS:
356 return CreateObjectLiteralBoilerplate(literals, elements, false);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000357 case CompileTimeValue::ARRAY_LITERAL:
358 return CreateArrayLiteralBoilerplate(literals, elements);
359 default:
360 UNREACHABLE();
361 return Handle<Object>::null();
362 }
363}
364
365
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000366static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000367 // Takes a FixedArray of elements containing the literal elements of
368 // the array literal and produces JSArray with those elements.
369 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000370 // which contains the context from which to get the Array function
371 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000372 HandleScope scope;
373 ASSERT(args.length() == 3);
374 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
375 CONVERT_SMI_CHECKED(literals_index, args[1]);
376 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000377
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000378 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
379 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000380
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000381 // Update the functions literal and return the boilerplate.
382 literals->set(literals_index, *object);
383 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000384}
385
386
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000387static Object* Runtime_CreateObjectLiteral(Arguments args) {
388 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000389 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000390 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
391 CONVERT_SMI_CHECKED(literals_index, args[1]);
392 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000393 CONVERT_SMI_CHECKED(fast_elements, args[3]);
394 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000395
396 // Check if boilerplate exists. If not, create it first.
397 Handle<Object> boilerplate(literals->get(literals_index));
398 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000399 boilerplate = CreateObjectLiteralBoilerplate(literals,
400 constant_properties,
401 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000402 if (boilerplate.is_null()) return Failure::Exception();
403 // Update the functions literal and return the boilerplate.
404 literals->set(literals_index, *boilerplate);
405 }
406 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
407}
408
409
410static Object* Runtime_CreateObjectLiteralShallow(Arguments args) {
411 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000412 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000413 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
414 CONVERT_SMI_CHECKED(literals_index, args[1]);
415 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000416 CONVERT_SMI_CHECKED(fast_elements, args[3]);
417 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000418
419 // Check if boilerplate exists. If not, create it first.
420 Handle<Object> boilerplate(literals->get(literals_index));
421 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000422 boilerplate = CreateObjectLiteralBoilerplate(literals,
423 constant_properties,
424 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000425 if (boilerplate.is_null()) return Failure::Exception();
426 // Update the functions literal and return the boilerplate.
427 literals->set(literals_index, *boilerplate);
428 }
429 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
430}
431
432
433static Object* Runtime_CreateArrayLiteral(Arguments args) {
434 HandleScope scope;
435 ASSERT(args.length() == 3);
436 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
437 CONVERT_SMI_CHECKED(literals_index, args[1]);
438 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
439
440 // Check if boilerplate exists. If not, create it first.
441 Handle<Object> boilerplate(literals->get(literals_index));
442 if (*boilerplate == Heap::undefined_value()) {
443 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
444 if (boilerplate.is_null()) return Failure::Exception();
445 // Update the functions literal and return the boilerplate.
446 literals->set(literals_index, *boilerplate);
447 }
448 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
449}
450
451
452static Object* Runtime_CreateArrayLiteralShallow(Arguments args) {
453 HandleScope scope;
454 ASSERT(args.length() == 3);
455 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
456 CONVERT_SMI_CHECKED(literals_index, args[1]);
457 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
458
459 // Check if boilerplate exists. If not, create it first.
460 Handle<Object> boilerplate(literals->get(literals_index));
461 if (*boilerplate == Heap::undefined_value()) {
462 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
463 if (boilerplate.is_null()) return Failure::Exception();
464 // Update the functions literal and return the boilerplate.
465 literals->set(literals_index, *boilerplate);
466 }
467 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
468}
469
470
ager@chromium.org32912102009-01-16 10:38:43 +0000471static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
472 ASSERT(args.length() == 2);
473 CONVERT_CHECKED(String, key, args[0]);
474 Object* value = args[1];
475 // Create a catch context extension object.
476 JSFunction* constructor =
477 Top::context()->global_context()->context_extension_function();
478 Object* object = Heap::AllocateJSObject(constructor);
479 if (object->IsFailure()) return object;
480 // Assign the exception value to the catch variable and make sure
481 // that the catch variable is DontDelete.
482 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
483 if (value->IsFailure()) return value;
484 return object;
485}
486
487
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000488static Object* Runtime_ClassOf(Arguments args) {
489 NoHandleAllocation ha;
490 ASSERT(args.length() == 1);
491 Object* obj = args[0];
492 if (!obj->IsJSObject()) return Heap::null_value();
493 return JSObject::cast(obj)->class_name();
494}
495
ager@chromium.org7c537e22008-10-16 08:43:32 +0000496
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000497static Object* Runtime_IsInPrototypeChain(Arguments args) {
498 NoHandleAllocation ha;
499 ASSERT(args.length() == 2);
500 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
501 Object* O = args[0];
502 Object* V = args[1];
503 while (true) {
504 Object* prototype = V->GetPrototype();
505 if (prototype->IsNull()) return Heap::false_value();
506 if (O == prototype) return Heap::true_value();
507 V = prototype;
508 }
509}
510
511
ager@chromium.org9085a012009-05-11 19:22:57 +0000512// Inserts an object as the hidden prototype of another object.
513static Object* Runtime_SetHiddenPrototype(Arguments args) {
514 NoHandleAllocation ha;
515 ASSERT(args.length() == 2);
516 CONVERT_CHECKED(JSObject, jsobject, args[0]);
517 CONVERT_CHECKED(JSObject, proto, args[1]);
518
519 // Sanity checks. The old prototype (that we are replacing) could
520 // theoretically be null, but if it is not null then check that we
521 // didn't already install a hidden prototype here.
522 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
523 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
524 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
525
526 // Allocate up front before we start altering state in case we get a GC.
527 Object* map_or_failure = proto->map()->CopyDropTransitions();
528 if (map_or_failure->IsFailure()) return map_or_failure;
529 Map* new_proto_map = Map::cast(map_or_failure);
530
531 map_or_failure = jsobject->map()->CopyDropTransitions();
532 if (map_or_failure->IsFailure()) return map_or_failure;
533 Map* new_map = Map::cast(map_or_failure);
534
535 // Set proto's prototype to be the old prototype of the object.
536 new_proto_map->set_prototype(jsobject->GetPrototype());
537 proto->set_map(new_proto_map);
538 new_proto_map->set_is_hidden_prototype();
539
540 // Set the object's prototype to proto.
541 new_map->set_prototype(proto);
542 jsobject->set_map(new_map);
543
544 return Heap::undefined_value();
545}
546
547
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000548static Object* Runtime_IsConstructCall(Arguments args) {
549 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000550 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000551 JavaScriptFrameIterator it;
552 return Heap::ToBoolean(it.frame()->IsConstructor());
553}
554
555
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000556// Recursively traverses hidden prototypes if property is not found
557static void GetOwnPropertyImplementation(JSObject* obj,
558 String* name,
559 LookupResult* result) {
560 obj->LocalLookupRealNamedProperty(name, result);
561
562 if (!result->IsProperty()) {
563 Object* proto = obj->GetPrototype();
564 if (proto->IsJSObject() &&
565 JSObject::cast(proto)->map()->is_hidden_prototype())
566 GetOwnPropertyImplementation(JSObject::cast(proto),
567 name, result);
568 }
569}
570
571
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000572// Enumerator used as indices into the array returned from GetOwnProperty
573enum PropertyDescriptorIndices {
574 IS_ACCESSOR_INDEX,
575 VALUE_INDEX,
576 GETTER_INDEX,
577 SETTER_INDEX,
578 WRITABLE_INDEX,
579 ENUMERABLE_INDEX,
580 CONFIGURABLE_INDEX,
581 DESCRIPTOR_SIZE
582};
583
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000584// Returns an array with the property description:
585// if args[1] is not a property on args[0]
586// returns undefined
587// if args[1] is a data property on args[0]
588// [false, value, Writeable, Enumerable, Configurable]
589// if args[1] is an accessor on args[0]
590// [true, GetFunction, SetFunction, Enumerable, Configurable]
591static Object* Runtime_GetOwnProperty(Arguments args) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000592 ASSERT(args.length() == 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000593 HandleScope scope;
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000594 Handle<FixedArray> elms = Factory::NewFixedArray(DESCRIPTOR_SIZE);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000595 Handle<JSArray> desc = Factory::NewJSArrayWithElements(elms);
596 LookupResult result;
597 CONVERT_CHECKED(JSObject, obj, args[0]);
598 CONVERT_CHECKED(String, name, args[1]);
599
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000600 // This could be an element.
601 uint32_t index;
602 if (name->AsArrayIndex(&index)) {
603 if (!obj->HasLocalElement(index)) {
604 return Heap::undefined_value();
605 }
606
607 // Special handling of string objects according to ECMAScript 5 15.5.5.2.
608 // Note that this might be a string object with elements other than the
609 // actual string value. This is covered by the subsequent cases.
610 if (obj->IsStringObjectWithCharacterAt(index)) {
611 JSValue* js_value = JSValue::cast(obj);
612 String* str = String::cast(js_value->value());
613 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
614 elms->set(VALUE_INDEX, str->SubString(index, index+1));
615 elms->set(WRITABLE_INDEX, Heap::false_value());
616 elms->set(ENUMERABLE_INDEX, Heap::false_value());
617 elms->set(CONFIGURABLE_INDEX, Heap::false_value());
618 return *desc;
619 }
620
621 // This can potentially be an element in the elements dictionary or
622 // a fast element.
623 if (obj->HasDictionaryElements()) {
624 NumberDictionary* dictionary = obj->element_dictionary();
625 int entry = dictionary->FindEntry(index);
626 PropertyDetails details = dictionary->DetailsAt(entry);
627 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
628 elms->set(VALUE_INDEX, dictionary->ValueAt(entry));
629 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!details.IsDontDelete()));
630 elms->set(ENUMERABLE_INDEX, Heap::ToBoolean(!details.IsDontEnum()));
631 elms->set(CONFIGURABLE_INDEX, Heap::ToBoolean(!details.IsReadOnly()));
632 return *desc;
633 } else {
634 // Elements that are stored as array elements always has:
635 // writable: true, configurable: true, enumerable: true.
636 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
637 elms->set(VALUE_INDEX, obj->GetElement(index));
638 elms->set(WRITABLE_INDEX, Heap::true_value());
639 elms->set(ENUMERABLE_INDEX, Heap::true_value());
640 elms->set(CONFIGURABLE_INDEX, Heap::true_value());
641 return *desc;
642 }
643 }
644
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000645 // Use recursive implementation to also traverse hidden prototypes
646 GetOwnPropertyImplementation(obj, name, &result);
647
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000648 if (!result.IsProperty()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000649 return Heap::undefined_value();
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000650 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000651 if (result.type() == CALLBACKS) {
652 Object* structure = result.GetCallbackObject();
ager@chromium.org5c838252010-02-19 08:53:10 +0000653 if (structure->IsProxy() || structure->IsAccessorInfo()) {
654 // Property that is internally implemented as a callback or
655 // an API defined callback.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000656 Object* value = obj->GetPropertyWithCallback(
657 obj, structure, name, result.holder());
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000658 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
659 elms->set(VALUE_INDEX, value);
660 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!result.IsReadOnly()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000661 } else if (structure->IsFixedArray()) {
662 // __defineGetter__/__defineSetter__ callback.
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000663 elms->set(IS_ACCESSOR_INDEX, Heap::true_value());
664 elms->set(GETTER_INDEX, FixedArray::cast(structure)->get(0));
665 elms->set(SETTER_INDEX, FixedArray::cast(structure)->get(1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000666 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000667 return Heap::undefined_value();
668 }
669 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000670 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
671 elms->set(VALUE_INDEX, result.GetLazyValue());
672 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!result.IsReadOnly()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000673 }
674
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000675 elms->set(ENUMERABLE_INDEX, Heap::ToBoolean(!result.IsDontEnum()));
676 elms->set(CONFIGURABLE_INDEX, Heap::ToBoolean(!result.IsDontDelete()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000677 return *desc;
678}
679
680
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000681static Object* Runtime_IsExtensible(Arguments args) {
682 ASSERT(args.length() == 1);
683 CONVERT_CHECKED(JSObject, obj, args[0]);
684 return obj->map()->is_extensible() ? Heap::true_value()
685 : Heap::false_value();
686}
687
688
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000689static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000690 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000691 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000692 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
693 CONVERT_ARG_CHECKED(String, pattern, 1);
694 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000695 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
696 if (result.is_null()) return Failure::Exception();
697 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000698}
699
700
701static Object* Runtime_CreateApiFunction(Arguments args) {
702 HandleScope scope;
703 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000704 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000705 return *Factory::CreateApiFunction(data);
706}
707
708
709static Object* Runtime_IsTemplate(Arguments args) {
710 ASSERT(args.length() == 1);
711 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000712 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000713 return Heap::ToBoolean(result);
714}
715
716
717static Object* Runtime_GetTemplateField(Arguments args) {
718 ASSERT(args.length() == 2);
719 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000720 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000721 int index = field->value();
722 int offset = index * kPointerSize + HeapObject::kHeaderSize;
723 InstanceType type = templ->map()->instance_type();
724 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
725 type == OBJECT_TEMPLATE_INFO_TYPE);
726 RUNTIME_ASSERT(offset > 0);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000727 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000728 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
729 } else {
730 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
731 }
732 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000733}
734
735
ager@chromium.org870a0b62008-11-04 11:43:05 +0000736static Object* Runtime_DisableAccessChecks(Arguments args) {
737 ASSERT(args.length() == 1);
738 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000739 Map* old_map = object->map();
740 bool needs_access_checks = old_map->is_access_check_needed();
741 if (needs_access_checks) {
742 // Copy map so it won't interfere constructor's initial map.
743 Object* new_map = old_map->CopyDropTransitions();
744 if (new_map->IsFailure()) return new_map;
745
746 Map::cast(new_map)->set_is_access_check_needed(false);
747 object->set_map(Map::cast(new_map));
748 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000749 return needs_access_checks ? Heap::true_value() : Heap::false_value();
750}
751
752
753static Object* Runtime_EnableAccessChecks(Arguments args) {
754 ASSERT(args.length() == 1);
755 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000756 Map* old_map = object->map();
757 if (!old_map->is_access_check_needed()) {
758 // Copy map so it won't interfere constructor's initial map.
759 Object* new_map = old_map->CopyDropTransitions();
760 if (new_map->IsFailure()) return new_map;
761
762 Map::cast(new_map)->set_is_access_check_needed(true);
763 object->set_map(Map::cast(new_map));
764 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000765 return Heap::undefined_value();
766}
767
768
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000769static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
770 HandleScope scope;
771 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
772 Handle<Object> args[2] = { type_handle, name };
773 Handle<Object> error =
774 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
775 return Top::Throw(*error);
776}
777
778
779static Object* Runtime_DeclareGlobals(Arguments args) {
780 HandleScope scope;
781 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
782
ager@chromium.org3811b432009-10-28 14:53:37 +0000783 Handle<Context> context = args.at<Context>(0);
784 CONVERT_ARG_CHECKED(FixedArray, pairs, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000785 bool is_eval = Smi::cast(args[2])->value() == 1;
786
787 // Compute the property attributes. According to ECMA-262, section
788 // 13, page 71, the property must be read-only and
789 // non-deletable. However, neither SpiderMonkey nor KJS creates the
790 // property as read-only, so we don't either.
791 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
792
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000793 // Traverse the name/value pairs and set the properties.
794 int length = pairs->length();
795 for (int i = 0; i < length; i += 2) {
796 HandleScope scope;
797 Handle<String> name(String::cast(pairs->get(i)));
798 Handle<Object> value(pairs->get(i + 1));
799
800 // We have to declare a global const property. To capture we only
801 // assign to it when evaluating the assignment for "const x =
802 // <expr>" the initial value is the hole.
803 bool is_const_property = value->IsTheHole();
804
805 if (value->IsUndefined() || is_const_property) {
806 // Lookup the property in the global object, and don't set the
807 // value of the variable if the property is already there.
808 LookupResult lookup;
809 global->Lookup(*name, &lookup);
810 if (lookup.IsProperty()) {
811 // Determine if the property is local by comparing the holder
812 // against the global object. The information will be used to
813 // avoid throwing re-declaration errors when declaring
814 // variables or constants that exist in the prototype chain.
815 bool is_local = (*global == lookup.holder());
816 // Get the property attributes and determine if the property is
817 // read-only.
818 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
819 bool is_read_only = (attributes & READ_ONLY) != 0;
820 if (lookup.type() == INTERCEPTOR) {
821 // If the interceptor says the property is there, we
822 // just return undefined without overwriting the property.
823 // Otherwise, we continue to setting the property.
824 if (attributes != ABSENT) {
825 // Check if the existing property conflicts with regards to const.
826 if (is_local && (is_read_only || is_const_property)) {
827 const char* type = (is_read_only) ? "const" : "var";
828 return ThrowRedeclarationError(type, name);
829 };
830 // The property already exists without conflicting: Go to
831 // the next declaration.
832 continue;
833 }
834 // Fall-through and introduce the absent property by using
835 // SetProperty.
836 } else {
837 if (is_local && (is_read_only || is_const_property)) {
838 const char* type = (is_read_only) ? "const" : "var";
839 return ThrowRedeclarationError(type, name);
840 }
841 // The property already exists without conflicting: Go to
842 // the next declaration.
843 continue;
844 }
845 }
846 } else {
847 // Copy the function and update its context. Use it as value.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000848 Handle<SharedFunctionInfo> shared =
849 Handle<SharedFunctionInfo>::cast(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000850 Handle<JSFunction> function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000851 Factory::NewFunctionFromSharedFunctionInfo(shared, context, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000852 value = function;
853 }
854
855 LookupResult lookup;
856 global->LocalLookup(*name, &lookup);
857
858 PropertyAttributes attributes = is_const_property
859 ? static_cast<PropertyAttributes>(base | READ_ONLY)
860 : base;
861
862 if (lookup.IsProperty()) {
863 // There's a local property that we need to overwrite because
864 // we're either declaring a function or there's an interceptor
865 // that claims the property is absent.
866
867 // Check for conflicting re-declarations. We cannot have
868 // conflicting types in case of intercepted properties because
869 // they are absent.
870 if (lookup.type() != INTERCEPTOR &&
871 (lookup.IsReadOnly() || is_const_property)) {
872 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
873 return ThrowRedeclarationError(type, name);
874 }
875 SetProperty(global, name, value, attributes);
876 } else {
877 // If a property with this name does not already exist on the
878 // global object add the property locally. We take special
879 // precautions to always add it as a local property even in case
880 // of callbacks in the prototype chain (this rules out using
881 // SetProperty). Also, we must use the handle-based version to
882 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000883 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000884 }
885 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000886
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000887 return Heap::undefined_value();
888}
889
890
891static Object* Runtime_DeclareContextSlot(Arguments args) {
892 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000893 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000894
ager@chromium.org7c537e22008-10-16 08:43:32 +0000895 CONVERT_ARG_CHECKED(Context, context, 0);
896 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000897 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000898 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000899 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000900 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000901
902 // Declarations are always done in the function context.
903 context = Handle<Context>(context->fcontext());
904
905 int index;
906 PropertyAttributes attributes;
907 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000908 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000909 context->Lookup(name, flags, &index, &attributes);
910
911 if (attributes != ABSENT) {
912 // The name was declared before; check for conflicting
913 // re-declarations: This is similar to the code in parser.cc in
914 // the AstBuildingParser::Declare function.
915 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
916 // Functions are not read-only.
917 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
918 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
919 return ThrowRedeclarationError(type, name);
920 }
921
922 // Initialize it if necessary.
923 if (*initial_value != NULL) {
924 if (index >= 0) {
925 // The variable or constant context slot should always be in
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000926 // the function context or the arguments object.
927 if (holder->IsContext()) {
928 ASSERT(holder.is_identical_to(context));
929 if (((attributes & READ_ONLY) == 0) ||
930 context->get(index)->IsTheHole()) {
931 context->set(index, *initial_value);
932 }
933 } else {
934 Handle<JSObject>::cast(holder)->SetElement(index, *initial_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000935 }
936 } else {
937 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000938 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000939 SetProperty(context_ext, name, initial_value, mode);
940 }
941 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000942
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000943 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000944 // The property is not in the function context. It needs to be
945 // "declared" in the function context's extension context, or in the
946 // global context.
947 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000948 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000949 // The function context's extension context exists - use it.
950 context_ext = Handle<JSObject>(context->extension());
951 } else {
952 // The function context's extension context does not exists - allocate
953 // it.
954 context_ext = Factory::NewJSObject(Top::context_extension_function());
955 // And store it in the extension slot.
956 context->set_extension(*context_ext);
957 }
958 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000959
ager@chromium.org7c537e22008-10-16 08:43:32 +0000960 // Declare the property by setting it to the initial value if provided,
961 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
962 // constant declarations).
963 ASSERT(!context_ext->HasLocalProperty(*name));
964 Handle<Object> value(Heap::undefined_value());
965 if (*initial_value != NULL) value = initial_value;
966 SetProperty(context_ext, name, value, mode);
967 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
968 }
969
970 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000971}
972
973
974static Object* Runtime_InitializeVarGlobal(Arguments args) {
975 NoHandleAllocation nha;
976
977 // Determine if we need to assign to the variable if it already
978 // exists (based on the number of arguments).
979 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
980 bool assign = args.length() == 2;
981
982 CONVERT_ARG_CHECKED(String, name, 0);
983 GlobalObject* global = Top::context()->global();
984
985 // According to ECMA-262, section 12.2, page 62, the property must
986 // not be deletable.
987 PropertyAttributes attributes = DONT_DELETE;
988
989 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000990 // there, there is a property with this name in the prototype chain.
991 // We follow Safari and Firefox behavior and only set the property
992 // locally if there is an explicit initialization value that we have
993 // to assign to the property. When adding the property we take
994 // special precautions to always add it as a local property even in
995 // case of callbacks in the prototype chain (this rules out using
996 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
997 // this.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000998 // Note that objects can have hidden prototypes, so we need to traverse
999 // the whole chain of hidden prototypes to do a 'local' lookup.
1000 JSObject* real_holder = global;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001001 LookupResult lookup;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001002 while (true) {
1003 real_holder->LocalLookup(*name, &lookup);
1004 if (lookup.IsProperty()) {
1005 // Determine if this is a redeclaration of something read-only.
1006 if (lookup.IsReadOnly()) {
1007 // If we found readonly property on one of hidden prototypes,
1008 // just shadow it.
1009 if (real_holder != Top::context()->global()) break;
1010 return ThrowRedeclarationError("const", name);
1011 }
1012
1013 // Determine if this is a redeclaration of an intercepted read-only
1014 // property and figure out if the property exists at all.
1015 bool found = true;
1016 PropertyType type = lookup.type();
1017 if (type == INTERCEPTOR) {
1018 HandleScope handle_scope;
1019 Handle<JSObject> holder(real_holder);
1020 PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
1021 real_holder = *holder;
1022 if (intercepted == ABSENT) {
1023 // The interceptor claims the property isn't there. We need to
1024 // make sure to introduce it.
1025 found = false;
1026 } else if ((intercepted & READ_ONLY) != 0) {
1027 // The property is present, but read-only. Since we're trying to
1028 // overwrite it with a variable declaration we must throw a
1029 // re-declaration error. However if we found readonly property
1030 // on one of hidden prototypes, just shadow it.
1031 if (real_holder != Top::context()->global()) break;
1032 return ThrowRedeclarationError("const", name);
1033 }
1034 }
1035
1036 if (found && !assign) {
1037 // The global property is there and we're not assigning any value
1038 // to it. Just return.
1039 return Heap::undefined_value();
1040 }
1041
1042 // Assign the value (or undefined) to the property.
1043 Object* value = (assign) ? args[1] : Heap::undefined_value();
1044 return real_holder->SetProperty(&lookup, *name, value, attributes);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001045 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001046
1047 Object* proto = real_holder->GetPrototype();
1048 if (!proto->IsJSObject())
1049 break;
1050
1051 if (!JSObject::cast(proto)->map()->is_hidden_prototype())
1052 break;
1053
1054 real_holder = JSObject::cast(proto);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001055 }
1056
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001057 global = Top::context()->global();
1058 if (assign) {
1059 return global->IgnoreAttributesAndSetLocalProperty(*name,
1060 args[1],
1061 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001062 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001063 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001064}
1065
1066
1067static Object* Runtime_InitializeConstGlobal(Arguments args) {
1068 // All constants are declared with an initial value. The name
1069 // of the constant is the first argument and the initial value
1070 // is the second.
1071 RUNTIME_ASSERT(args.length() == 2);
1072 CONVERT_ARG_CHECKED(String, name, 0);
1073 Handle<Object> value = args.at<Object>(1);
1074
1075 // Get the current global object from top.
1076 GlobalObject* global = Top::context()->global();
1077
1078 // According to ECMA-262, section 12.2, page 62, the property must
1079 // not be deletable. Since it's a const, it must be READ_ONLY too.
1080 PropertyAttributes attributes =
1081 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
1082
1083 // Lookup the property locally in the global object. If it isn't
1084 // there, we add the property and take special precautions to always
1085 // add it as a local property even in case of callbacks in the
1086 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001087 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001088 LookupResult lookup;
1089 global->LocalLookup(*name, &lookup);
1090 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001091 return global->IgnoreAttributesAndSetLocalProperty(*name,
1092 *value,
1093 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001094 }
1095
1096 // Determine if this is a redeclaration of something not
1097 // read-only. In case the result is hidden behind an interceptor we
1098 // need to ask it for the property attributes.
1099 if (!lookup.IsReadOnly()) {
1100 if (lookup.type() != INTERCEPTOR) {
1101 return ThrowRedeclarationError("var", name);
1102 }
1103
1104 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
1105
1106 // Throw re-declaration error if the intercepted property is present
1107 // but not read-only.
1108 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
1109 return ThrowRedeclarationError("var", name);
1110 }
1111
1112 // Restore global object from context (in case of GC) and continue
1113 // with setting the value because the property is either absent or
1114 // read-only. We also have to do redo the lookup.
1115 global = Top::context()->global();
1116
1117 // BUG 1213579: Handle the case where we have to set a read-only
1118 // property through an interceptor and only do it if it's
1119 // uninitialized, e.g. the hole. Nirk...
1120 global->SetProperty(*name, *value, attributes);
1121 return *value;
1122 }
1123
1124 // Set the value, but only we're assigning the initial value to a
1125 // constant. For now, we determine this by checking if the
1126 // current value is the hole.
1127 PropertyType type = lookup.type();
1128 if (type == FIELD) {
1129 FixedArray* properties = global->properties();
1130 int index = lookup.GetFieldIndex();
1131 if (properties->get(index)->IsTheHole()) {
1132 properties->set(index, *value);
1133 }
1134 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001135 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
1136 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001137 }
1138 } else {
1139 // Ignore re-initialization of constants that have already been
1140 // assigned a function value.
1141 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
1142 }
1143
1144 // Use the set value as the result of the operation.
1145 return *value;
1146}
1147
1148
1149static Object* Runtime_InitializeConstContextSlot(Arguments args) {
1150 HandleScope scope;
1151 ASSERT(args.length() == 3);
1152
1153 Handle<Object> value(args[0]);
1154 ASSERT(!value->IsTheHole());
1155 CONVERT_ARG_CHECKED(Context, context, 1);
1156 Handle<String> name(String::cast(args[2]));
1157
1158 // Initializations are always done in the function context.
1159 context = Handle<Context>(context->fcontext());
1160
1161 int index;
1162 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001163 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001164 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001165 context->Lookup(name, flags, &index, &attributes);
1166
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001167 // In most situations, the property introduced by the const
1168 // declaration should be present in the context extension object.
1169 // However, because declaration and initialization are separate, the
1170 // property might have been deleted (if it was introduced by eval)
1171 // before we reach the initialization point.
1172 //
1173 // Example:
1174 //
1175 // function f() { eval("delete x; const x;"); }
1176 //
1177 // In that case, the initialization behaves like a normal assignment
1178 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001179 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001180 // Property was found in a context.
1181 if (holder->IsContext()) {
1182 // The holder cannot be the function context. If it is, there
1183 // should have been a const redeclaration error when declaring
1184 // the const property.
1185 ASSERT(!holder.is_identical_to(context));
1186 if ((attributes & READ_ONLY) == 0) {
1187 Handle<Context>::cast(holder)->set(index, *value);
1188 }
1189 } else {
1190 // The holder is an arguments object.
1191 ASSERT((attributes & READ_ONLY) == 0);
1192 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001193 }
1194 return *value;
1195 }
1196
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001197 // The property could not be found, we introduce it in the global
1198 // context.
1199 if (attributes == ABSENT) {
1200 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1201 SetProperty(global, name, value, NONE);
1202 return *value;
1203 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001204
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001205 // The property was present in a context extension object.
1206 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001207
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001208 if (*context_ext == context->extension()) {
1209 // This is the property that was introduced by the const
1210 // declaration. Set it if it hasn't been set before. NOTE: We
1211 // cannot use GetProperty() to get the current value as it
1212 // 'unholes' the value.
1213 LookupResult lookup;
1214 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1215 ASSERT(lookup.IsProperty()); // the property was declared
1216 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1217
1218 PropertyType type = lookup.type();
1219 if (type == FIELD) {
1220 FixedArray* properties = context_ext->properties();
1221 int index = lookup.GetFieldIndex();
1222 if (properties->get(index)->IsTheHole()) {
1223 properties->set(index, *value);
1224 }
1225 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001226 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
1227 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001228 }
1229 } else {
1230 // We should not reach here. Any real, named property should be
1231 // either a field or a dictionary slot.
1232 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001233 }
1234 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001235 // The property was found in a different context extension object.
1236 // Set it if it is not a read-only property.
1237 if ((attributes & READ_ONLY) == 0) {
1238 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1239 // Setting a property might throw an exception. Exceptions
1240 // are converted to empty handles in handle operations. We
1241 // need to convert back to exceptions here.
1242 if (set.is_null()) {
1243 ASSERT(Top::has_pending_exception());
1244 return Failure::Exception();
1245 }
1246 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001247 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001248
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001249 return *value;
1250}
1251
1252
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001253static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1254 Arguments args) {
1255 HandleScope scope;
1256 ASSERT(args.length() == 2);
1257 CONVERT_ARG_CHECKED(JSObject, object, 0);
1258 CONVERT_SMI_CHECKED(properties, args[1]);
1259 if (object->HasFastProperties()) {
1260 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1261 }
1262 return *object;
1263}
1264
1265
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001266static Object* Runtime_RegExpExec(Arguments args) {
1267 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001268 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001269 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1270 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001271 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001272 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001273 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001274 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001275 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001276 RUNTIME_ASSERT(index >= 0);
1277 RUNTIME_ASSERT(index <= subject->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001278 Counters::regexp_entry_runtime.Increment();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001279 Handle<Object> result = RegExpImpl::Exec(regexp,
1280 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001281 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001282 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001283 if (result.is_null()) return Failure::Exception();
1284 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001285}
1286
1287
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00001288static Object* Runtime_RegExpConstructResult(Arguments args) {
1289 ASSERT(args.length() == 3);
1290 CONVERT_SMI_CHECKED(elements_count, args[0]);
1291 if (elements_count > JSArray::kMaxFastElementsLength) {
1292 return Top::ThrowIllegalOperation();
1293 }
1294 Object* new_object = Heap::AllocateFixedArrayWithHoles(elements_count);
1295 if (new_object->IsFailure()) return new_object;
1296 FixedArray* elements = FixedArray::cast(new_object);
1297 new_object = Heap::AllocateRaw(JSRegExpResult::kSize,
1298 NEW_SPACE,
1299 OLD_POINTER_SPACE);
1300 if (new_object->IsFailure()) return new_object;
1301 {
1302 AssertNoAllocation no_gc;
1303 HandleScope scope;
1304 reinterpret_cast<HeapObject*>(new_object)->
1305 set_map(Top::global_context()->regexp_result_map());
1306 }
1307 JSArray* array = JSArray::cast(new_object);
1308 array->set_properties(Heap::empty_fixed_array());
1309 array->set_elements(elements);
1310 array->set_length(Smi::FromInt(elements_count));
1311 // Write in-object properties after the length of the array.
1312 array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, args[1]);
1313 array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, args[2]);
1314 return array;
1315}
1316
1317
lrn@chromium.org25156de2010-04-06 13:10:27 +00001318static Object* Runtime_RegExpInitializeObject(Arguments args) {
1319 AssertNoAllocation no_alloc;
1320 ASSERT(args.length() == 5);
1321 CONVERT_CHECKED(JSRegExp, regexp, args[0]);
1322 CONVERT_CHECKED(String, source, args[1]);
1323
1324 Object* global = args[2];
1325 if (!global->IsTrue()) global = Heap::false_value();
1326
1327 Object* ignoreCase = args[3];
1328 if (!ignoreCase->IsTrue()) ignoreCase = Heap::false_value();
1329
1330 Object* multiline = args[4];
1331 if (!multiline->IsTrue()) multiline = Heap::false_value();
1332
1333 Map* map = regexp->map();
1334 Object* constructor = map->constructor();
1335 if (constructor->IsJSFunction() &&
1336 JSFunction::cast(constructor)->initial_map() == map) {
1337 // If we still have the original map, set in-object properties directly.
1338 regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, source);
1339 // TODO(lrn): Consider skipping write barrier on booleans as well.
1340 // Both true and false should be in oldspace at all times.
1341 regexp->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, global);
1342 regexp->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex, ignoreCase);
1343 regexp->InObjectPropertyAtPut(JSRegExp::kMultilineFieldIndex, multiline);
1344 regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex,
1345 Smi::FromInt(0),
1346 SKIP_WRITE_BARRIER);
1347 return regexp;
1348 }
1349
1350 // Map has changed, so use generic, but slower, method.
1351 PropertyAttributes final =
1352 static_cast<PropertyAttributes>(READ_ONLY | DONT_ENUM | DONT_DELETE);
1353 PropertyAttributes writable =
1354 static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
1355 regexp->IgnoreAttributesAndSetLocalProperty(Heap::source_symbol(),
1356 source,
1357 final);
1358 regexp->IgnoreAttributesAndSetLocalProperty(Heap::global_symbol(),
1359 global,
1360 final);
1361 regexp->IgnoreAttributesAndSetLocalProperty(Heap::ignore_case_symbol(),
1362 ignoreCase,
1363 final);
1364 regexp->IgnoreAttributesAndSetLocalProperty(Heap::multiline_symbol(),
1365 multiline,
1366 final);
1367 regexp->IgnoreAttributesAndSetLocalProperty(Heap::last_index_symbol(),
1368 Smi::FromInt(0),
1369 writable);
1370 return regexp;
1371}
1372
1373
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001374static Object* Runtime_FinishArrayPrototypeSetup(Arguments args) {
1375 HandleScope scope;
1376 ASSERT(args.length() == 1);
1377 CONVERT_ARG_CHECKED(JSArray, prototype, 0);
1378 // This is necessary to enable fast checks for absence of elements
1379 // on Array.prototype and below.
1380 prototype->set_elements(Heap::empty_fixed_array());
1381 return Smi::FromInt(0);
1382}
1383
1384
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001385static Handle<JSFunction> InstallBuiltin(Handle<JSObject> holder,
1386 const char* name,
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001387 Builtins::Name builtin_name) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001388 Handle<String> key = Factory::LookupAsciiSymbol(name);
1389 Handle<Code> code(Builtins::builtin(builtin_name));
1390 Handle<JSFunction> optimized = Factory::NewFunction(key,
1391 JS_OBJECT_TYPE,
1392 JSObject::kHeaderSize,
1393 code,
1394 false);
1395 optimized->shared()->DontAdaptArguments();
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001396 SetProperty(holder, key, optimized, NONE);
1397 return optimized;
1398}
1399
1400
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001401static Object* Runtime_SpecialArrayFunctions(Arguments args) {
1402 HandleScope scope;
1403 ASSERT(args.length() == 1);
1404 CONVERT_ARG_CHECKED(JSObject, holder, 0);
1405
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001406 InstallBuiltin(holder, "pop", Builtins::ArrayPop);
1407 InstallBuiltin(holder, "push", Builtins::ArrayPush);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001408 InstallBuiltin(holder, "shift", Builtins::ArrayShift);
1409 InstallBuiltin(holder, "unshift", Builtins::ArrayUnshift);
1410 InstallBuiltin(holder, "slice", Builtins::ArraySlice);
1411 InstallBuiltin(holder, "splice", Builtins::ArraySplice);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001412 InstallBuiltin(holder, "concat", Builtins::ArrayConcat);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001413
1414 return *holder;
1415}
1416
1417
ager@chromium.org357bf652010-04-12 11:30:10 +00001418static Object* Runtime_GetGlobalReceiver(Arguments args) {
1419 // Returns a real global receiver, not one of builtins object.
1420 Context* global_context = Top::context()->global()->global_context();
1421 return global_context->global()->global_receiver();
1422}
1423
1424
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001425static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1426 HandleScope scope;
1427 ASSERT(args.length() == 4);
1428 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1429 int index = Smi::cast(args[1])->value();
1430 Handle<String> pattern = args.at<String>(2);
1431 Handle<String> flags = args.at<String>(3);
1432
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001433 // Get the RegExp function from the context in the literals array.
1434 // This is the RegExp function from the context in which the
1435 // function was created. We do not use the RegExp function from the
1436 // current global context because this might be the RegExp function
1437 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001438 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001439 Handle<JSFunction>(
1440 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001441 // Compute the regular expression literal.
1442 bool has_pending_exception;
1443 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001444 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1445 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001446 if (has_pending_exception) {
1447 ASSERT(Top::has_pending_exception());
1448 return Failure::Exception();
1449 }
1450 literals->set(index, *regexp);
1451 return *regexp;
1452}
1453
1454
1455static Object* Runtime_FunctionGetName(Arguments args) {
1456 NoHandleAllocation ha;
1457 ASSERT(args.length() == 1);
1458
1459 CONVERT_CHECKED(JSFunction, f, args[0]);
1460 return f->shared()->name();
1461}
1462
1463
ager@chromium.org236ad962008-09-25 09:45:57 +00001464static Object* Runtime_FunctionSetName(Arguments args) {
1465 NoHandleAllocation ha;
1466 ASSERT(args.length() == 2);
1467
1468 CONVERT_CHECKED(JSFunction, f, args[0]);
1469 CONVERT_CHECKED(String, name, args[1]);
1470 f->shared()->set_name(name);
1471 return Heap::undefined_value();
1472}
1473
1474
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001475static Object* Runtime_FunctionRemovePrototype(Arguments args) {
1476 NoHandleAllocation ha;
1477 ASSERT(args.length() == 1);
1478
1479 CONVERT_CHECKED(JSFunction, f, args[0]);
1480 Object* obj = f->RemovePrototype();
1481 if (obj->IsFailure()) return obj;
1482
1483 return Heap::undefined_value();
1484}
1485
1486
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001487static Object* Runtime_FunctionGetScript(Arguments args) {
1488 HandleScope scope;
1489 ASSERT(args.length() == 1);
1490
1491 CONVERT_CHECKED(JSFunction, fun, args[0]);
1492 Handle<Object> script = Handle<Object>(fun->shared()->script());
1493 if (!script->IsScript()) return Heap::undefined_value();
1494
1495 return *GetScriptWrapper(Handle<Script>::cast(script));
1496}
1497
1498
1499static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1500 NoHandleAllocation ha;
1501 ASSERT(args.length() == 1);
1502
1503 CONVERT_CHECKED(JSFunction, f, args[0]);
1504 return f->shared()->GetSourceCode();
1505}
1506
1507
1508static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1509 NoHandleAllocation ha;
1510 ASSERT(args.length() == 1);
1511
1512 CONVERT_CHECKED(JSFunction, fun, args[0]);
1513 int pos = fun->shared()->start_position();
1514 return Smi::FromInt(pos);
1515}
1516
1517
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001518static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1519 ASSERT(args.length() == 2);
1520
1521 CONVERT_CHECKED(JSFunction, fun, args[0]);
1522 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1523
1524 Code* code = fun->code();
1525 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1526
1527 Address pc = code->address() + offset;
1528 return Smi::FromInt(fun->code()->SourcePosition(pc));
1529}
1530
1531
1532
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001533static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1534 NoHandleAllocation ha;
1535 ASSERT(args.length() == 2);
1536
1537 CONVERT_CHECKED(JSFunction, fun, args[0]);
1538 CONVERT_CHECKED(String, name, args[1]);
1539 fun->SetInstanceClassName(name);
1540 return Heap::undefined_value();
1541}
1542
1543
1544static Object* Runtime_FunctionSetLength(Arguments args) {
1545 NoHandleAllocation ha;
1546 ASSERT(args.length() == 2);
1547
1548 CONVERT_CHECKED(JSFunction, fun, args[0]);
1549 CONVERT_CHECKED(Smi, length, args[1]);
1550 fun->shared()->set_length(length->value());
1551 return length;
1552}
1553
1554
1555static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001556 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001557 ASSERT(args.length() == 2);
1558
1559 CONVERT_CHECKED(JSFunction, fun, args[0]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001560 ASSERT(fun->should_have_prototype());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001561 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1562 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001563 return args[0]; // return TOS
1564}
1565
1566
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001567static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1568 NoHandleAllocation ha;
1569 ASSERT(args.length() == 1);
1570
1571 CONVERT_CHECKED(JSFunction, f, args[0]);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001572 return f->shared()->IsApiFunction() ? Heap::true_value()
1573 : Heap::false_value();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001574}
1575
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001576static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1577 NoHandleAllocation ha;
1578 ASSERT(args.length() == 1);
1579
1580 CONVERT_CHECKED(JSFunction, f, args[0]);
1581 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1582}
1583
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001584
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001585static Object* Runtime_SetCode(Arguments args) {
1586 HandleScope scope;
1587 ASSERT(args.length() == 2);
1588
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001589 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001590 Handle<Object> code = args.at<Object>(1);
1591
1592 Handle<Context> context(target->context());
1593
1594 if (!code->IsNull()) {
1595 RUNTIME_ASSERT(code->IsJSFunction());
1596 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001597 Handle<SharedFunctionInfo> shared(fun->shared());
1598 SetExpectedNofProperties(target, shared->expected_nof_properties());
1599
1600 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001601 return Failure::Exception();
1602 }
1603 // Set the code, formal parameter count, and the length of the target
1604 // function.
1605 target->set_code(fun->code());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001606 target->shared()->set_length(shared->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001607 target->shared()->set_formal_parameter_count(
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001608 shared->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001609 // Set the source code of the target function to undefined.
1610 // SetCode is only used for built-in constructors like String,
1611 // Array, and Object, and some web code
1612 // doesn't like seeing source code for constructors.
1613 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001614 // Clear the optimization hints related to the compiled code as these are no
1615 // longer valid when the code is overwritten.
1616 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001617 context = Handle<Context>(fun->context());
1618
1619 // Make sure we get a fresh copy of the literal vector to avoid
1620 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001621 int number_of_literals = fun->NumberOfLiterals();
1622 Handle<FixedArray> literals =
1623 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001624 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001625 // Insert the object, regexp and array functions in the literals
1626 // array prefix. These are the functions that will be used when
1627 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001628 literals->set(JSFunction::kLiteralGlobalContextIndex,
1629 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001630 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001631 // It's okay to skip the write barrier here because the literals
1632 // are guaranteed to be in old space.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001633 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001634 }
1635
1636 target->set_context(*context);
1637 return *target;
1638}
1639
1640
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001641static Object* CharFromCode(Object* char_code) {
1642 uint32_t code;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001643 if (char_code->ToArrayIndex(&code)) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001644 if (code <= 0xffff) {
1645 return Heap::LookupSingleCharacterStringFromCode(code);
1646 }
1647 }
1648 return Heap::empty_string();
1649}
1650
1651
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001652static Object* Runtime_StringCharCodeAt(Arguments args) {
1653 NoHandleAllocation ha;
1654 ASSERT(args.length() == 2);
1655
1656 CONVERT_CHECKED(String, subject, args[0]);
1657 Object* index = args[1];
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001658 RUNTIME_ASSERT(index->IsNumber());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001659
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001660 uint32_t i = 0;
1661 if (index->IsSmi()) {
1662 int value = Smi::cast(index)->value();
1663 if (value < 0) return Heap::nan_value();
1664 i = value;
1665 } else {
1666 ASSERT(index->IsHeapNumber());
1667 double value = HeapNumber::cast(index)->value();
1668 i = static_cast<uint32_t>(DoubleToInteger(value));
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001669 }
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001670
1671 // Flatten the string. If someone wants to get a char at an index
1672 // in a cons string, it is likely that more indices will be
1673 // accessed.
1674 Object* flat = subject->TryFlatten();
1675 if (flat->IsFailure()) return flat;
1676 subject = String::cast(flat);
1677
1678 if (i >= static_cast<uint32_t>(subject->length())) {
1679 return Heap::nan_value();
1680 }
1681
1682 return Smi::FromInt(subject->Get(i));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001683}
1684
1685
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001686static Object* Runtime_CharFromCode(Arguments args) {
1687 NoHandleAllocation ha;
1688 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001689 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001690}
1691
lrn@chromium.org25156de2010-04-06 13:10:27 +00001692
1693class FixedArrayBuilder {
1694 public:
1695 explicit FixedArrayBuilder(int initial_capacity)
1696 : array_(Factory::NewFixedArrayWithHoles(initial_capacity)),
1697 length_(0) {
1698 // Require a non-zero initial size. Ensures that doubling the size to
1699 // extend the array will work.
1700 ASSERT(initial_capacity > 0);
1701 }
1702
1703 explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
1704 : array_(backing_store),
1705 length_(0) {
1706 // Require a non-zero initial size. Ensures that doubling the size to
1707 // extend the array will work.
1708 ASSERT(backing_store->length() > 0);
1709 }
1710
1711 bool HasCapacity(int elements) {
1712 int length = array_->length();
1713 int required_length = length_ + elements;
1714 return (length >= required_length);
1715 }
1716
1717 void EnsureCapacity(int elements) {
1718 int length = array_->length();
1719 int required_length = length_ + elements;
1720 if (length < required_length) {
1721 int new_length = length;
1722 do {
1723 new_length *= 2;
1724 } while (new_length < required_length);
1725 Handle<FixedArray> extended_array =
1726 Factory::NewFixedArrayWithHoles(new_length);
1727 array_->CopyTo(0, *extended_array, 0, length_);
1728 array_ = extended_array;
1729 }
1730 }
1731
1732 void Add(Object* value) {
1733 ASSERT(length_ < capacity());
1734 array_->set(length_, value);
1735 length_++;
1736 }
1737
1738 void Add(Smi* value) {
1739 ASSERT(length_ < capacity());
1740 array_->set(length_, value);
1741 length_++;
1742 }
1743
1744 Handle<FixedArray> array() {
1745 return array_;
1746 }
1747
1748 int length() {
1749 return length_;
1750 }
1751
1752 int capacity() {
1753 return array_->length();
1754 }
1755
1756 Handle<JSArray> ToJSArray() {
1757 Handle<JSArray> result_array = Factory::NewJSArrayWithElements(array_);
1758 result_array->set_length(Smi::FromInt(length_));
1759 return result_array;
1760 }
1761
1762 Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
1763 target_array->set_elements(*array_);
1764 target_array->set_length(Smi::FromInt(length_));
1765 return target_array;
1766 }
1767
1768 private:
1769 Handle<FixedArray> array_;
1770 int length_;
1771};
1772
1773
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001774// Forward declarations.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001775const int kStringBuilderConcatHelperLengthBits = 11;
1776const int kStringBuilderConcatHelperPositionBits = 19;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001777
1778template <typename schar>
1779static inline void StringBuilderConcatHelper(String*,
1780 schar*,
1781 FixedArray*,
1782 int);
1783
lrn@chromium.org25156de2010-04-06 13:10:27 +00001784typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
1785 StringBuilderSubstringLength;
1786typedef BitField<int,
1787 kStringBuilderConcatHelperLengthBits,
1788 kStringBuilderConcatHelperPositionBits>
1789 StringBuilderSubstringPosition;
1790
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001791
1792class ReplacementStringBuilder {
1793 public:
1794 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
lrn@chromium.org25156de2010-04-06 13:10:27 +00001795 : array_builder_(estimated_part_count),
1796 subject_(subject),
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001797 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001798 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001799 // Require a non-zero initial size. Ensures that doubling the size to
1800 // extend the array will work.
1801 ASSERT(estimated_part_count > 0);
1802 }
1803
lrn@chromium.org25156de2010-04-06 13:10:27 +00001804 static inline void AddSubjectSlice(FixedArrayBuilder* builder,
1805 int from,
1806 int to) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001807 ASSERT(from >= 0);
1808 int length = to - from;
1809 ASSERT(length > 0);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001810 if (StringBuilderSubstringLength::is_valid(length) &&
1811 StringBuilderSubstringPosition::is_valid(from)) {
1812 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1813 StringBuilderSubstringPosition::encode(from);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001814 builder->Add(Smi::FromInt(encoded_slice));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001815 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001816 // Otherwise encode as two smis.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001817 builder->Add(Smi::FromInt(-length));
1818 builder->Add(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001819 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00001820 }
1821
1822
1823 void EnsureCapacity(int elements) {
1824 array_builder_.EnsureCapacity(elements);
1825 }
1826
1827
1828 void AddSubjectSlice(int from, int to) {
1829 AddSubjectSlice(&array_builder_, from, to);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001830 IncrementCharacterCount(to - from);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001831 }
1832
1833
1834 void AddString(Handle<String> string) {
1835 int length = string->length();
1836 ASSERT(length > 0);
1837 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001838 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001839 is_ascii_ = false;
1840 }
1841 IncrementCharacterCount(length);
1842 }
1843
1844
1845 Handle<String> ToString() {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001846 if (array_builder_.length() == 0) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001847 return Factory::empty_string();
1848 }
1849
1850 Handle<String> joined_string;
1851 if (is_ascii_) {
1852 joined_string = NewRawAsciiString(character_count_);
1853 AssertNoAllocation no_alloc;
1854 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1855 char* char_buffer = seq->GetChars();
1856 StringBuilderConcatHelper(*subject_,
1857 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001858 *array_builder_.array(),
1859 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001860 } else {
1861 // Non-ASCII.
1862 joined_string = NewRawTwoByteString(character_count_);
1863 AssertNoAllocation no_alloc;
1864 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1865 uc16* char_buffer = seq->GetChars();
1866 StringBuilderConcatHelper(*subject_,
1867 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001868 *array_builder_.array(),
1869 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001870 }
1871 return joined_string;
1872 }
1873
1874
1875 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001876 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001877 V8::FatalProcessOutOfMemory("String.replace result too large.");
1878 }
1879 character_count_ += by;
1880 }
1881
lrn@chromium.org25156de2010-04-06 13:10:27 +00001882 Handle<JSArray> GetParts() {
1883 Handle<JSArray> result =
1884 Factory::NewJSArrayWithElements(array_builder_.array());
1885 result->set_length(Smi::FromInt(array_builder_.length()));
1886 return result;
1887 }
kmillikin@chromium.orgd9825192010-03-30 08:36:16 +00001888
lrn@chromium.org25156de2010-04-06 13:10:27 +00001889 private:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001890 Handle<String> NewRawAsciiString(int size) {
1891 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1892 }
1893
1894
1895 Handle<String> NewRawTwoByteString(int size) {
1896 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1897 }
1898
1899
1900 void AddElement(Object* element) {
1901 ASSERT(element->IsSmi() || element->IsString());
lrn@chromium.org25156de2010-04-06 13:10:27 +00001902 ASSERT(array_builder_.capacity() > array_builder_.length());
1903 array_builder_.Add(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001904 }
1905
lrn@chromium.org25156de2010-04-06 13:10:27 +00001906 FixedArrayBuilder array_builder_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001907 Handle<String> subject_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001908 int character_count_;
1909 bool is_ascii_;
1910};
1911
1912
1913class CompiledReplacement {
1914 public:
1915 CompiledReplacement()
1916 : parts_(1), replacement_substrings_(0) {}
1917
1918 void Compile(Handle<String> replacement,
1919 int capture_count,
1920 int subject_length);
1921
1922 void Apply(ReplacementStringBuilder* builder,
1923 int match_from,
1924 int match_to,
1925 Handle<JSArray> last_match_info);
1926
1927 // Number of distinct parts of the replacement pattern.
1928 int parts() {
1929 return parts_.length();
1930 }
1931 private:
1932 enum PartType {
1933 SUBJECT_PREFIX = 1,
1934 SUBJECT_SUFFIX,
1935 SUBJECT_CAPTURE,
1936 REPLACEMENT_SUBSTRING,
1937 REPLACEMENT_STRING,
1938
1939 NUMBER_OF_PART_TYPES
1940 };
1941
1942 struct ReplacementPart {
1943 static inline ReplacementPart SubjectMatch() {
1944 return ReplacementPart(SUBJECT_CAPTURE, 0);
1945 }
1946 static inline ReplacementPart SubjectCapture(int capture_index) {
1947 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1948 }
1949 static inline ReplacementPart SubjectPrefix() {
1950 return ReplacementPart(SUBJECT_PREFIX, 0);
1951 }
1952 static inline ReplacementPart SubjectSuffix(int subject_length) {
1953 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1954 }
1955 static inline ReplacementPart ReplacementString() {
1956 return ReplacementPart(REPLACEMENT_STRING, 0);
1957 }
1958 static inline ReplacementPart ReplacementSubString(int from, int to) {
1959 ASSERT(from >= 0);
1960 ASSERT(to > from);
1961 return ReplacementPart(-from, to);
1962 }
1963
1964 // If tag <= 0 then it is the negation of a start index of a substring of
1965 // the replacement pattern, otherwise it's a value from PartType.
1966 ReplacementPart(int tag, int data)
1967 : tag(tag), data(data) {
1968 // Must be non-positive or a PartType value.
1969 ASSERT(tag < NUMBER_OF_PART_TYPES);
1970 }
1971 // Either a value of PartType or a non-positive number that is
1972 // the negation of an index into the replacement string.
1973 int tag;
1974 // The data value's interpretation depends on the value of tag:
1975 // tag == SUBJECT_PREFIX ||
1976 // tag == SUBJECT_SUFFIX: data is unused.
1977 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1978 // tag == REPLACEMENT_SUBSTRING ||
1979 // tag == REPLACEMENT_STRING: data is index into array of substrings
1980 // of the replacement string.
1981 // tag <= 0: Temporary representation of the substring of the replacement
1982 // string ranging over -tag .. data.
1983 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1984 // substring objects.
1985 int data;
1986 };
1987
1988 template<typename Char>
1989 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1990 Vector<Char> characters,
1991 int capture_count,
1992 int subject_length) {
1993 int length = characters.length();
1994 int last = 0;
1995 for (int i = 0; i < length; i++) {
1996 Char c = characters[i];
1997 if (c == '$') {
1998 int next_index = i + 1;
1999 if (next_index == length) { // No next character!
2000 break;
2001 }
2002 Char c2 = characters[next_index];
2003 switch (c2) {
2004 case '$':
2005 if (i > last) {
2006 // There is a substring before. Include the first "$".
2007 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
2008 last = next_index + 1; // Continue after the second "$".
2009 } else {
2010 // Let the next substring start with the second "$".
2011 last = next_index;
2012 }
2013 i = next_index;
2014 break;
2015 case '`':
2016 if (i > last) {
2017 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2018 }
2019 parts->Add(ReplacementPart::SubjectPrefix());
2020 i = next_index;
2021 last = i + 1;
2022 break;
2023 case '\'':
2024 if (i > last) {
2025 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2026 }
2027 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
2028 i = next_index;
2029 last = i + 1;
2030 break;
2031 case '&':
2032 if (i > last) {
2033 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2034 }
2035 parts->Add(ReplacementPart::SubjectMatch());
2036 i = next_index;
2037 last = i + 1;
2038 break;
2039 case '0':
2040 case '1':
2041 case '2':
2042 case '3':
2043 case '4':
2044 case '5':
2045 case '6':
2046 case '7':
2047 case '8':
2048 case '9': {
2049 int capture_ref = c2 - '0';
2050 if (capture_ref > capture_count) {
2051 i = next_index;
2052 continue;
2053 }
2054 int second_digit_index = next_index + 1;
2055 if (second_digit_index < length) {
2056 // Peek ahead to see if we have two digits.
2057 Char c3 = characters[second_digit_index];
2058 if ('0' <= c3 && c3 <= '9') { // Double digits.
2059 int double_digit_ref = capture_ref * 10 + c3 - '0';
2060 if (double_digit_ref <= capture_count) {
2061 next_index = second_digit_index;
2062 capture_ref = double_digit_ref;
2063 }
2064 }
2065 }
2066 if (capture_ref > 0) {
2067 if (i > last) {
2068 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2069 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002070 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002071 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
2072 last = next_index + 1;
2073 }
2074 i = next_index;
2075 break;
2076 }
2077 default:
2078 i = next_index;
2079 break;
2080 }
2081 }
2082 }
2083 if (length > last) {
2084 if (last == 0) {
2085 parts->Add(ReplacementPart::ReplacementString());
2086 } else {
2087 parts->Add(ReplacementPart::ReplacementSubString(last, length));
2088 }
2089 }
2090 }
2091
2092 ZoneList<ReplacementPart> parts_;
2093 ZoneList<Handle<String> > replacement_substrings_;
2094};
2095
2096
2097void CompiledReplacement::Compile(Handle<String> replacement,
2098 int capture_count,
2099 int subject_length) {
2100 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00002101 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002102 AssertNoAllocation no_alloc;
2103 ParseReplacementPattern(&parts_,
2104 replacement->ToAsciiVector(),
2105 capture_count,
2106 subject_length);
2107 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00002108 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002109 AssertNoAllocation no_alloc;
2110
2111 ParseReplacementPattern(&parts_,
2112 replacement->ToUC16Vector(),
2113 capture_count,
2114 subject_length);
2115 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002116 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002117 int substring_index = 0;
2118 for (int i = 0, n = parts_.length(); i < n; i++) {
2119 int tag = parts_[i].tag;
2120 if (tag <= 0) { // A replacement string slice.
2121 int from = -tag;
2122 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002123 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002124 parts_[i].tag = REPLACEMENT_SUBSTRING;
2125 parts_[i].data = substring_index;
2126 substring_index++;
2127 } else if (tag == REPLACEMENT_STRING) {
2128 replacement_substrings_.Add(replacement);
2129 parts_[i].data = substring_index;
2130 substring_index++;
2131 }
2132 }
2133}
2134
2135
2136void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
2137 int match_from,
2138 int match_to,
2139 Handle<JSArray> last_match_info) {
2140 for (int i = 0, n = parts_.length(); i < n; i++) {
2141 ReplacementPart part = parts_[i];
2142 switch (part.tag) {
2143 case SUBJECT_PREFIX:
2144 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
2145 break;
2146 case SUBJECT_SUFFIX: {
2147 int subject_length = part.data;
2148 if (match_to < subject_length) {
2149 builder->AddSubjectSlice(match_to, subject_length);
2150 }
2151 break;
2152 }
2153 case SUBJECT_CAPTURE: {
2154 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002155 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002156 int from = RegExpImpl::GetCapture(match_info, capture * 2);
2157 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
2158 if (from >= 0 && to > from) {
2159 builder->AddSubjectSlice(from, to);
2160 }
2161 break;
2162 }
2163 case REPLACEMENT_SUBSTRING:
2164 case REPLACEMENT_STRING:
2165 builder->AddString(replacement_substrings_[part.data]);
2166 break;
2167 default:
2168 UNREACHABLE();
2169 }
2170 }
2171}
2172
2173
2174
2175static Object* StringReplaceRegExpWithString(String* subject,
2176 JSRegExp* regexp,
2177 String* replacement,
2178 JSArray* last_match_info) {
2179 ASSERT(subject->IsFlat());
2180 ASSERT(replacement->IsFlat());
2181
2182 HandleScope handles;
2183
2184 int length = subject->length();
2185 Handle<String> subject_handle(subject);
2186 Handle<JSRegExp> regexp_handle(regexp);
2187 Handle<String> replacement_handle(replacement);
2188 Handle<JSArray> last_match_info_handle(last_match_info);
2189 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2190 subject_handle,
2191 0,
2192 last_match_info_handle);
2193 if (match.is_null()) {
2194 return Failure::Exception();
2195 }
2196 if (match->IsNull()) {
2197 return *subject_handle;
2198 }
2199
2200 int capture_count = regexp_handle->CaptureCount();
2201
2202 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002203 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002204 CompiledReplacement compiled_replacement;
2205 compiled_replacement.Compile(replacement_handle,
2206 capture_count,
2207 length);
2208
2209 bool is_global = regexp_handle->GetFlags().is_global();
2210
2211 // Guessing the number of parts that the final result string is built
2212 // from. Global regexps can match any number of times, so we guess
2213 // conservatively.
2214 int expected_parts =
2215 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
2216 ReplacementStringBuilder builder(subject_handle, expected_parts);
2217
2218 // Index of end of last match.
2219 int prev = 0;
2220
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002221 // Number of parts added by compiled replacement plus preceeding
2222 // string and possibly suffix after last match. It is possible for
2223 // all components to use two elements when encoded as two smis.
2224 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002225 bool matched = true;
2226 do {
2227 ASSERT(last_match_info_handle->HasFastElements());
2228 // Increase the capacity of the builder before entering local handle-scope,
2229 // so its internal buffer can safely allocate a new handle if it grows.
2230 builder.EnsureCapacity(parts_added_per_loop);
2231
2232 HandleScope loop_scope;
2233 int start, end;
2234 {
2235 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002236 FixedArray* match_info_array =
2237 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002238
2239 ASSERT_EQ(capture_count * 2 + 2,
2240 RegExpImpl::GetLastCaptureCount(match_info_array));
2241 start = RegExpImpl::GetCapture(match_info_array, 0);
2242 end = RegExpImpl::GetCapture(match_info_array, 1);
2243 }
2244
2245 if (prev < start) {
2246 builder.AddSubjectSlice(prev, start);
2247 }
2248 compiled_replacement.Apply(&builder,
2249 start,
2250 end,
2251 last_match_info_handle);
2252 prev = end;
2253
2254 // Only continue checking for global regexps.
2255 if (!is_global) break;
2256
2257 // Continue from where the match ended, unless it was an empty match.
2258 int next = end;
2259 if (start == end) {
2260 next = end + 1;
2261 if (next > length) break;
2262 }
2263
2264 match = RegExpImpl::Exec(regexp_handle,
2265 subject_handle,
2266 next,
2267 last_match_info_handle);
2268 if (match.is_null()) {
2269 return Failure::Exception();
2270 }
2271 matched = !match->IsNull();
2272 } while (matched);
2273
2274 if (prev < length) {
2275 builder.AddSubjectSlice(prev, length);
2276 }
2277
2278 return *(builder.ToString());
2279}
2280
2281
2282static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2283 ASSERT(args.length() == 4);
2284
2285 CONVERT_CHECKED(String, subject, args[0]);
2286 if (!subject->IsFlat()) {
2287 Object* flat_subject = subject->TryFlatten();
2288 if (flat_subject->IsFailure()) {
2289 return flat_subject;
2290 }
2291 subject = String::cast(flat_subject);
2292 }
2293
2294 CONVERT_CHECKED(String, replacement, args[2]);
2295 if (!replacement->IsFlat()) {
2296 Object* flat_replacement = replacement->TryFlatten();
2297 if (flat_replacement->IsFailure()) {
2298 return flat_replacement;
2299 }
2300 replacement = String::cast(flat_replacement);
2301 }
2302
2303 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2304 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2305
2306 ASSERT(last_match_info->HasFastElements());
2307
2308 return StringReplaceRegExpWithString(subject,
2309 regexp,
2310 replacement,
2311 last_match_info);
2312}
2313
2314
ager@chromium.org7c537e22008-10-16 08:43:32 +00002315// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2316// limit, we can fix the size of tables.
2317static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002318// Reduce alphabet to this size.
2319static const int kBMAlphabetSize = 0x100;
2320// For patterns below this length, the skip length of Boyer-Moore is too short
2321// to compensate for the algorithmic overhead compared to simple brute force.
2322static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002323
ager@chromium.org7c537e22008-10-16 08:43:32 +00002324// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2325// shift. Only allows the last kBMMaxShift characters of the needle
2326// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002327class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002328 public:
2329 BMGoodSuffixBuffers() {}
2330 inline void init(int needle_length) {
2331 ASSERT(needle_length > 1);
2332 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2333 int len = needle_length - start;
2334 biased_suffixes_ = suffixes_ - start;
2335 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2336 for (int i = 0; i <= len; i++) {
2337 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002338 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002339 }
2340 inline int& suffix(int index) {
2341 ASSERT(biased_suffixes_ + index >= suffixes_);
2342 return biased_suffixes_[index];
2343 }
2344 inline int& shift(int index) {
2345 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2346 return biased_good_suffix_shift_[index];
2347 }
2348 private:
2349 int suffixes_[kBMMaxShift + 1];
2350 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002351 int* biased_suffixes_;
2352 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002353 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2354};
2355
2356// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002357static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002358static BMGoodSuffixBuffers bmgs_buffers;
2359
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002360// State of the string match tables.
2361// SIMPLE: No usable content in the buffers.
2362// BOYER_MOORE_HORSPOOL: The bad_char_occurences table has been populated.
2363// BOYER_MOORE: The bmgs_buffers tables have also been populated.
2364// Whenever starting with a new needle, one should call InitializeStringSearch
2365// to determine which search strategy to use, and in the case of a long-needle
2366// strategy, the call also initializes the algorithm to SIMPLE.
2367enum StringSearchAlgorithm { SIMPLE_SEARCH, BOYER_MOORE_HORSPOOL, BOYER_MOORE };
2368static StringSearchAlgorithm algorithm;
2369
2370
ager@chromium.org7c537e22008-10-16 08:43:32 +00002371// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002372template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002373static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern) {
2374 // Only preprocess at most kBMMaxShift last characters of pattern.
2375 int start = pattern.length() < kBMMaxShift ? 0
2376 : pattern.length() - kBMMaxShift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002377 // Run forwards to populate bad_char_table, so that *last* instance
2378 // of character equivalence class is the one registered.
2379 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002380 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2381 : kBMAlphabetSize;
2382 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002383 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002384 } else {
2385 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002386 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002387 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002388 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002389 for (int i = start; i < pattern.length() - 1; i++) {
2390 pchar c = pattern[i];
2391 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002392 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002393 }
2394}
2395
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002396
ager@chromium.org7c537e22008-10-16 08:43:32 +00002397template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002398static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002399 int m = pattern.length();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002400 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002401 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002402 // Compute Good Suffix tables.
2403 bmgs_buffers.init(m);
2404
2405 bmgs_buffers.shift(m-1) = 1;
2406 bmgs_buffers.suffix(m) = m + 1;
2407 pchar last_char = pattern[m - 1];
2408 int suffix = m + 1;
2409 for (int i = m; i > start;) {
2410 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2411 if (bmgs_buffers.shift(suffix) == len) {
2412 bmgs_buffers.shift(suffix) = suffix - i;
2413 }
2414 suffix = bmgs_buffers.suffix(suffix);
2415 }
2416 i--;
2417 suffix--;
2418 bmgs_buffers.suffix(i) = suffix;
2419 if (suffix == m) {
2420 // No suffix to extend, so we check against last_char only.
2421 while (i > start && pattern[i - 1] != last_char) {
2422 if (bmgs_buffers.shift(m) == len) {
2423 bmgs_buffers.shift(m) = m - i;
2424 }
2425 i--;
2426 bmgs_buffers.suffix(i) = m;
2427 }
2428 if (i > start) {
2429 i--;
2430 suffix--;
2431 bmgs_buffers.suffix(i) = suffix;
2432 }
2433 }
2434 }
2435 if (suffix < m) {
2436 for (int i = start; i <= m; i++) {
2437 if (bmgs_buffers.shift(i) == len) {
2438 bmgs_buffers.shift(i) = suffix - start;
2439 }
2440 if (i == suffix) {
2441 suffix = bmgs_buffers.suffix(suffix);
2442 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002443 }
2444 }
2445}
2446
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002447
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002448template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002449static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002450 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002451 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002452 }
2453 if (sizeof(pchar) == 1) {
2454 if (char_code > String::kMaxAsciiCharCode) {
2455 return -1;
2456 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002457 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002458 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002459 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002460}
2461
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002462
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002463// Restricted simplified Boyer-Moore string matching.
2464// Uses only the bad-shift table of Boyer-Moore and only uses it
2465// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002466template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002467static int BoyerMooreHorspool(Vector<const schar> subject,
2468 Vector<const pchar> pattern,
2469 int start_index,
2470 bool* complete) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002471 ASSERT(algorithm <= BOYER_MOORE_HORSPOOL);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002472 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002473 int m = pattern.length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002474
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002475 int badness = -m;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002476
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002477 // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002478 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002479 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002480 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002481 // Perform search
2482 for (idx = start_index; idx <= n - m;) {
2483 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002484 int c;
2485 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002486 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002487 int shift = j - bc_occ;
2488 idx += shift;
2489 badness += 1 - shift; // at most zero, so badness cannot increase.
2490 if (idx > n - m) {
2491 *complete = true;
2492 return -1;
2493 }
2494 }
2495 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002496 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002497 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002498 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002499 return idx;
2500 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002501 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002502 // Badness increases by the number of characters we have
2503 // checked, and decreases by the number of characters we
2504 // can skip by shifting. It's a measure of how we are doing
2505 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002506 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002507 if (badness > 0) {
2508 *complete = false;
2509 return idx;
2510 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002511 }
2512 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002513 *complete = true;
2514 return -1;
2515}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002516
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002517
2518template <typename schar, typename pchar>
2519static int BoyerMooreIndexOf(Vector<const schar> subject,
2520 Vector<const pchar> pattern,
2521 int idx) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002522 ASSERT(algorithm <= BOYER_MOORE);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002523 int n = subject.length();
2524 int m = pattern.length();
2525 // Only preprocess at most kBMMaxShift last characters of pattern.
2526 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2527
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002528 pchar last_char = pattern[m - 1];
2529 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002530 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002531 int j = m - 1;
2532 schar c;
2533 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002534 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002535 idx += shift;
2536 if (idx > n - m) {
2537 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002538 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002539 }
2540 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2541 if (j < 0) {
2542 return idx;
2543 } else if (j < start) {
2544 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002545 // Fall back on BMH shift.
2546 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002547 } else {
2548 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002549 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002550 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002551 if (gs_shift > shift) {
2552 shift = gs_shift;
2553 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002554 idx += shift;
2555 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002556 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002557
2558 return -1;
2559}
2560
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002561
2562template <typename schar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002563static inline int SingleCharIndexOf(Vector<const schar> string,
2564 schar pattern_char,
2565 int start_index) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002566 if (sizeof(schar) == 1) {
2567 const schar* pos = reinterpret_cast<const schar*>(
2568 memchr(string.start() + start_index,
2569 pattern_char,
2570 string.length() - start_index));
2571 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002572 return static_cast<int>(pos - string.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002573 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002574 for (int i = start_index, n = string.length(); i < n; i++) {
2575 if (pattern_char == string[i]) {
2576 return i;
2577 }
2578 }
2579 return -1;
2580}
2581
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002582
2583template <typename schar>
2584static int SingleCharLastIndexOf(Vector<const schar> string,
2585 schar pattern_char,
2586 int start_index) {
2587 for (int i = start_index; i >= 0; i--) {
2588 if (pattern_char == string[i]) {
2589 return i;
2590 }
2591 }
2592 return -1;
2593}
2594
2595
ager@chromium.org7c537e22008-10-16 08:43:32 +00002596// Trivial string search for shorter strings.
2597// On return, if "complete" is set to true, the return value is the
2598// final result of searching for the patter in the subject.
2599// If "complete" is set to false, the return value is the index where
2600// further checking should start, i.e., it's guaranteed that the pattern
2601// does not occur at a position prior to the returned index.
2602template <typename pchar, typename schar>
2603static int SimpleIndexOf(Vector<const schar> subject,
2604 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002605 int idx,
2606 bool* complete) {
2607 // Badness is a count of how much work we have done. When we have
2608 // done enough work we decide it's probably worth switching to a better
2609 // algorithm.
2610 int badness = -10 - (pattern.length() << 2);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002611
ager@chromium.org7c537e22008-10-16 08:43:32 +00002612 // We know our pattern is at least 2 characters, we cache the first so
2613 // the common case of the first character not matching is faster.
2614 pchar pattern_first_char = pattern[0];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002615 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2616 badness++;
2617 if (badness > 0) {
2618 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002619 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002620 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002621 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2622 const schar* pos = reinterpret_cast<const schar*>(
2623 memchr(subject.start() + i,
2624 pattern_first_char,
2625 n - i + 1));
2626 if (pos == NULL) {
2627 *complete = true;
2628 return -1;
2629 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002630 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002631 } else {
2632 if (subject[i] != pattern_first_char) continue;
2633 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002634 int j = 1;
2635 do {
2636 if (pattern[j] != subject[i+j]) {
2637 break;
2638 }
2639 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002640 } while (j < pattern.length());
2641 if (j == pattern.length()) {
2642 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002643 return i;
2644 }
2645 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002646 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002647 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002648 return -1;
2649}
2650
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002651// Simple indexOf that never bails out. For short patterns only.
2652template <typename pchar, typename schar>
2653static int SimpleIndexOf(Vector<const schar> subject,
2654 Vector<const pchar> pattern,
2655 int idx) {
2656 pchar pattern_first_char = pattern[0];
2657 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002658 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2659 const schar* pos = reinterpret_cast<const schar*>(
2660 memchr(subject.start() + i,
2661 pattern_first_char,
2662 n - i + 1));
2663 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002664 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002665 } else {
2666 if (subject[i] != pattern_first_char) continue;
2667 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002668 int j = 1;
2669 do {
2670 if (pattern[j] != subject[i+j]) {
2671 break;
2672 }
2673 j++;
2674 } while (j < pattern.length());
2675 if (j == pattern.length()) {
2676 return i;
2677 }
2678 }
2679 return -1;
2680}
2681
2682
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002683// Strategy for searching for a string in another string.
2684enum StringSearchStrategy { SEARCH_FAIL, SEARCH_SHORT, SEARCH_LONG };
ager@chromium.org7c537e22008-10-16 08:43:32 +00002685
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002686
2687template <typename pchar>
2688static inline StringSearchStrategy InitializeStringSearch(
2689 Vector<const pchar> pat, bool ascii_subject) {
2690 ASSERT(pat.length() > 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002691 // We have an ASCII haystack and a non-ASCII needle. Check if there
2692 // really is a non-ASCII character in the needle and bail out if there
2693 // is.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002694 if (ascii_subject && sizeof(pchar) > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002695 for (int i = 0; i < pat.length(); i++) {
2696 uc16 c = pat[i];
2697 if (c > String::kMaxAsciiCharCode) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002698 return SEARCH_FAIL;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002699 }
2700 }
2701 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002702 if (pat.length() < kBMMinPatternLength) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002703 return SEARCH_SHORT;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002704 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002705 algorithm = SIMPLE_SEARCH;
2706 return SEARCH_LONG;
2707}
2708
2709
2710// Dispatch long needle searches to different algorithms.
2711template <typename schar, typename pchar>
2712static int ComplexIndexOf(Vector<const schar> sub,
2713 Vector<const pchar> pat,
2714 int start_index) {
2715 ASSERT(pat.length() >= kBMMinPatternLength);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002716 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002717 bool complete;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002718 int idx = start_index;
2719 switch (algorithm) {
2720 case SIMPLE_SEARCH:
2721 idx = SimpleIndexOf(sub, pat, idx, &complete);
2722 if (complete) return idx;
2723 BoyerMoorePopulateBadCharTable(pat);
2724 algorithm = BOYER_MOORE_HORSPOOL;
2725 // FALLTHROUGH.
2726 case BOYER_MOORE_HORSPOOL:
2727 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
2728 if (complete) return idx;
2729 // Build the Good Suffix table and continue searching.
2730 BoyerMoorePopulateGoodSuffixTable(pat);
2731 algorithm = BOYER_MOORE;
2732 // FALLTHROUGH.
2733 case BOYER_MOORE:
2734 return BoyerMooreIndexOf(sub, pat, idx);
2735 }
2736 UNREACHABLE();
2737 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002738}
2739
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002740
2741// Dispatch to different search strategies for a single search.
2742// If searching multiple times on the same needle, the search
2743// strategy should only be computed once and then dispatch to different
2744// loops.
2745template <typename schar, typename pchar>
2746static int StringSearch(Vector<const schar> sub,
2747 Vector<const pchar> pat,
2748 int start_index) {
2749 bool ascii_subject = (sizeof(schar) == 1);
2750 StringSearchStrategy strategy = InitializeStringSearch(pat, ascii_subject);
2751 switch (strategy) {
2752 case SEARCH_FAIL: return -1;
2753 case SEARCH_SHORT: return SimpleIndexOf(sub, pat, start_index);
2754 case SEARCH_LONG: return ComplexIndexOf(sub, pat, start_index);
2755 }
2756 UNREACHABLE();
2757 return -1;
2758}
2759
2760
ager@chromium.org7c537e22008-10-16 08:43:32 +00002761// Perform string match of pattern on subject, starting at start index.
2762// Caller must ensure that 0 <= start_index <= sub->length(),
2763// and should check that pat->length() + start_index <= sub->length()
2764int Runtime::StringMatch(Handle<String> sub,
2765 Handle<String> pat,
2766 int start_index) {
2767 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002768 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002769
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002770 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002771 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002772
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002773 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002774 if (start_index + pattern_length > subject_length) return -1;
2775
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002776 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002777 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002778 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002779
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002780 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002781 // character patterns linear search is necessary, so any smart
2782 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002783 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002784 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002785 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002786 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002787 if (pchar > String::kMaxAsciiCharCode) {
2788 return -1;
2789 }
2790 Vector<const char> ascii_vector =
2791 sub->ToAsciiVector().SubVector(start_index, subject_length);
2792 const void* pos = memchr(ascii_vector.start(),
2793 static_cast<const char>(pchar),
2794 static_cast<size_t>(ascii_vector.length()));
2795 if (pos == NULL) {
2796 return -1;
2797 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002798 return static_cast<int>(reinterpret_cast<const char*>(pos)
2799 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002800 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002801 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002802 }
2803
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002804 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002805 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002806 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002807
ager@chromium.org7c537e22008-10-16 08:43:32 +00002808 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2809 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002810 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002811 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002812 if (sub->IsAsciiRepresentation()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002813 return StringSearch(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002814 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002815 return StringSearch(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002816 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002817 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002818 if (sub->IsAsciiRepresentation()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002819 return StringSearch(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002820 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002821 return StringSearch(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002822}
2823
2824
2825static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002826 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002827 ASSERT(args.length() == 3);
2828
ager@chromium.org7c537e22008-10-16 08:43:32 +00002829 CONVERT_ARG_CHECKED(String, sub, 0);
2830 CONVERT_ARG_CHECKED(String, pat, 1);
2831
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002832 Object* index = args[2];
2833 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002834 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002835
ager@chromium.org870a0b62008-11-04 11:43:05 +00002836 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002837 int position = Runtime::StringMatch(sub, pat, start_index);
2838 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002839}
2840
2841
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002842template <typename schar, typename pchar>
2843static int StringMatchBackwards(Vector<const schar> sub,
2844 Vector<const pchar> pat,
2845 int idx) {
2846 ASSERT(pat.length() >= 1);
2847 ASSERT(idx + pat.length() <= sub.length());
2848
2849 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
2850 for (int i = 0; i < pat.length(); i++) {
2851 uc16 c = pat[i];
2852 if (c > String::kMaxAsciiCharCode) {
2853 return -1;
2854 }
2855 }
2856 }
2857
2858 pchar pattern_first_char = pat[0];
2859 for (int i = idx; i >= 0; i--) {
2860 if (sub[i] != pattern_first_char) continue;
2861 int j = 1;
2862 while (j < pat.length()) {
2863 if (pat[j] != sub[i+j]) {
2864 break;
2865 }
2866 j++;
2867 }
2868 if (j == pat.length()) {
2869 return i;
2870 }
2871 }
2872 return -1;
2873}
2874
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002875static Object* Runtime_StringLastIndexOf(Arguments args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002876 HandleScope scope; // create a new handle scope
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002877 ASSERT(args.length() == 3);
2878
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002879 CONVERT_ARG_CHECKED(String, sub, 0);
2880 CONVERT_ARG_CHECKED(String, pat, 1);
2881
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002882 Object* index = args[2];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002883 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002884 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002885
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002886 uint32_t pat_length = pat->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002887 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002888
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002889 if (start_index + pat_length > sub_length) {
2890 start_index = sub_length - pat_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002891 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002892
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002893 if (pat_length == 0) {
2894 return Smi::FromInt(start_index);
2895 }
2896
2897 if (!sub->IsFlat()) {
2898 FlattenString(sub);
2899 }
2900
2901 if (pat_length == 1) {
2902 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2903 if (sub->IsAsciiRepresentation()) {
2904 uc16 pchar = pat->Get(0);
2905 if (pchar > String::kMaxAsciiCharCode) {
2906 return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002907 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002908 return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
2909 static_cast<char>(pat->Get(0)),
2910 start_index));
2911 } else {
2912 return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
2913 pat->Get(0),
2914 start_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002915 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002916 }
2917
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002918 if (!pat->IsFlat()) {
2919 FlattenString(pat);
2920 }
2921
2922 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2923
2924 int position = -1;
2925
2926 if (pat->IsAsciiRepresentation()) {
2927 Vector<const char> pat_vector = pat->ToAsciiVector();
2928 if (sub->IsAsciiRepresentation()) {
2929 position = StringMatchBackwards(sub->ToAsciiVector(),
2930 pat_vector,
2931 start_index);
2932 } else {
2933 position = StringMatchBackwards(sub->ToUC16Vector(),
2934 pat_vector,
2935 start_index);
2936 }
2937 } else {
2938 Vector<const uc16> pat_vector = pat->ToUC16Vector();
2939 if (sub->IsAsciiRepresentation()) {
2940 position = StringMatchBackwards(sub->ToAsciiVector(),
2941 pat_vector,
2942 start_index);
2943 } else {
2944 position = StringMatchBackwards(sub->ToUC16Vector(),
2945 pat_vector,
2946 start_index);
2947 }
2948 }
2949
2950 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002951}
2952
2953
2954static Object* Runtime_StringLocaleCompare(Arguments args) {
2955 NoHandleAllocation ha;
2956 ASSERT(args.length() == 2);
2957
2958 CONVERT_CHECKED(String, str1, args[0]);
2959 CONVERT_CHECKED(String, str2, args[1]);
2960
2961 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002962 int str1_length = str1->length();
2963 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002964
2965 // Decide trivial cases without flattening.
2966 if (str1_length == 0) {
2967 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2968 return Smi::FromInt(-str2_length);
2969 } else {
2970 if (str2_length == 0) return Smi::FromInt(str1_length);
2971 }
2972
2973 int end = str1_length < str2_length ? str1_length : str2_length;
2974
2975 // No need to flatten if we are going to find the answer on the first
2976 // character. At this point we know there is at least one character
2977 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002978 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002979 if (d != 0) return Smi::FromInt(d);
2980
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002981 str1->TryFlatten();
2982 str2->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002983
2984 static StringInputBuffer buf1;
2985 static StringInputBuffer buf2;
2986
2987 buf1.Reset(str1);
2988 buf2.Reset(str2);
2989
2990 for (int i = 0; i < end; i++) {
2991 uint16_t char1 = buf1.GetNext();
2992 uint16_t char2 = buf2.GetNext();
2993 if (char1 != char2) return Smi::FromInt(char1 - char2);
2994 }
2995
2996 return Smi::FromInt(str1_length - str2_length);
2997}
2998
2999
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003000static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003001 NoHandleAllocation ha;
3002 ASSERT(args.length() == 3);
3003
3004 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00003005 Object* from = args[1];
3006 Object* to = args[2];
3007 int start, end;
3008 // We have a fast integer-only case here to avoid a conversion to double in
3009 // the common case where from and to are Smis.
3010 if (from->IsSmi() && to->IsSmi()) {
3011 start = Smi::cast(from)->value();
3012 end = Smi::cast(to)->value();
3013 } else {
3014 CONVERT_DOUBLE_CHECKED(from_number, from);
3015 CONVERT_DOUBLE_CHECKED(to_number, to);
3016 start = FastD2I(from_number);
3017 end = FastD2I(to_number);
3018 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003019 RUNTIME_ASSERT(end >= start);
3020 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003021 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003022 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003023 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003024}
3025
3026
ager@chromium.org41826e72009-03-30 13:30:57 +00003027static Object* Runtime_StringMatch(Arguments args) {
3028 ASSERT_EQ(3, args.length());
3029
3030 CONVERT_ARG_CHECKED(String, subject, 0);
3031 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
3032 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
3033 HandleScope handles;
3034
3035 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
3036
3037 if (match.is_null()) {
3038 return Failure::Exception();
3039 }
3040 if (match->IsNull()) {
3041 return Heap::null_value();
3042 }
3043 int length = subject->length();
3044
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00003045 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00003046 ZoneList<int> offsets(8);
3047 do {
3048 int start;
3049 int end;
3050 {
3051 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003052 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00003053 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
3054 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
3055 }
3056 offsets.Add(start);
3057 offsets.Add(end);
3058 int index = start < end ? end : end + 1;
3059 if (index > length) break;
3060 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
3061 if (match.is_null()) {
3062 return Failure::Exception();
3063 }
3064 } while (!match->IsNull());
3065 int matches = offsets.length() / 2;
3066 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
3067 for (int i = 0; i < matches ; i++) {
3068 int from = offsets.at(i * 2);
3069 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003070 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00003071 }
3072 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
3073 result->set_length(Smi::FromInt(matches));
3074 return *result;
3075}
3076
3077
lrn@chromium.org25156de2010-04-06 13:10:27 +00003078// Two smis before and after the match, for very long strings.
3079const int kMaxBuilderEntriesPerRegExpMatch = 5;
3080
3081
3082static void SetLastMatchInfoNoCaptures(Handle<String> subject,
3083 Handle<JSArray> last_match_info,
3084 int match_start,
3085 int match_end) {
3086 // Fill last_match_info with a single capture.
3087 last_match_info->EnsureSize(2 + RegExpImpl::kLastMatchOverhead);
3088 AssertNoAllocation no_gc;
3089 FixedArray* elements = FixedArray::cast(last_match_info->elements());
3090 RegExpImpl::SetLastCaptureCount(elements, 2);
3091 RegExpImpl::SetLastInput(elements, *subject);
3092 RegExpImpl::SetLastSubject(elements, *subject);
3093 RegExpImpl::SetCapture(elements, 0, match_start);
3094 RegExpImpl::SetCapture(elements, 1, match_end);
3095}
3096
3097
3098template <typename schar>
3099static bool SearchCharMultiple(Vector<schar> subject,
3100 String* pattern,
3101 schar pattern_char,
3102 FixedArrayBuilder* builder,
3103 int* match_pos) {
3104 // Position of last match.
3105 int pos = *match_pos;
3106 int subject_length = subject.length();
3107 while (pos < subject_length) {
3108 int match_end = pos + 1;
3109 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3110 *match_pos = pos;
3111 return false;
3112 }
3113 int new_pos = SingleCharIndexOf(subject, pattern_char, match_end);
3114 if (new_pos >= 0) {
3115 // Match has been found.
3116 if (new_pos > match_end) {
3117 ReplacementStringBuilder::AddSubjectSlice(builder, match_end, new_pos);
3118 }
3119 pos = new_pos;
3120 builder->Add(pattern);
3121 } else {
3122 break;
3123 }
3124 }
3125 if (pos + 1 < subject_length) {
3126 ReplacementStringBuilder::AddSubjectSlice(builder, pos + 1, subject_length);
3127 }
3128 *match_pos = pos;
3129 return true;
3130}
3131
3132
3133static bool SearchCharMultiple(Handle<String> subject,
3134 Handle<String> pattern,
3135 Handle<JSArray> last_match_info,
3136 FixedArrayBuilder* builder) {
3137 ASSERT(subject->IsFlat());
3138 ASSERT_EQ(1, pattern->length());
3139 uc16 pattern_char = pattern->Get(0);
3140 // Treating position before first as initial "previous match position".
3141 int match_pos = -1;
3142
3143 for (;;) { // Break when search complete.
3144 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3145 AssertNoAllocation no_gc;
3146 if (subject->IsAsciiRepresentation()) {
3147 if (pattern_char > String::kMaxAsciiCharCode) {
3148 break;
3149 }
3150 Vector<const char> subject_vector = subject->ToAsciiVector();
3151 char pattern_ascii_char = static_cast<char>(pattern_char);
3152 bool complete = SearchCharMultiple<const char>(subject_vector,
3153 *pattern,
3154 pattern_ascii_char,
3155 builder,
3156 &match_pos);
3157 if (complete) break;
3158 } else {
3159 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3160 bool complete = SearchCharMultiple<const uc16>(subject_vector,
3161 *pattern,
3162 pattern_char,
3163 builder,
3164 &match_pos);
3165 if (complete) break;
3166 }
3167 }
3168
3169 if (match_pos >= 0) {
3170 SetLastMatchInfoNoCaptures(subject,
3171 last_match_info,
3172 match_pos,
3173 match_pos + 1);
3174 return true;
3175 }
3176 return false; // No matches at all.
3177}
3178
3179
3180template <typename schar, typename pchar>
3181static bool SearchStringMultiple(Vector<schar> subject,
3182 String* pattern,
3183 Vector<pchar> pattern_string,
3184 FixedArrayBuilder* builder,
3185 int* match_pos) {
3186 int pos = *match_pos;
3187 int subject_length = subject.length();
3188 int pattern_length = pattern_string.length();
3189 int max_search_start = subject_length - pattern_length;
3190 bool is_ascii = (sizeof(schar) == 1);
3191 StringSearchStrategy strategy =
3192 InitializeStringSearch(pattern_string, is_ascii);
3193 switch (strategy) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003194 case SEARCH_FAIL: break;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003195 case SEARCH_SHORT:
3196 while (pos <= max_search_start) {
3197 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3198 *match_pos = pos;
3199 return false;
3200 }
3201 // Position of end of previous match.
3202 int match_end = pos + pattern_length;
3203 int new_pos = SimpleIndexOf(subject, pattern_string, match_end);
3204 if (new_pos >= 0) {
3205 // A match.
3206 if (new_pos > match_end) {
3207 ReplacementStringBuilder::AddSubjectSlice(builder,
3208 match_end,
3209 new_pos);
3210 }
3211 pos = new_pos;
3212 builder->Add(pattern);
3213 } else {
3214 break;
3215 }
3216 }
3217 break;
3218 case SEARCH_LONG:
3219 while (pos <= max_search_start) {
3220 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003221 *match_pos = pos;
3222 return false;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003223 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003224 int match_end = pos + pattern_length;
3225 int new_pos = ComplexIndexOf(subject, pattern_string, match_end);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003226 if (new_pos >= 0) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003227 // A match has been found.
3228 if (new_pos > match_end) {
3229 ReplacementStringBuilder::AddSubjectSlice(builder,
3230 match_end,
3231 new_pos);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003232 }
3233 pos = new_pos;
3234 builder->Add(pattern);
3235 } else {
3236 break;
3237 }
3238 }
3239 break;
3240 }
3241 if (pos < max_search_start) {
3242 ReplacementStringBuilder::AddSubjectSlice(builder,
3243 pos + pattern_length,
3244 subject_length);
3245 }
3246 *match_pos = pos;
3247 return true;
3248}
3249
3250
3251static bool SearchStringMultiple(Handle<String> subject,
3252 Handle<String> pattern,
3253 Handle<JSArray> last_match_info,
3254 FixedArrayBuilder* builder) {
3255 ASSERT(subject->IsFlat());
3256 ASSERT(pattern->IsFlat());
3257 ASSERT(pattern->length() > 1);
3258
3259 // Treating as if a previous match was before first character.
3260 int match_pos = -pattern->length();
3261
3262 for (;;) { // Break when search complete.
3263 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3264 AssertNoAllocation no_gc;
3265 if (subject->IsAsciiRepresentation()) {
3266 Vector<const char> subject_vector = subject->ToAsciiVector();
3267 if (pattern->IsAsciiRepresentation()) {
3268 if (SearchStringMultiple(subject_vector,
3269 *pattern,
3270 pattern->ToAsciiVector(),
3271 builder,
3272 &match_pos)) break;
3273 } else {
3274 if (SearchStringMultiple(subject_vector,
3275 *pattern,
3276 pattern->ToUC16Vector(),
3277 builder,
3278 &match_pos)) break;
3279 }
3280 } else {
3281 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3282 if (pattern->IsAsciiRepresentation()) {
3283 if (SearchStringMultiple(subject_vector,
3284 *pattern,
3285 pattern->ToAsciiVector(),
3286 builder,
3287 &match_pos)) break;
3288 } else {
3289 if (SearchStringMultiple(subject_vector,
3290 *pattern,
3291 pattern->ToUC16Vector(),
3292 builder,
3293 &match_pos)) break;
3294 }
3295 }
3296 }
3297
3298 if (match_pos >= 0) {
3299 SetLastMatchInfoNoCaptures(subject,
3300 last_match_info,
3301 match_pos,
3302 match_pos + pattern->length());
3303 return true;
3304 }
3305 return false; // No matches at all.
3306}
3307
3308
3309static RegExpImpl::IrregexpResult SearchRegExpNoCaptureMultiple(
3310 Handle<String> subject,
3311 Handle<JSRegExp> regexp,
3312 Handle<JSArray> last_match_array,
3313 FixedArrayBuilder* builder) {
3314 ASSERT(subject->IsFlat());
3315 int match_start = -1;
3316 int match_end = 0;
3317 int pos = 0;
3318 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3319 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3320
3321 OffsetsVector registers(required_registers);
3322 Vector<int> register_vector(registers.vector(), registers.length());
3323 int subject_length = subject->length();
3324
3325 for (;;) { // Break on failure, return on exception.
3326 RegExpImpl::IrregexpResult result =
3327 RegExpImpl::IrregexpExecOnce(regexp,
3328 subject,
3329 pos,
3330 register_vector);
3331 if (result == RegExpImpl::RE_SUCCESS) {
3332 match_start = register_vector[0];
3333 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3334 if (match_end < match_start) {
3335 ReplacementStringBuilder::AddSubjectSlice(builder,
3336 match_end,
3337 match_start);
3338 }
3339 match_end = register_vector[1];
3340 HandleScope loop_scope;
3341 builder->Add(*Factory::NewSubString(subject, match_start, match_end));
3342 if (match_start != match_end) {
3343 pos = match_end;
3344 } else {
3345 pos = match_end + 1;
3346 if (pos > subject_length) break;
3347 }
3348 } else if (result == RegExpImpl::RE_FAILURE) {
3349 break;
3350 } else {
3351 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3352 return result;
3353 }
3354 }
3355
3356 if (match_start >= 0) {
3357 if (match_end < subject_length) {
3358 ReplacementStringBuilder::AddSubjectSlice(builder,
3359 match_end,
3360 subject_length);
3361 }
3362 SetLastMatchInfoNoCaptures(subject,
3363 last_match_array,
3364 match_start,
3365 match_end);
3366 return RegExpImpl::RE_SUCCESS;
3367 } else {
3368 return RegExpImpl::RE_FAILURE; // No matches at all.
3369 }
3370}
3371
3372
3373static RegExpImpl::IrregexpResult SearchRegExpMultiple(
3374 Handle<String> subject,
3375 Handle<JSRegExp> regexp,
3376 Handle<JSArray> last_match_array,
3377 FixedArrayBuilder* builder) {
3378
3379 ASSERT(subject->IsFlat());
3380 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3381 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3382
3383 OffsetsVector registers(required_registers);
3384 Vector<int> register_vector(registers.vector(), registers.length());
3385
3386 RegExpImpl::IrregexpResult result =
3387 RegExpImpl::IrregexpExecOnce(regexp,
3388 subject,
3389 0,
3390 register_vector);
3391
3392 int capture_count = regexp->CaptureCount();
3393 int subject_length = subject->length();
3394
3395 // Position to search from.
3396 int pos = 0;
3397 // End of previous match. Differs from pos if match was empty.
3398 int match_end = 0;
3399 if (result == RegExpImpl::RE_SUCCESS) {
3400 // Need to keep a copy of the previous match for creating last_match_info
3401 // at the end, so we have two vectors that we swap between.
3402 OffsetsVector registers2(required_registers);
3403 Vector<int> prev_register_vector(registers2.vector(), registers2.length());
3404
3405 do {
3406 int match_start = register_vector[0];
3407 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3408 if (match_end < match_start) {
3409 ReplacementStringBuilder::AddSubjectSlice(builder,
3410 match_end,
3411 match_start);
3412 }
3413 match_end = register_vector[1];
3414
3415 {
3416 // Avoid accumulating new handles inside loop.
3417 HandleScope temp_scope;
3418 // Arguments array to replace function is match, captures, index and
3419 // subject, i.e., 3 + capture count in total.
3420 Handle<FixedArray> elements = Factory::NewFixedArray(3 + capture_count);
3421 elements->set(0, *Factory::NewSubString(subject,
3422 match_start,
3423 match_end));
3424 for (int i = 1; i <= capture_count; i++) {
3425 int start = register_vector[i * 2];
3426 if (start >= 0) {
3427 int end = register_vector[i * 2 + 1];
3428 ASSERT(start <= end);
3429 Handle<String> substring = Factory::NewSubString(subject,
3430 start,
3431 end);
3432 elements->set(i, *substring);
3433 } else {
3434 ASSERT(register_vector[i * 2 + 1] < 0);
3435 elements->set(i, Heap::undefined_value());
3436 }
3437 }
3438 elements->set(capture_count + 1, Smi::FromInt(match_start));
3439 elements->set(capture_count + 2, *subject);
3440 builder->Add(*Factory::NewJSArrayWithElements(elements));
3441 }
3442 // Swap register vectors, so the last successful match is in
3443 // prev_register_vector.
3444 Vector<int> tmp = prev_register_vector;
3445 prev_register_vector = register_vector;
3446 register_vector = tmp;
3447
3448 if (match_end > match_start) {
3449 pos = match_end;
3450 } else {
3451 pos = match_end + 1;
3452 if (pos > subject_length) {
3453 break;
3454 }
3455 }
3456
3457 result = RegExpImpl::IrregexpExecOnce(regexp,
3458 subject,
3459 pos,
3460 register_vector);
3461 } while (result == RegExpImpl::RE_SUCCESS);
3462
3463 if (result != RegExpImpl::RE_EXCEPTION) {
3464 // Finished matching, with at least one match.
3465 if (match_end < subject_length) {
3466 ReplacementStringBuilder::AddSubjectSlice(builder,
3467 match_end,
3468 subject_length);
3469 }
3470
3471 int last_match_capture_count = (capture_count + 1) * 2;
3472 int last_match_array_size =
3473 last_match_capture_count + RegExpImpl::kLastMatchOverhead;
3474 last_match_array->EnsureSize(last_match_array_size);
3475 AssertNoAllocation no_gc;
3476 FixedArray* elements = FixedArray::cast(last_match_array->elements());
3477 RegExpImpl::SetLastCaptureCount(elements, last_match_capture_count);
3478 RegExpImpl::SetLastSubject(elements, *subject);
3479 RegExpImpl::SetLastInput(elements, *subject);
3480 for (int i = 0; i < last_match_capture_count; i++) {
3481 RegExpImpl::SetCapture(elements, i, prev_register_vector[i]);
3482 }
3483 return RegExpImpl::RE_SUCCESS;
3484 }
3485 }
3486 // No matches at all, return failure or exception result directly.
3487 return result;
3488}
3489
3490
3491static Object* Runtime_RegExpExecMultiple(Arguments args) {
3492 ASSERT(args.length() == 4);
3493 HandleScope handles;
3494
3495 CONVERT_ARG_CHECKED(String, subject, 1);
3496 if (!subject->IsFlat()) { FlattenString(subject); }
3497 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
3498 CONVERT_ARG_CHECKED(JSArray, last_match_info, 2);
3499 CONVERT_ARG_CHECKED(JSArray, result_array, 3);
3500
3501 ASSERT(last_match_info->HasFastElements());
3502 ASSERT(regexp->GetFlags().is_global());
3503 Handle<FixedArray> result_elements;
3504 if (result_array->HasFastElements()) {
3505 result_elements =
3506 Handle<FixedArray>(FixedArray::cast(result_array->elements()));
3507 } else {
3508 result_elements = Factory::NewFixedArrayWithHoles(16);
3509 }
3510 FixedArrayBuilder builder(result_elements);
3511
3512 if (regexp->TypeTag() == JSRegExp::ATOM) {
3513 Handle<String> pattern(
3514 String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex)));
3515 int pattern_length = pattern->length();
3516 if (pattern_length == 1) {
3517 if (SearchCharMultiple(subject, pattern, last_match_info, &builder)) {
3518 return *builder.ToJSArray(result_array);
3519 }
3520 return Heap::null_value();
3521 }
3522
3523 if (!pattern->IsFlat()) FlattenString(pattern);
3524 if (SearchStringMultiple(subject, pattern, last_match_info, &builder)) {
3525 return *builder.ToJSArray(result_array);
3526 }
3527 return Heap::null_value();
3528 }
3529
3530 ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
3531
3532 RegExpImpl::IrregexpResult result;
3533 if (regexp->CaptureCount() == 0) {
3534 result = SearchRegExpNoCaptureMultiple(subject,
3535 regexp,
3536 last_match_info,
3537 &builder);
3538 } else {
3539 result = SearchRegExpMultiple(subject, regexp, last_match_info, &builder);
3540 }
3541 if (result == RegExpImpl::RE_SUCCESS) return *builder.ToJSArray(result_array);
3542 if (result == RegExpImpl::RE_FAILURE) return Heap::null_value();
3543 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3544 return Failure::Exception();
3545}
3546
3547
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003548static Object* Runtime_NumberToRadixString(Arguments args) {
3549 NoHandleAllocation ha;
3550 ASSERT(args.length() == 2);
3551
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003552 // Fast case where the result is a one character string.
3553 if (args[0]->IsSmi() && args[1]->IsSmi()) {
3554 int value = Smi::cast(args[0])->value();
3555 int radix = Smi::cast(args[1])->value();
3556 if (value >= 0 && value < radix) {
3557 RUNTIME_ASSERT(radix <= 36);
3558 // Character array used for conversion.
3559 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
3560 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
3561 }
3562 }
3563
3564 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003565 CONVERT_DOUBLE_CHECKED(value, args[0]);
3566 if (isnan(value)) {
3567 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3568 }
3569 if (isinf(value)) {
3570 if (value < 0) {
3571 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3572 }
3573 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3574 }
3575 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
3576 int radix = FastD2I(radix_number);
3577 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3578 char* str = DoubleToRadixCString(value, radix);
3579 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3580 DeleteArray(str);
3581 return result;
3582}
3583
3584
3585static Object* Runtime_NumberToFixed(Arguments args) {
3586 NoHandleAllocation ha;
3587 ASSERT(args.length() == 2);
3588
3589 CONVERT_DOUBLE_CHECKED(value, args[0]);
3590 if (isnan(value)) {
3591 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3592 }
3593 if (isinf(value)) {
3594 if (value < 0) {
3595 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3596 }
3597 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3598 }
3599 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3600 int f = FastD2I(f_number);
3601 RUNTIME_ASSERT(f >= 0);
3602 char* str = DoubleToFixedCString(value, f);
3603 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3604 DeleteArray(str);
3605 return res;
3606}
3607
3608
3609static Object* Runtime_NumberToExponential(Arguments args) {
3610 NoHandleAllocation ha;
3611 ASSERT(args.length() == 2);
3612
3613 CONVERT_DOUBLE_CHECKED(value, args[0]);
3614 if (isnan(value)) {
3615 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3616 }
3617 if (isinf(value)) {
3618 if (value < 0) {
3619 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3620 }
3621 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3622 }
3623 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3624 int f = FastD2I(f_number);
3625 RUNTIME_ASSERT(f >= -1 && f <= 20);
3626 char* str = DoubleToExponentialCString(value, f);
3627 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3628 DeleteArray(str);
3629 return res;
3630}
3631
3632
3633static Object* Runtime_NumberToPrecision(Arguments args) {
3634 NoHandleAllocation ha;
3635 ASSERT(args.length() == 2);
3636
3637 CONVERT_DOUBLE_CHECKED(value, args[0]);
3638 if (isnan(value)) {
3639 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3640 }
3641 if (isinf(value)) {
3642 if (value < 0) {
3643 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3644 }
3645 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3646 }
3647 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3648 int f = FastD2I(f_number);
3649 RUNTIME_ASSERT(f >= 1 && f <= 21);
3650 char* str = DoubleToPrecisionCString(value, f);
3651 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3652 DeleteArray(str);
3653 return res;
3654}
3655
3656
3657// Returns a single character string where first character equals
3658// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003659static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003660 if (index < static_cast<uint32_t>(string->length())) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003661 string->TryFlatten();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003662 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003663 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003664 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003665 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003666}
3667
3668
3669Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
3670 // Handle [] indexing on Strings
3671 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003672 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
3673 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003674 }
3675
3676 // Handle [] indexing on String objects
3677 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003678 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
3679 Handle<Object> result =
3680 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
3681 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003682 }
3683
3684 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003685 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003686 return prototype->GetElement(index);
3687 }
3688
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003689 return GetElement(object, index);
3690}
3691
3692
3693Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003694 return object->GetElement(index);
3695}
3696
3697
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003698Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
3699 HandleScope scope;
3700
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003701 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003702 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003703 Handle<Object> error =
3704 Factory::NewTypeError("non_object_property_load",
3705 HandleVector(args, 2));
3706 return Top::Throw(*error);
3707 }
3708
3709 // Check if the given key is an array index.
3710 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003711 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003712 return GetElementOrCharAt(object, index);
3713 }
3714
3715 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003716 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003717 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003718 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003719 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003720 bool has_pending_exception = false;
3721 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003722 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003723 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003724 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003725 }
3726
ager@chromium.org32912102009-01-16 10:38:43 +00003727 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003728 // the element if so.
3729 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003730 return GetElementOrCharAt(object, index);
3731 } else {
3732 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003733 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003734 }
3735}
3736
3737
3738static Object* Runtime_GetProperty(Arguments args) {
3739 NoHandleAllocation ha;
3740 ASSERT(args.length() == 2);
3741
3742 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003743 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003744
3745 return Runtime::GetObjectProperty(object, key);
3746}
3747
3748
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003749// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003750static Object* Runtime_KeyedGetProperty(Arguments args) {
3751 NoHandleAllocation ha;
3752 ASSERT(args.length() == 2);
3753
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003754 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00003755 // itself.
3756 //
3757 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00003758 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00003759 // global proxy object never has properties. This is the case
3760 // because the global proxy object forwards everything to its hidden
3761 // prototype including local lookups.
3762 //
3763 // Additionally, we need to make sure that we do not cache results
3764 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003765 if (args[0]->IsJSObject() &&
3766 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00003767 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003768 args[1]->IsString()) {
3769 JSObject* receiver = JSObject::cast(args[0]);
3770 String* key = String::cast(args[1]);
3771 if (receiver->HasFastProperties()) {
3772 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003773 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003774 int offset = KeyedLookupCache::Lookup(receiver_map, key);
3775 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003776 Object* value = receiver->FastPropertyAt(offset);
3777 return value->IsTheHole() ? Heap::undefined_value() : value;
3778 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003779 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003780 LookupResult result;
3781 receiver->LocalLookup(key, &result);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00003782 if (result.IsProperty() && result.type() == FIELD) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003783 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003784 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003785 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003786 }
3787 } else {
3788 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003789 StringDictionary* dictionary = receiver->property_dictionary();
3790 int entry = dictionary->FindEntry(key);
3791 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003792 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003793 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003794 if (!receiver->IsGlobalObject()) return value;
3795 value = JSGlobalPropertyCell::cast(value)->value();
3796 if (!value->IsTheHole()) return value;
3797 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003798 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003799 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003800 } else if (args[0]->IsString() && args[1]->IsSmi()) {
3801 // Fast case for string indexing using [] with a smi index.
3802 HandleScope scope;
3803 Handle<String> str = args.at<String>(0);
3804 int index = Smi::cast(args[1])->value();
3805 Handle<Object> result = GetCharAt(str, index);
3806 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003807 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003808
3809 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003810 return Runtime::GetObjectProperty(args.at<Object>(0),
3811 args.at<Object>(1));
3812}
3813
3814
ager@chromium.org5c838252010-02-19 08:53:10 +00003815static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
3816 ASSERT(args.length() == 5);
3817 HandleScope scope;
3818 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3819 CONVERT_CHECKED(String, name, args[1]);
3820 CONVERT_CHECKED(Smi, flag_setter, args[2]);
3821 CONVERT_CHECKED(JSFunction, fun, args[3]);
3822 CONVERT_CHECKED(Smi, flag_attr, args[4]);
3823 int unchecked = flag_attr->value();
3824 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3825 RUNTIME_ASSERT(!obj->IsNull());
3826 LookupResult result;
3827 obj->LocalLookupRealNamedProperty(name, &result);
3828
3829 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3830 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
3831 // delete it to avoid running into trouble in DefineAccessor, which
3832 // handles this incorrectly if the property is readonly (does nothing)
3833 if (result.IsProperty() &&
3834 (result.type() == FIELD || result.type() == NORMAL
3835 || result.type() == CONSTANT_FUNCTION)) {
3836 obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
3837 }
3838 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
3839}
3840
3841static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
3842 ASSERT(args.length() == 4);
3843 HandleScope scope;
3844 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
3845 CONVERT_ARG_CHECKED(String, name, 1);
3846 Handle<Object> obj_value = args.at<Object>(2);
3847
3848 CONVERT_CHECKED(Smi, flag, args[3]);
3849 int unchecked = flag->value();
3850 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3851
3852 LookupResult result;
3853 js_object->LocalLookupRealNamedProperty(*name, &result);
3854
3855 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3856
3857 // Take special care when attributes are different and there is already
3858 // a property. For simplicity we normalize the property which enables us
3859 // to not worry about changing the instance_descriptor and creating a new
3860 // map. The current version of SetObjectProperty does not handle attributes
3861 // correctly in the case where a property is a field and is reset with
3862 // new attributes.
3863 if (result.IsProperty() && attr != result.GetAttributes()) {
3864 // New attributes - normalize to avoid writing to instance descriptor
3865 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3866 // Use IgnoreAttributes version since a readonly property may be
3867 // overridden and SetProperty does not allow this.
3868 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
3869 *obj_value,
3870 attr);
3871 }
3872 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
3873}
3874
3875
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003876Object* Runtime::SetObjectProperty(Handle<Object> object,
3877 Handle<Object> key,
3878 Handle<Object> value,
3879 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003880 HandleScope scope;
3881
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003882 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003883 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003884 Handle<Object> error =
3885 Factory::NewTypeError("non_object_property_store",
3886 HandleVector(args, 2));
3887 return Top::Throw(*error);
3888 }
3889
3890 // If the object isn't a JavaScript object, we ignore the store.
3891 if (!object->IsJSObject()) return *value;
3892
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003893 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3894
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003895 // Check if the given key is an array index.
3896 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003897 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003898 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3899 // of a string using [] notation. We need to support this too in
3900 // JavaScript.
3901 // In the case of a String object we just need to redirect the assignment to
3902 // the underlying string if the index is in range. Since the underlying
3903 // string does nothing with the assignment then we can ignore such
3904 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003905 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003906 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003907 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003908
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003909 Handle<Object> result = SetElement(js_object, index, value);
3910 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003911 return *value;
3912 }
3913
3914 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003915 Handle<Object> result;
3916 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003917 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003918 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003919 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003920 key_string->TryFlatten();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003921 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003922 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003923 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003924 return *value;
3925 }
3926
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003927 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003928 bool has_pending_exception = false;
3929 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3930 if (has_pending_exception) return Failure::Exception();
3931 Handle<String> name = Handle<String>::cast(converted);
3932
3933 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003934 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003935 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003936 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003937 }
3938}
3939
3940
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003941Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
3942 Handle<Object> key,
3943 Handle<Object> value,
3944 PropertyAttributes attr) {
3945 HandleScope scope;
3946
3947 // Check if the given key is an array index.
3948 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003949 if (key->ToArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003950 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3951 // of a string using [] notation. We need to support this too in
3952 // JavaScript.
3953 // In the case of a String object we just need to redirect the assignment to
3954 // the underlying string if the index is in range. Since the underlying
3955 // string does nothing with the assignment then we can ignore such
3956 // assignments.
3957 if (js_object->IsStringObjectWithCharacterAt(index)) {
3958 return *value;
3959 }
3960
3961 return js_object->SetElement(index, *value);
3962 }
3963
3964 if (key->IsString()) {
3965 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003966 return js_object->SetElement(index, *value);
3967 } else {
3968 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003969 key_string->TryFlatten();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003970 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
3971 *value,
3972 attr);
3973 }
3974 }
3975
3976 // Call-back into JavaScript to convert the key to a string.
3977 bool has_pending_exception = false;
3978 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3979 if (has_pending_exception) return Failure::Exception();
3980 Handle<String> name = Handle<String>::cast(converted);
3981
3982 if (name->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003983 return js_object->SetElement(index, *value);
3984 } else {
3985 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
3986 }
3987}
3988
3989
ager@chromium.orge2902be2009-06-08 12:21:35 +00003990Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
3991 Handle<Object> key) {
3992 HandleScope scope;
3993
3994 // Check if the given key is an array index.
3995 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003996 if (key->ToArrayIndex(&index)) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00003997 // In Firefox/SpiderMonkey, Safari and Opera you can access the
3998 // characters of a string using [] notation. In the case of a
3999 // String object we just need to redirect the deletion to the
4000 // underlying string if the index is in range. Since the
4001 // underlying string does nothing with the deletion, we can ignore
4002 // such deletions.
4003 if (js_object->IsStringObjectWithCharacterAt(index)) {
4004 return Heap::true_value();
4005 }
4006
4007 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
4008 }
4009
4010 Handle<String> key_string;
4011 if (key->IsString()) {
4012 key_string = Handle<String>::cast(key);
4013 } else {
4014 // Call-back into JavaScript to convert the key to a string.
4015 bool has_pending_exception = false;
4016 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4017 if (has_pending_exception) return Failure::Exception();
4018 key_string = Handle<String>::cast(converted);
4019 }
4020
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004021 key_string->TryFlatten();
ager@chromium.orge2902be2009-06-08 12:21:35 +00004022 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
4023}
4024
4025
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004026static Object* Runtime_SetProperty(Arguments args) {
4027 NoHandleAllocation ha;
4028 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
4029
4030 Handle<Object> object = args.at<Object>(0);
4031 Handle<Object> key = args.at<Object>(1);
4032 Handle<Object> value = args.at<Object>(2);
4033
4034 // Compute attributes.
4035 PropertyAttributes attributes = NONE;
4036 if (args.length() == 4) {
4037 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004038 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004039 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004040 RUNTIME_ASSERT(
4041 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4042 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004043 }
4044 return Runtime::SetObjectProperty(object, key, value, attributes);
4045}
4046
4047
4048// Set a local property, even if it is READ_ONLY. If the property does not
4049// exist, it will be added with attributes NONE.
4050static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
4051 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004052 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004053 CONVERT_CHECKED(JSObject, object, args[0]);
4054 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004055 // Compute attributes.
4056 PropertyAttributes attributes = NONE;
4057 if (args.length() == 4) {
4058 CONVERT_CHECKED(Smi, value_obj, args[3]);
4059 int unchecked_value = value_obj->value();
4060 // Only attribute bits should be set.
4061 RUNTIME_ASSERT(
4062 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4063 attributes = static_cast<PropertyAttributes>(unchecked_value);
4064 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004065
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004066 return object->
4067 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004068}
4069
4070
4071static Object* Runtime_DeleteProperty(Arguments args) {
4072 NoHandleAllocation ha;
4073 ASSERT(args.length() == 2);
4074
4075 CONVERT_CHECKED(JSObject, object, args[0]);
4076 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00004077 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004078}
4079
4080
ager@chromium.org9085a012009-05-11 19:22:57 +00004081static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
4082 Handle<String> key) {
4083 if (object->HasLocalProperty(*key)) return Heap::true_value();
4084 // Handle hidden prototypes. If there's a hidden prototype above this thing
4085 // then we have to check it for properties, because they are supposed to
4086 // look like they are on this object.
4087 Handle<Object> proto(object->GetPrototype());
4088 if (proto->IsJSObject() &&
4089 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
4090 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
4091 }
4092 return Heap::false_value();
4093}
4094
4095
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004096static Object* Runtime_HasLocalProperty(Arguments args) {
4097 NoHandleAllocation ha;
4098 ASSERT(args.length() == 2);
4099 CONVERT_CHECKED(String, key, args[1]);
4100
ager@chromium.org9085a012009-05-11 19:22:57 +00004101 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004102 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00004103 if (obj->IsJSObject()) {
4104 JSObject* object = JSObject::cast(obj);
4105 // Fast case - no interceptors.
4106 if (object->HasRealNamedProperty(key)) return Heap::true_value();
4107 // Slow case. Either it's not there or we have an interceptor. We should
4108 // have handles for this kind of deal.
4109 HandleScope scope;
4110 return HasLocalPropertyImplementation(Handle<JSObject>(object),
4111 Handle<String>(key));
4112 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004113 // Well, there is one exception: Handle [] on strings.
4114 uint32_t index;
4115 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00004116 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004117 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004118 return Heap::true_value();
4119 }
4120 }
4121 return Heap::false_value();
4122}
4123
4124
4125static Object* Runtime_HasProperty(Arguments args) {
4126 NoHandleAllocation na;
4127 ASSERT(args.length() == 2);
4128
4129 // Only JS objects can have properties.
4130 if (args[0]->IsJSObject()) {
4131 JSObject* object = JSObject::cast(args[0]);
4132 CONVERT_CHECKED(String, key, args[1]);
4133 if (object->HasProperty(key)) return Heap::true_value();
4134 }
4135 return Heap::false_value();
4136}
4137
4138
4139static Object* Runtime_HasElement(Arguments args) {
4140 NoHandleAllocation na;
4141 ASSERT(args.length() == 2);
4142
4143 // Only JS objects can have elements.
4144 if (args[0]->IsJSObject()) {
4145 JSObject* object = JSObject::cast(args[0]);
4146 CONVERT_CHECKED(Smi, index_obj, args[1]);
4147 uint32_t index = index_obj->value();
4148 if (object->HasElement(index)) return Heap::true_value();
4149 }
4150 return Heap::false_value();
4151}
4152
4153
4154static Object* Runtime_IsPropertyEnumerable(Arguments args) {
4155 NoHandleAllocation ha;
4156 ASSERT(args.length() == 2);
4157
4158 CONVERT_CHECKED(JSObject, object, args[0]);
4159 CONVERT_CHECKED(String, key, args[1]);
4160
4161 uint32_t index;
4162 if (key->AsArrayIndex(&index)) {
4163 return Heap::ToBoolean(object->HasElement(index));
4164 }
4165
ager@chromium.org870a0b62008-11-04 11:43:05 +00004166 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
4167 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004168}
4169
4170
4171static Object* Runtime_GetPropertyNames(Arguments args) {
4172 HandleScope scope;
4173 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004174 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004175 return *GetKeysFor(object);
4176}
4177
4178
4179// Returns either a FixedArray as Runtime_GetPropertyNames,
4180// or, if the given object has an enum cache that contains
4181// all enumerable properties of the object and its prototypes
4182// have none, the map of the object. This is used to speed up
4183// the check for deletions during a for-in.
4184static Object* Runtime_GetPropertyNamesFast(Arguments args) {
4185 ASSERT(args.length() == 1);
4186
4187 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4188
4189 if (raw_object->IsSimpleEnum()) return raw_object->map();
4190
4191 HandleScope scope;
4192 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004193 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
4194 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004195
4196 // Test again, since cache may have been built by preceding call.
4197 if (object->IsSimpleEnum()) return object->map();
4198
4199 return *content;
4200}
4201
4202
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004203// Find the length of the prototype chain that is to to handled as one. If a
4204// prototype object is hidden it is to be viewed as part of the the object it
4205// is prototype for.
4206static int LocalPrototypeChainLength(JSObject* obj) {
4207 int count = 1;
4208 Object* proto = obj->GetPrototype();
4209 while (proto->IsJSObject() &&
4210 JSObject::cast(proto)->map()->is_hidden_prototype()) {
4211 count++;
4212 proto = JSObject::cast(proto)->GetPrototype();
4213 }
4214 return count;
4215}
4216
4217
4218// Return the names of the local named properties.
4219// args[0]: object
4220static Object* Runtime_GetLocalPropertyNames(Arguments args) {
4221 HandleScope scope;
4222 ASSERT(args.length() == 1);
4223 if (!args[0]->IsJSObject()) {
4224 return Heap::undefined_value();
4225 }
4226 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4227
4228 // Skip the global proxy as it has no properties and always delegates to the
4229 // real global object.
4230 if (obj->IsJSGlobalProxy()) {
4231 // Only collect names if access is permitted.
4232 if (obj->IsAccessCheckNeeded() &&
4233 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
4234 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
4235 return *Factory::NewJSArray(0);
4236 }
4237 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
4238 }
4239
4240 // Find the number of objects making up this.
4241 int length = LocalPrototypeChainLength(*obj);
4242
4243 // Find the number of local properties for each of the objects.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004244 ScopedVector<int> local_property_count(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004245 int total_property_count = 0;
4246 Handle<JSObject> jsproto = obj;
4247 for (int i = 0; i < length; i++) {
4248 // Only collect names if access is permitted.
4249 if (jsproto->IsAccessCheckNeeded() &&
4250 !Top::MayNamedAccess(*jsproto,
4251 Heap::undefined_value(),
4252 v8::ACCESS_KEYS)) {
4253 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
4254 return *Factory::NewJSArray(0);
4255 }
4256 int n;
4257 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4258 local_property_count[i] = n;
4259 total_property_count += n;
4260 if (i < length - 1) {
4261 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4262 }
4263 }
4264
4265 // Allocate an array with storage for all the property names.
4266 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
4267
4268 // Get the property names.
4269 jsproto = obj;
4270 int proto_with_hidden_properties = 0;
4271 for (int i = 0; i < length; i++) {
4272 jsproto->GetLocalPropertyNames(*names,
4273 i == 0 ? 0 : local_property_count[i - 1]);
4274 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
4275 proto_with_hidden_properties++;
4276 }
4277 if (i < length - 1) {
4278 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4279 }
4280 }
4281
4282 // Filter out name of hidden propeties object.
4283 if (proto_with_hidden_properties > 0) {
4284 Handle<FixedArray> old_names = names;
4285 names = Factory::NewFixedArray(
4286 names->length() - proto_with_hidden_properties);
4287 int dest_pos = 0;
4288 for (int i = 0; i < total_property_count; i++) {
4289 Object* name = old_names->get(i);
4290 if (name == Heap::hidden_symbol()) {
4291 continue;
4292 }
4293 names->set(dest_pos++, name);
4294 }
4295 }
4296
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004297 return *Factory::NewJSArrayWithElements(names);
4298}
4299
4300
4301// Return the names of the local indexed properties.
4302// args[0]: object
4303static Object* Runtime_GetLocalElementNames(Arguments args) {
4304 HandleScope scope;
4305 ASSERT(args.length() == 1);
4306 if (!args[0]->IsJSObject()) {
4307 return Heap::undefined_value();
4308 }
4309 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4310
4311 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4312 Handle<FixedArray> names = Factory::NewFixedArray(n);
4313 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4314 return *Factory::NewJSArrayWithElements(names);
4315}
4316
4317
4318// Return information on whether an object has a named or indexed interceptor.
4319// args[0]: object
4320static Object* Runtime_GetInterceptorInfo(Arguments args) {
4321 HandleScope scope;
4322 ASSERT(args.length() == 1);
4323 if (!args[0]->IsJSObject()) {
4324 return Smi::FromInt(0);
4325 }
4326 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4327
4328 int result = 0;
4329 if (obj->HasNamedInterceptor()) result |= 2;
4330 if (obj->HasIndexedInterceptor()) result |= 1;
4331
4332 return Smi::FromInt(result);
4333}
4334
4335
4336// Return property names from named interceptor.
4337// args[0]: object
4338static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
4339 HandleScope scope;
4340 ASSERT(args.length() == 1);
4341 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4342
4343 if (obj->HasNamedInterceptor()) {
4344 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4345 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4346 }
4347 return Heap::undefined_value();
4348}
4349
4350
4351// Return element names from indexed interceptor.
4352// args[0]: object
4353static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
4354 HandleScope scope;
4355 ASSERT(args.length() == 1);
4356 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4357
4358 if (obj->HasIndexedInterceptor()) {
4359 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4360 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4361 }
4362 return Heap::undefined_value();
4363}
4364
4365
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004366static Object* Runtime_LocalKeys(Arguments args) {
4367 ASSERT_EQ(args.length(), 1);
4368 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4369 HandleScope scope;
4370 Handle<JSObject> object(raw_object);
4371 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
4372 LOCAL_ONLY);
4373 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
4374 // property array and since the result is mutable we have to create
4375 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004376 int length = contents->length();
4377 Handle<FixedArray> copy = Factory::NewFixedArray(length);
4378 for (int i = 0; i < length; i++) {
4379 Object* entry = contents->get(i);
4380 if (entry->IsString()) {
4381 copy->set(i, entry);
4382 } else {
4383 ASSERT(entry->IsNumber());
4384 HandleScope scope;
4385 Handle<Object> entry_handle(entry);
4386 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
4387 copy->set(i, *entry_str);
4388 }
4389 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004390 return *Factory::NewJSArrayWithElements(copy);
4391}
4392
4393
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004394static Object* Runtime_GetArgumentsProperty(Arguments args) {
4395 NoHandleAllocation ha;
4396 ASSERT(args.length() == 1);
4397
4398 // Compute the frame holding the arguments.
4399 JavaScriptFrameIterator it;
4400 it.AdvanceToArgumentsFrame();
4401 JavaScriptFrame* frame = it.frame();
4402
4403 // Get the actual number of provided arguments.
4404 const uint32_t n = frame->GetProvidedParametersCount();
4405
4406 // Try to convert the key to an index. If successful and within
4407 // index return the the argument from the frame.
4408 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004409 if (args[0]->ToArrayIndex(&index) && index < n) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004410 return frame->GetParameter(index);
4411 }
4412
4413 // Convert the key to a string.
4414 HandleScope scope;
4415 bool exception = false;
4416 Handle<Object> converted =
4417 Execution::ToString(args.at<Object>(0), &exception);
4418 if (exception) return Failure::Exception();
4419 Handle<String> key = Handle<String>::cast(converted);
4420
4421 // Try to convert the string key into an array index.
4422 if (key->AsArrayIndex(&index)) {
4423 if (index < n) {
4424 return frame->GetParameter(index);
4425 } else {
4426 return Top::initial_object_prototype()->GetElement(index);
4427 }
4428 }
4429
4430 // Handle special arguments properties.
4431 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
4432 if (key->Equals(Heap::callee_symbol())) return frame->function();
4433
4434 // Lookup in the initial Object.prototype object.
4435 return Top::initial_object_prototype()->GetProperty(*key);
4436}
4437
4438
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004439static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004440 HandleScope scope;
4441
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004442 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004443 Handle<Object> object = args.at<Object>(0);
4444 if (object->IsJSObject()) {
4445 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00004446 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
4447 js_object->TransformToFastProperties(0);
4448 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004449 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004450 return *object;
4451}
4452
4453
4454static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004455 HandleScope scope;
4456
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004457 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004458 Handle<Object> object = args.at<Object>(0);
4459 if (object->IsJSObject()) {
4460 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004461 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004462 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004463 return *object;
4464}
4465
4466
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004467static Object* Runtime_ToBool(Arguments args) {
4468 NoHandleAllocation ha;
4469 ASSERT(args.length() == 1);
4470
4471 return args[0]->ToBoolean();
4472}
4473
4474
4475// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
4476// Possible optimizations: put the type string into the oddballs.
4477static Object* Runtime_Typeof(Arguments args) {
4478 NoHandleAllocation ha;
4479
4480 Object* obj = args[0];
4481 if (obj->IsNumber()) return Heap::number_symbol();
4482 HeapObject* heap_obj = HeapObject::cast(obj);
4483
4484 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004485 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004486
4487 InstanceType instance_type = heap_obj->map()->instance_type();
4488 if (instance_type < FIRST_NONSTRING_TYPE) {
4489 return Heap::string_symbol();
4490 }
4491
4492 switch (instance_type) {
4493 case ODDBALL_TYPE:
4494 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
4495 return Heap::boolean_symbol();
4496 }
4497 if (heap_obj->IsNull()) {
4498 return Heap::object_symbol();
4499 }
4500 ASSERT(heap_obj->IsUndefined());
4501 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004502 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004503 return Heap::function_symbol();
4504 default:
4505 // For any kind of object not handled above, the spec rule for
4506 // host objects gives that it is okay to return "object"
4507 return Heap::object_symbol();
4508 }
4509}
4510
4511
lrn@chromium.org25156de2010-04-06 13:10:27 +00004512static bool AreDigits(const char*s, int from, int to) {
4513 for (int i = from; i < to; i++) {
4514 if (s[i] < '0' || s[i] > '9') return false;
4515 }
4516
4517 return true;
4518}
4519
4520
4521static int ParseDecimalInteger(const char*s, int from, int to) {
4522 ASSERT(to - from < 10); // Overflow is not possible.
4523 ASSERT(from < to);
4524 int d = s[from] - '0';
4525
4526 for (int i = from + 1; i < to; i++) {
4527 d = 10 * d + (s[i] - '0');
4528 }
4529
4530 return d;
4531}
4532
4533
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004534static Object* Runtime_StringToNumber(Arguments args) {
4535 NoHandleAllocation ha;
4536 ASSERT(args.length() == 1);
4537 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004538 subject->TryFlatten();
lrn@chromium.org25156de2010-04-06 13:10:27 +00004539
4540 // Fast case: short integer or some sorts of junk values.
4541 int len = subject->length();
4542 if (subject->IsSeqAsciiString()) {
4543 if (len == 0) return Smi::FromInt(0);
4544
4545 char const* data = SeqAsciiString::cast(subject)->GetChars();
4546 bool minus = (data[0] == '-');
4547 int start_pos = (minus ? 1 : 0);
4548
4549 if (start_pos == len) {
4550 return Heap::nan_value();
4551 } else if (data[start_pos] > '9') {
4552 // Fast check for a junk value. A valid string may start from a
4553 // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit or
4554 // the 'I' character ('Infinity'). All of that have codes not greater than
4555 // '9' except 'I'.
4556 if (data[start_pos] != 'I') {
4557 return Heap::nan_value();
4558 }
4559 } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
4560 // The maximal/minimal smi has 10 digits. If the string has less digits we
4561 // know it will fit into the smi-data type.
4562 int d = ParseDecimalInteger(data, start_pos, len);
4563 if (minus) {
4564 if (d == 0) return Heap::minus_zero_value();
4565 d = -d;
4566 }
4567 return Smi::FromInt(d);
4568 }
4569 }
4570
4571 // Slower case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004572 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
4573}
4574
4575
4576static Object* Runtime_StringFromCharCodeArray(Arguments args) {
4577 NoHandleAllocation ha;
4578 ASSERT(args.length() == 1);
4579
4580 CONVERT_CHECKED(JSArray, codes, args[0]);
4581 int length = Smi::cast(codes->length())->value();
4582
4583 // Check if the string can be ASCII.
4584 int i;
4585 for (i = 0; i < length; i++) {
4586 Object* element = codes->GetElement(i);
4587 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
4588 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
4589 break;
4590 }
4591
4592 Object* object = NULL;
4593 if (i == length) { // The string is ASCII.
4594 object = Heap::AllocateRawAsciiString(length);
4595 } else { // The string is not ASCII.
4596 object = Heap::AllocateRawTwoByteString(length);
4597 }
4598
4599 if (object->IsFailure()) return object;
4600 String* result = String::cast(object);
4601 for (int i = 0; i < length; i++) {
4602 Object* element = codes->GetElement(i);
4603 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004604 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004605 }
4606 return result;
4607}
4608
4609
4610// kNotEscaped is generated by the following:
4611//
4612// #!/bin/perl
4613// for (my $i = 0; $i < 256; $i++) {
4614// print "\n" if $i % 16 == 0;
4615// my $c = chr($i);
4616// my $escaped = 1;
4617// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
4618// print $escaped ? "0, " : "1, ";
4619// }
4620
4621
4622static bool IsNotEscaped(uint16_t character) {
4623 // Only for 8 bit characters, the rest are always escaped (in a different way)
4624 ASSERT(character < 256);
4625 static const char kNotEscaped[256] = {
4626 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4627 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4628 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
4629 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
4630 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4631 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
4632 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4633 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
4634 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4635 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4636 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4637 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4638 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4639 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4640 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4641 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4642 };
4643 return kNotEscaped[character] != 0;
4644}
4645
4646
4647static Object* Runtime_URIEscape(Arguments args) {
4648 const char hex_chars[] = "0123456789ABCDEF";
4649 NoHandleAllocation ha;
4650 ASSERT(args.length() == 1);
4651 CONVERT_CHECKED(String, source, args[0]);
4652
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004653 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004654
4655 int escaped_length = 0;
4656 int length = source->length();
4657 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004658 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004659 buffer->Reset(source);
4660 while (buffer->has_more()) {
4661 uint16_t character = buffer->GetNext();
4662 if (character >= 256) {
4663 escaped_length += 6;
4664 } else if (IsNotEscaped(character)) {
4665 escaped_length++;
4666 } else {
4667 escaped_length += 3;
4668 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004669 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004670 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004671 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004672 Top::context()->mark_out_of_memory();
4673 return Failure::OutOfMemoryException();
4674 }
4675 }
4676 }
4677 // No length change implies no change. Return original string if no change.
4678 if (escaped_length == length) {
4679 return source;
4680 }
4681 Object* o = Heap::AllocateRawAsciiString(escaped_length);
4682 if (o->IsFailure()) return o;
4683 String* destination = String::cast(o);
4684 int dest_position = 0;
4685
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004686 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004687 buffer->Rewind();
4688 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00004689 uint16_t chr = buffer->GetNext();
4690 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004691 destination->Set(dest_position, '%');
4692 destination->Set(dest_position+1, 'u');
4693 destination->Set(dest_position+2, hex_chars[chr >> 12]);
4694 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
4695 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
4696 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004697 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00004698 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004699 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004700 dest_position++;
4701 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004702 destination->Set(dest_position, '%');
4703 destination->Set(dest_position+1, hex_chars[chr >> 4]);
4704 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004705 dest_position += 3;
4706 }
4707 }
4708 return destination;
4709}
4710
4711
4712static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
4713 static const signed char kHexValue['g'] = {
4714 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4715 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4716 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4717 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
4718 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4719 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4720 -1, 10, 11, 12, 13, 14, 15 };
4721
4722 if (character1 > 'f') return -1;
4723 int hi = kHexValue[character1];
4724 if (hi == -1) return -1;
4725 if (character2 > 'f') return -1;
4726 int lo = kHexValue[character2];
4727 if (lo == -1) return -1;
4728 return (hi << 4) + lo;
4729}
4730
4731
ager@chromium.org870a0b62008-11-04 11:43:05 +00004732static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004733 int i,
4734 int length,
4735 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004736 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00004737 int32_t hi = 0;
4738 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004739 if (character == '%' &&
4740 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004741 source->Get(i + 1) == 'u' &&
4742 (hi = TwoDigitHex(source->Get(i + 2),
4743 source->Get(i + 3))) != -1 &&
4744 (lo = TwoDigitHex(source->Get(i + 4),
4745 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004746 *step = 6;
4747 return (hi << 8) + lo;
4748 } else if (character == '%' &&
4749 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004750 (lo = TwoDigitHex(source->Get(i + 1),
4751 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004752 *step = 3;
4753 return lo;
4754 } else {
4755 *step = 1;
4756 return character;
4757 }
4758}
4759
4760
4761static Object* Runtime_URIUnescape(Arguments args) {
4762 NoHandleAllocation ha;
4763 ASSERT(args.length() == 1);
4764 CONVERT_CHECKED(String, source, args[0]);
4765
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004766 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004767
4768 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004769 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004770
4771 int unescaped_length = 0;
4772 for (int i = 0; i < length; unescaped_length++) {
4773 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004774 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004775 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004776 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004777 i += step;
4778 }
4779
4780 // No length change implies no change. Return original string if no change.
4781 if (unescaped_length == length)
4782 return source;
4783
4784 Object* o = ascii ?
4785 Heap::AllocateRawAsciiString(unescaped_length) :
4786 Heap::AllocateRawTwoByteString(unescaped_length);
4787 if (o->IsFailure()) return o;
4788 String* destination = String::cast(o);
4789
4790 int dest_position = 0;
4791 for (int i = 0; i < length; dest_position++) {
4792 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004793 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004794 i += step;
4795 }
4796 return destination;
4797}
4798
4799
4800static Object* Runtime_StringParseInt(Arguments args) {
4801 NoHandleAllocation ha;
4802
4803 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004804 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004805
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004806 s->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004807
lrn@chromium.org25156de2010-04-06 13:10:27 +00004808 RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
4809 double value = StringToInt(s, radix);
4810 return Heap::NumberFromDouble(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004811 return Heap::nan_value();
4812}
4813
4814
4815static Object* Runtime_StringParseFloat(Arguments args) {
4816 NoHandleAllocation ha;
4817 CONVERT_CHECKED(String, str, args[0]);
4818
4819 // ECMA-262 section 15.1.2.3, empty string is NaN
4820 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
4821
4822 // Create a number object from the value.
4823 return Heap::NumberFromDouble(value);
4824}
4825
4826
4827static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
4828static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
4829
4830
4831template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004832static Object* ConvertCaseHelper(String* s,
4833 int length,
4834 int input_string_length,
4835 unibrow::Mapping<Converter, 128>* mapping) {
4836 // We try this twice, once with the assumption that the result is no longer
4837 // than the input and, if that assumption breaks, again with the exact
4838 // length. This may not be pretty, but it is nicer than what was here before
4839 // and I hereby claim my vaffel-is.
4840 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004841 // Allocate the resulting string.
4842 //
4843 // NOTE: This assumes that the upper/lower case of an ascii
4844 // character is also ascii. This is currently the case, but it
4845 // might break in the future if we implement more context and locale
4846 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00004847 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004848 ? Heap::AllocateRawAsciiString(length)
4849 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004850 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004851 String* result = String::cast(o);
4852 bool has_changed_character = false;
4853
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004854 // Convert all characters to upper case, assuming that they will fit
4855 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004856 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004857 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004858 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004859 // We can assume that the string is not empty
4860 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004861 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00004862 bool has_next = buffer->has_more();
4863 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004864 int char_length = mapping->get(current, next, chars);
4865 if (char_length == 0) {
4866 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004867 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004868 i++;
4869 } else if (char_length == 1) {
4870 // Common case: converting the letter resulted in one character.
4871 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004872 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004873 has_changed_character = true;
4874 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004875 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004876 // We've assumed that the result would be as long as the
4877 // input but here is a character that converts to several
4878 // characters. No matter, we calculate the exact length
4879 // of the result and try the whole thing again.
4880 //
4881 // Note that this leaves room for optimization. We could just
4882 // memcpy what we already have to the result string. Also,
4883 // the result string is the last object allocated we could
4884 // "realloc" it and probably, in the vast majority of cases,
4885 // extend the existing string to be able to hold the full
4886 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00004887 int next_length = 0;
4888 if (has_next) {
4889 next_length = mapping->get(next, 0, chars);
4890 if (next_length == 0) next_length = 1;
4891 }
4892 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004893 while (buffer->has_more()) {
4894 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00004895 // NOTE: we use 0 as the next character here because, while
4896 // the next character may affect what a character converts to,
4897 // it does not in any case affect the length of what it convert
4898 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004899 int char_length = mapping->get(current, 0, chars);
4900 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00004901 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004902 if (current_length > Smi::kMaxValue) {
4903 Top::context()->mark_out_of_memory();
4904 return Failure::OutOfMemoryException();
4905 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004906 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004907 // Try again with the real length.
4908 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004909 } else {
4910 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004911 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004912 i++;
4913 }
4914 has_changed_character = true;
4915 }
4916 current = next;
4917 }
4918 if (has_changed_character) {
4919 return result;
4920 } else {
4921 // If we didn't actually change anything in doing the conversion
4922 // we simple return the result and let the converted string
4923 // become garbage; there is no reason to keep two identical strings
4924 // alive.
4925 return s;
4926 }
4927}
4928
4929
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004930static inline SeqAsciiString* TryGetSeqAsciiString(String* s) {
4931 if (!s->IsFlat() || !s->IsAsciiRepresentation()) return NULL;
4932 if (s->IsConsString()) {
4933 ASSERT(ConsString::cast(s)->second()->length() == 0);
4934 return SeqAsciiString::cast(ConsString::cast(s)->first());
4935 }
4936 return SeqAsciiString::cast(s);
4937}
4938
4939
4940namespace {
4941
4942struct ToLowerTraits {
4943 typedef unibrow::ToLowercase UnibrowConverter;
4944
4945 static bool ConvertAscii(char* dst, char* src, int length) {
4946 bool changed = false;
4947 for (int i = 0; i < length; ++i) {
4948 char c = src[i];
4949 if ('A' <= c && c <= 'Z') {
4950 c += ('a' - 'A');
4951 changed = true;
4952 }
4953 dst[i] = c;
4954 }
4955 return changed;
4956 }
4957};
4958
4959
4960struct ToUpperTraits {
4961 typedef unibrow::ToUppercase UnibrowConverter;
4962
4963 static bool ConvertAscii(char* dst, char* src, int length) {
4964 bool changed = false;
4965 for (int i = 0; i < length; ++i) {
4966 char c = src[i];
4967 if ('a' <= c && c <= 'z') {
4968 c -= ('a' - 'A');
4969 changed = true;
4970 }
4971 dst[i] = c;
4972 }
4973 return changed;
4974 }
4975};
4976
4977} // namespace
4978
4979
4980template <typename ConvertTraits>
4981static Object* ConvertCase(
4982 Arguments args,
4983 unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004984 NoHandleAllocation ha;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004985 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004986 s->TryFlatten();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004987
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004988 const int length = s->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004989 // Assume that the string is not empty; we need this assumption later
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004990 if (length == 0) return s;
4991
4992 // Simpler handling of ascii strings.
4993 //
4994 // NOTE: This assumes that the upper/lower case of an ascii
4995 // character is also ascii. This is currently the case, but it
4996 // might break in the future if we implement more context and locale
4997 // dependent upper/lower conversions.
4998 SeqAsciiString* seq_ascii = TryGetSeqAsciiString(s);
4999 if (seq_ascii != NULL) {
5000 Object* o = Heap::AllocateRawAsciiString(length);
5001 if (o->IsFailure()) return o;
5002 SeqAsciiString* result = SeqAsciiString::cast(o);
5003 bool has_changed_character = ConvertTraits::ConvertAscii(
5004 result->GetChars(), seq_ascii->GetChars(), length);
5005 return has_changed_character ? result : s;
5006 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005007
5008 Object* answer = ConvertCaseHelper(s, length, length, mapping);
5009 if (answer->IsSmi()) {
5010 // Retry with correct length.
5011 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
5012 }
5013 return answer; // This may be a failure.
5014}
5015
5016
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005017static Object* Runtime_StringToLowerCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005018 return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005019}
5020
5021
5022static Object* Runtime_StringToUpperCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005023 return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005024}
5025
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005026
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005027static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
5028 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
5029}
5030
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005031
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005032static Object* Runtime_StringTrim(Arguments args) {
5033 NoHandleAllocation ha;
5034 ASSERT(args.length() == 3);
5035
5036 CONVERT_CHECKED(String, s, args[0]);
5037 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
5038 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
5039
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005040 s->TryFlatten();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005041 int length = s->length();
5042
5043 int left = 0;
5044 if (trimLeft) {
5045 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
5046 left++;
5047 }
5048 }
5049
5050 int right = length;
5051 if (trimRight) {
5052 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
5053 right--;
5054 }
5055 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005056 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005057}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005058
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005059
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005060template <typename schar, typename pchar>
5061void FindStringIndices(Vector<const schar> subject,
5062 Vector<const pchar> pattern,
5063 ZoneList<int>* indices,
5064 unsigned int limit) {
5065 ASSERT(limit > 0);
5066 // Collect indices of pattern in subject, and the end-of-string index.
5067 // Stop after finding at most limit values.
5068 StringSearchStrategy strategy =
5069 InitializeStringSearch(pattern, sizeof(schar) == 1);
5070 switch (strategy) {
5071 case SEARCH_FAIL: return;
5072 case SEARCH_SHORT: {
5073 int pattern_length = pattern.length();
5074 int index = 0;
5075 while (limit > 0) {
5076 index = SimpleIndexOf(subject, pattern, index);
5077 if (index < 0) return;
5078 indices->Add(index);
5079 index += pattern_length;
5080 limit--;
5081 }
5082 return;
5083 }
5084 case SEARCH_LONG: {
5085 int pattern_length = pattern.length();
5086 int index = 0;
5087 while (limit > 0) {
5088 index = ComplexIndexOf(subject, pattern, index);
5089 if (index < 0) return;
5090 indices->Add(index);
5091 index += pattern_length;
5092 limit--;
5093 }
5094 return;
5095 }
5096 default:
5097 UNREACHABLE();
5098 return;
5099 }
5100}
5101
5102template <typename schar>
5103inline void FindCharIndices(Vector<const schar> subject,
5104 const schar pattern_char,
5105 ZoneList<int>* indices,
5106 unsigned int limit) {
5107 // Collect indices of pattern_char in subject, and the end-of-string index.
5108 // Stop after finding at most limit values.
5109 int index = 0;
5110 while (limit > 0) {
5111 index = SingleCharIndexOf(subject, pattern_char, index);
5112 if (index < 0) return;
5113 indices->Add(index);
5114 index++;
5115 limit--;
5116 }
5117}
5118
5119
5120static Object* Runtime_StringSplit(Arguments args) {
5121 ASSERT(args.length() == 3);
5122 HandleScope handle_scope;
5123 CONVERT_ARG_CHECKED(String, subject, 0);
5124 CONVERT_ARG_CHECKED(String, pattern, 1);
5125 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
5126
5127 int subject_length = subject->length();
5128 int pattern_length = pattern->length();
5129 RUNTIME_ASSERT(pattern_length > 0);
5130
5131 // The limit can be very large (0xffffffffu), but since the pattern
5132 // isn't empty, we can never create more parts than ~half the length
5133 // of the subject.
5134
5135 if (!subject->IsFlat()) FlattenString(subject);
5136
5137 static const int kMaxInitialListCapacity = 16;
5138
5139 ZoneScope scope(DELETE_ON_EXIT);
5140
5141 // Find (up to limit) indices of separator and end-of-string in subject
5142 int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
5143 ZoneList<int> indices(initial_capacity);
5144 if (pattern_length == 1) {
5145 // Special case, go directly to fast single-character split.
5146 AssertNoAllocation nogc;
5147 uc16 pattern_char = pattern->Get(0);
5148 if (subject->IsTwoByteRepresentation()) {
5149 FindCharIndices(subject->ToUC16Vector(), pattern_char,
5150 &indices,
5151 limit);
5152 } else if (pattern_char <= String::kMaxAsciiCharCode) {
5153 FindCharIndices(subject->ToAsciiVector(),
5154 static_cast<char>(pattern_char),
5155 &indices,
5156 limit);
5157 }
5158 } else {
5159 if (!pattern->IsFlat()) FlattenString(pattern);
5160 AssertNoAllocation nogc;
5161 if (subject->IsAsciiRepresentation()) {
5162 Vector<const char> subject_vector = subject->ToAsciiVector();
5163 if (pattern->IsAsciiRepresentation()) {
5164 FindStringIndices(subject_vector,
5165 pattern->ToAsciiVector(),
5166 &indices,
5167 limit);
5168 } else {
5169 FindStringIndices(subject_vector,
5170 pattern->ToUC16Vector(),
5171 &indices,
5172 limit);
5173 }
5174 } else {
5175 Vector<const uc16> subject_vector = subject->ToUC16Vector();
5176 if (pattern->IsAsciiRepresentation()) {
5177 FindStringIndices(subject_vector,
5178 pattern->ToAsciiVector(),
5179 &indices,
5180 limit);
5181 } else {
5182 FindStringIndices(subject_vector,
5183 pattern->ToUC16Vector(),
5184 &indices,
5185 limit);
5186 }
5187 }
5188 }
5189 if (static_cast<uint32_t>(indices.length()) < limit) {
5190 indices.Add(subject_length);
5191 }
5192 // The list indices now contains the end of each part to create.
5193
5194
5195 // Create JSArray of substrings separated by separator.
5196 int part_count = indices.length();
5197
5198 Handle<JSArray> result = Factory::NewJSArray(part_count);
5199 result->set_length(Smi::FromInt(part_count));
5200
5201 ASSERT(result->HasFastElements());
5202
5203 if (part_count == 1 && indices.at(0) == subject_length) {
5204 FixedArray::cast(result->elements())->set(0, *subject);
5205 return *result;
5206 }
5207
5208 Handle<FixedArray> elements(FixedArray::cast(result->elements()));
5209 int part_start = 0;
5210 for (int i = 0; i < part_count; i++) {
5211 HandleScope local_loop_handle;
5212 int part_end = indices.at(i);
5213 Handle<String> substring =
5214 Factory::NewSubString(subject, part_start, part_end);
5215 elements->set(i, *substring);
5216 part_start = part_end + pattern_length;
5217 }
5218
5219 return *result;
5220}
5221
5222
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005223// Copies ascii characters to the given fixed array looking up
5224// one-char strings in the cache. Gives up on the first char that is
5225// not in the cache and fills the remainder with smi zeros. Returns
5226// the length of the successfully copied prefix.
5227static int CopyCachedAsciiCharsToArray(const char* chars,
5228 FixedArray* elements,
5229 int length) {
5230 AssertNoAllocation nogc;
5231 FixedArray* ascii_cache = Heap::single_character_string_cache();
5232 Object* undefined = Heap::undefined_value();
5233 int i;
5234 for (i = 0; i < length; ++i) {
5235 Object* value = ascii_cache->get(chars[i]);
5236 if (value == undefined) break;
5237 ASSERT(!Heap::InNewSpace(value));
5238 elements->set(i, value, SKIP_WRITE_BARRIER);
5239 }
5240 if (i < length) {
5241 ASSERT(Smi::FromInt(0) == 0);
5242 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
5243 }
5244#ifdef DEBUG
5245 for (int j = 0; j < length; ++j) {
5246 Object* element = elements->get(j);
5247 ASSERT(element == Smi::FromInt(0) ||
5248 (element->IsString() && String::cast(element)->LooksValid()));
5249 }
5250#endif
5251 return i;
5252}
5253
5254
5255// Converts a String to JSArray.
5256// For example, "foo" => ["f", "o", "o"].
5257static Object* Runtime_StringToArray(Arguments args) {
5258 HandleScope scope;
5259 ASSERT(args.length() == 1);
5260 CONVERT_ARG_CHECKED(String, s, 0);
5261
5262 s->TryFlatten();
5263 const int length = s->length();
5264
5265 Handle<FixedArray> elements;
5266 if (s->IsFlat() && s->IsAsciiRepresentation()) {
5267 Object* obj = Heap::AllocateUninitializedFixedArray(length);
5268 if (obj->IsFailure()) return obj;
5269 elements = Handle<FixedArray>(FixedArray::cast(obj));
5270
5271 Vector<const char> chars = s->ToAsciiVector();
5272 // Note, this will initialize all elements (not only the prefix)
5273 // to prevent GC from seeing partially initialized array.
5274 int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
5275 *elements,
5276 length);
5277
5278 for (int i = num_copied_from_cache; i < length; ++i) {
5279 elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
5280 }
5281 } else {
5282 elements = Factory::NewFixedArray(length);
5283 for (int i = 0; i < length; ++i) {
5284 elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
5285 }
5286 }
5287
5288#ifdef DEBUG
5289 for (int i = 0; i < length; ++i) {
5290 ASSERT(String::cast(elements->get(i))->length() == 1);
5291 }
5292#endif
5293
5294 return *Factory::NewJSArrayWithElements(elements);
5295}
5296
5297
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00005298bool Runtime::IsUpperCaseChar(uint16_t ch) {
5299 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
5300 int char_length = to_upper_mapping.get(ch, 0, chars);
5301 return char_length == 0;
5302}
5303
5304
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005305static Object* Runtime_NumberToString(Arguments args) {
5306 NoHandleAllocation ha;
5307 ASSERT(args.length() == 1);
5308
5309 Object* number = args[0];
5310 RUNTIME_ASSERT(number->IsNumber());
5311
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005312 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005313}
5314
5315
ager@chromium.org357bf652010-04-12 11:30:10 +00005316static Object* Runtime_NumberToStringSkipCache(Arguments args) {
5317 NoHandleAllocation ha;
5318 ASSERT(args.length() == 1);
5319
5320 Object* number = args[0];
5321 RUNTIME_ASSERT(number->IsNumber());
5322
5323 return Heap::NumberToString(number, false);
5324}
5325
5326
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005327static Object* Runtime_NumberToInteger(Arguments args) {
5328 NoHandleAllocation ha;
5329 ASSERT(args.length() == 1);
5330
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005331 CONVERT_DOUBLE_CHECKED(number, args[0]);
5332
5333 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5334 if (number > 0 && number <= Smi::kMaxValue) {
5335 return Smi::FromInt(static_cast<int>(number));
5336 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005337 return Heap::NumberFromDouble(DoubleToInteger(number));
5338}
5339
5340
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005341
5342
5343
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005344static Object* Runtime_NumberToIntegerMapMinusZero(Arguments args) {
5345 NoHandleAllocation ha;
5346 ASSERT(args.length() == 1);
5347
5348 CONVERT_DOUBLE_CHECKED(number, args[0]);
5349
5350 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5351 if (number > 0 && number <= Smi::kMaxValue) {
5352 return Smi::FromInt(static_cast<int>(number));
5353 }
5354
5355 double double_value = DoubleToInteger(number);
5356 // Map both -0 and +0 to +0.
5357 if (double_value == 0) double_value = 0;
5358
5359 return Heap::NumberFromDouble(double_value);
5360}
5361
5362
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005363static Object* Runtime_NumberToJSUint32(Arguments args) {
5364 NoHandleAllocation ha;
5365 ASSERT(args.length() == 1);
5366
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005367 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005368 return Heap::NumberFromUint32(number);
5369}
5370
5371
5372static Object* Runtime_NumberToJSInt32(Arguments args) {
5373 NoHandleAllocation ha;
5374 ASSERT(args.length() == 1);
5375
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005376 CONVERT_DOUBLE_CHECKED(number, args[0]);
5377
5378 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5379 if (number > 0 && number <= Smi::kMaxValue) {
5380 return Smi::FromInt(static_cast<int>(number));
5381 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005382 return Heap::NumberFromInt32(DoubleToInt32(number));
5383}
5384
5385
ager@chromium.org870a0b62008-11-04 11:43:05 +00005386// Converts a Number to a Smi, if possible. Returns NaN if the number is not
5387// a small integer.
5388static Object* Runtime_NumberToSmi(Arguments args) {
5389 NoHandleAllocation ha;
5390 ASSERT(args.length() == 1);
5391
5392 Object* obj = args[0];
5393 if (obj->IsSmi()) {
5394 return obj;
5395 }
5396 if (obj->IsHeapNumber()) {
5397 double value = HeapNumber::cast(obj)->value();
5398 int int_value = FastD2I(value);
5399 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
5400 return Smi::FromInt(int_value);
5401 }
5402 }
5403 return Heap::nan_value();
5404}
5405
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005406
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005407static Object* Runtime_NumberAdd(Arguments args) {
5408 NoHandleAllocation ha;
5409 ASSERT(args.length() == 2);
5410
5411 CONVERT_DOUBLE_CHECKED(x, args[0]);
5412 CONVERT_DOUBLE_CHECKED(y, args[1]);
5413 return Heap::AllocateHeapNumber(x + y);
5414}
5415
5416
5417static Object* Runtime_NumberSub(Arguments args) {
5418 NoHandleAllocation ha;
5419 ASSERT(args.length() == 2);
5420
5421 CONVERT_DOUBLE_CHECKED(x, args[0]);
5422 CONVERT_DOUBLE_CHECKED(y, args[1]);
5423 return Heap::AllocateHeapNumber(x - y);
5424}
5425
5426
5427static Object* Runtime_NumberMul(Arguments args) {
5428 NoHandleAllocation ha;
5429 ASSERT(args.length() == 2);
5430
5431 CONVERT_DOUBLE_CHECKED(x, args[0]);
5432 CONVERT_DOUBLE_CHECKED(y, args[1]);
5433 return Heap::AllocateHeapNumber(x * y);
5434}
5435
5436
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005437static Object* Runtime_NumberUnaryMinus(Arguments args) {
5438 NoHandleAllocation ha;
5439 ASSERT(args.length() == 1);
5440
5441 CONVERT_DOUBLE_CHECKED(x, args[0]);
5442 return Heap::AllocateHeapNumber(-x);
5443}
5444
5445
5446static Object* Runtime_NumberDiv(Arguments args) {
5447 NoHandleAllocation ha;
5448 ASSERT(args.length() == 2);
5449
5450 CONVERT_DOUBLE_CHECKED(x, args[0]);
5451 CONVERT_DOUBLE_CHECKED(y, args[1]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005452 return Heap::NumberFromDouble(x / y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005453}
5454
5455
5456static Object* Runtime_NumberMod(Arguments args) {
5457 NoHandleAllocation ha;
5458 ASSERT(args.length() == 2);
5459
5460 CONVERT_DOUBLE_CHECKED(x, args[0]);
5461 CONVERT_DOUBLE_CHECKED(y, args[1]);
5462
ager@chromium.org3811b432009-10-28 14:53:37 +00005463 x = modulo(x, y);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005464 // NumberFromDouble may return a Smi instead of a Number object
5465 return Heap::NumberFromDouble(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005466}
5467
5468
5469static Object* Runtime_StringAdd(Arguments args) {
5470 NoHandleAllocation ha;
5471 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005472 CONVERT_CHECKED(String, str1, args[0]);
5473 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005474 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00005475 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005476}
5477
5478
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005479template <typename sinkchar>
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005480static inline void StringBuilderConcatHelper(String* special,
5481 sinkchar* sink,
5482 FixedArray* fixed_array,
5483 int array_length) {
5484 int position = 0;
5485 for (int i = 0; i < array_length; i++) {
5486 Object* element = fixed_array->get(i);
5487 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005488 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005489 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005490 int pos;
5491 int len;
5492 if (encoded_slice > 0) {
5493 // Position and length encoded in one smi.
5494 pos = StringBuilderSubstringPosition::decode(encoded_slice);
5495 len = StringBuilderSubstringLength::decode(encoded_slice);
5496 } else {
5497 // Position and length encoded in two smis.
5498 Object* obj = fixed_array->get(++i);
5499 ASSERT(obj->IsSmi());
5500 pos = Smi::cast(obj)->value();
5501 len = -encoded_slice;
5502 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00005503 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00005504 sink + position,
5505 pos,
5506 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005507 position += len;
5508 } else {
5509 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005510 int element_length = string->length();
5511 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005512 position += element_length;
5513 }
5514 }
5515}
5516
5517
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005518static Object* Runtime_StringBuilderConcat(Arguments args) {
5519 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005520 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005521 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005522 if (!args[1]->IsSmi()) {
5523 Top::context()->mark_out_of_memory();
5524 return Failure::OutOfMemoryException();
5525 }
5526 int array_length = Smi::cast(args[1])->value();
5527 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005528
5529 // This assumption is used by the slice encoding in one or two smis.
5530 ASSERT(Smi::kMaxValue >= String::kMaxLength);
5531
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005532 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005533 if (!array->HasFastElements()) {
5534 return Top::Throw(Heap::illegal_argument_symbol());
5535 }
5536 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005537 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005538 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005539 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005540
5541 if (array_length == 0) {
5542 return Heap::empty_string();
5543 } else if (array_length == 1) {
5544 Object* first = fixed_array->get(0);
5545 if (first->IsString()) return first;
5546 }
5547
ager@chromium.org5ec48922009-05-05 07:25:34 +00005548 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005549 int position = 0;
5550 for (int i = 0; i < array_length; i++) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005551 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005552 Object* elt = fixed_array->get(i);
5553 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005554 // Smi encoding of position and length.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005555 int smi_value = Smi::cast(elt)->value();
5556 int pos;
5557 int len;
5558 if (smi_value > 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005559 // Position and length encoded in one smi.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005560 pos = StringBuilderSubstringPosition::decode(smi_value);
5561 len = StringBuilderSubstringLength::decode(smi_value);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005562 } else {
5563 // Position and length encoded in two smis.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005564 len = -smi_value;
5565 // Get the position and check that it is a positive smi.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005566 i++;
5567 if (i >= array_length) {
5568 return Top::Throw(Heap::illegal_argument_symbol());
5569 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005570 Object* next_smi = fixed_array->get(i);
5571 if (!next_smi->IsSmi()) {
5572 return Top::Throw(Heap::illegal_argument_symbol());
5573 }
5574 pos = Smi::cast(next_smi)->value();
5575 if (pos < 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005576 return Top::Throw(Heap::illegal_argument_symbol());
5577 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005578 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005579 ASSERT(pos >= 0);
5580 ASSERT(len >= 0);
5581 if (pos > special_length || len > special_length - pos) {
5582 return Top::Throw(Heap::illegal_argument_symbol());
5583 }
5584 increment = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005585 } else if (elt->IsString()) {
5586 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005587 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005588 increment = element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005589 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005590 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005591 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005592 } else {
5593 return Top::Throw(Heap::illegal_argument_symbol());
5594 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005595 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005596 Top::context()->mark_out_of_memory();
5597 return Failure::OutOfMemoryException();
5598 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005599 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005600 }
5601
5602 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005603 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005604
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005605 if (ascii) {
5606 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005607 if (object->IsFailure()) return object;
5608 SeqAsciiString* answer = SeqAsciiString::cast(object);
5609 StringBuilderConcatHelper(special,
5610 answer->GetChars(),
5611 fixed_array,
5612 array_length);
5613 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005614 } else {
5615 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005616 if (object->IsFailure()) return object;
5617 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
5618 StringBuilderConcatHelper(special,
5619 answer->GetChars(),
5620 fixed_array,
5621 array_length);
5622 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005623 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005624}
5625
5626
5627static Object* Runtime_NumberOr(Arguments args) {
5628 NoHandleAllocation ha;
5629 ASSERT(args.length() == 2);
5630
5631 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5632 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5633 return Heap::NumberFromInt32(x | y);
5634}
5635
5636
5637static Object* Runtime_NumberAnd(Arguments args) {
5638 NoHandleAllocation ha;
5639 ASSERT(args.length() == 2);
5640
5641 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5642 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5643 return Heap::NumberFromInt32(x & y);
5644}
5645
5646
5647static Object* Runtime_NumberXor(Arguments args) {
5648 NoHandleAllocation ha;
5649 ASSERT(args.length() == 2);
5650
5651 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5652 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5653 return Heap::NumberFromInt32(x ^ y);
5654}
5655
5656
5657static Object* Runtime_NumberNot(Arguments args) {
5658 NoHandleAllocation ha;
5659 ASSERT(args.length() == 1);
5660
5661 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5662 return Heap::NumberFromInt32(~x);
5663}
5664
5665
5666static Object* Runtime_NumberShl(Arguments args) {
5667 NoHandleAllocation ha;
5668 ASSERT(args.length() == 2);
5669
5670 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5671 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5672 return Heap::NumberFromInt32(x << (y & 0x1f));
5673}
5674
5675
5676static Object* Runtime_NumberShr(Arguments args) {
5677 NoHandleAllocation ha;
5678 ASSERT(args.length() == 2);
5679
5680 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
5681 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5682 return Heap::NumberFromUint32(x >> (y & 0x1f));
5683}
5684
5685
5686static Object* Runtime_NumberSar(Arguments args) {
5687 NoHandleAllocation ha;
5688 ASSERT(args.length() == 2);
5689
5690 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5691 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5692 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
5693}
5694
5695
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005696static Object* Runtime_NumberEquals(Arguments args) {
5697 NoHandleAllocation ha;
5698 ASSERT(args.length() == 2);
5699
5700 CONVERT_DOUBLE_CHECKED(x, args[0]);
5701 CONVERT_DOUBLE_CHECKED(y, args[1]);
5702 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
5703 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
5704 if (x == y) return Smi::FromInt(EQUAL);
5705 Object* result;
5706 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
5707 result = Smi::FromInt(EQUAL);
5708 } else {
5709 result = Smi::FromInt(NOT_EQUAL);
5710 }
5711 return result;
5712}
5713
5714
5715static Object* Runtime_StringEquals(Arguments args) {
5716 NoHandleAllocation ha;
5717 ASSERT(args.length() == 2);
5718
5719 CONVERT_CHECKED(String, x, args[0]);
5720 CONVERT_CHECKED(String, y, args[1]);
5721
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005722 bool not_equal = !x->Equals(y);
5723 // This is slightly convoluted because the value that signifies
5724 // equality is 0 and inequality is 1 so we have to negate the result
5725 // from String::Equals.
5726 ASSERT(not_equal == 0 || not_equal == 1);
5727 STATIC_CHECK(EQUAL == 0);
5728 STATIC_CHECK(NOT_EQUAL == 1);
5729 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005730}
5731
5732
5733static Object* Runtime_NumberCompare(Arguments args) {
5734 NoHandleAllocation ha;
5735 ASSERT(args.length() == 3);
5736
5737 CONVERT_DOUBLE_CHECKED(x, args[0]);
5738 CONVERT_DOUBLE_CHECKED(y, args[1]);
5739 if (isnan(x) || isnan(y)) return args[2];
5740 if (x == y) return Smi::FromInt(EQUAL);
5741 if (isless(x, y)) return Smi::FromInt(LESS);
5742 return Smi::FromInt(GREATER);
5743}
5744
5745
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005746// Compare two Smis as if they were converted to strings and then
5747// compared lexicographically.
5748static Object* Runtime_SmiLexicographicCompare(Arguments args) {
5749 NoHandleAllocation ha;
5750 ASSERT(args.length() == 2);
5751
5752 // Arrays for the individual characters of the two Smis. Smis are
5753 // 31 bit integers and 10 decimal digits are therefore enough.
5754 static int x_elms[10];
5755 static int y_elms[10];
5756
5757 // Extract the integer values from the Smis.
5758 CONVERT_CHECKED(Smi, x, args[0]);
5759 CONVERT_CHECKED(Smi, y, args[1]);
5760 int x_value = x->value();
5761 int y_value = y->value();
5762
5763 // If the integers are equal so are the string representations.
5764 if (x_value == y_value) return Smi::FromInt(EQUAL);
5765
5766 // If one of the integers are zero the normal integer order is the
5767 // same as the lexicographic order of the string representations.
5768 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
5769
ager@chromium.org32912102009-01-16 10:38:43 +00005770 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005771 // smallest because the char code of '-' is less than the char code
5772 // of any digit. Otherwise, we make both values positive.
5773 if (x_value < 0 || y_value < 0) {
5774 if (y_value >= 0) return Smi::FromInt(LESS);
5775 if (x_value >= 0) return Smi::FromInt(GREATER);
5776 x_value = -x_value;
5777 y_value = -y_value;
5778 }
5779
5780 // Convert the integers to arrays of their decimal digits.
5781 int x_index = 0;
5782 int y_index = 0;
5783 while (x_value > 0) {
5784 x_elms[x_index++] = x_value % 10;
5785 x_value /= 10;
5786 }
5787 while (y_value > 0) {
5788 y_elms[y_index++] = y_value % 10;
5789 y_value /= 10;
5790 }
5791
5792 // Loop through the arrays of decimal digits finding the first place
5793 // where they differ.
5794 while (--x_index >= 0 && --y_index >= 0) {
5795 int diff = x_elms[x_index] - y_elms[y_index];
5796 if (diff != 0) return Smi::FromInt(diff);
5797 }
5798
5799 // If one array is a suffix of the other array, the longest array is
5800 // the representation of the largest of the Smis in the
5801 // lexicographic ordering.
5802 return Smi::FromInt(x_index - y_index);
5803}
5804
5805
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005806static Object* StringInputBufferCompare(String* x, String* y) {
5807 static StringInputBuffer bufx;
5808 static StringInputBuffer bufy;
5809 bufx.Reset(x);
5810 bufy.Reset(y);
5811 while (bufx.has_more() && bufy.has_more()) {
5812 int d = bufx.GetNext() - bufy.GetNext();
5813 if (d < 0) return Smi::FromInt(LESS);
5814 else if (d > 0) return Smi::FromInt(GREATER);
5815 }
5816
5817 // x is (non-trivial) prefix of y:
5818 if (bufy.has_more()) return Smi::FromInt(LESS);
5819 // y is prefix of x:
5820 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
5821}
5822
5823
5824static Object* FlatStringCompare(String* x, String* y) {
5825 ASSERT(x->IsFlat());
5826 ASSERT(y->IsFlat());
5827 Object* equal_prefix_result = Smi::FromInt(EQUAL);
5828 int prefix_length = x->length();
5829 if (y->length() < prefix_length) {
5830 prefix_length = y->length();
5831 equal_prefix_result = Smi::FromInt(GREATER);
5832 } else if (y->length() > prefix_length) {
5833 equal_prefix_result = Smi::FromInt(LESS);
5834 }
5835 int r;
5836 if (x->IsAsciiRepresentation()) {
5837 Vector<const char> x_chars = x->ToAsciiVector();
5838 if (y->IsAsciiRepresentation()) {
5839 Vector<const char> y_chars = y->ToAsciiVector();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005840 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005841 } else {
5842 Vector<const uc16> y_chars = y->ToUC16Vector();
5843 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5844 }
5845 } else {
5846 Vector<const uc16> x_chars = x->ToUC16Vector();
5847 if (y->IsAsciiRepresentation()) {
5848 Vector<const char> y_chars = y->ToAsciiVector();
5849 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5850 } else {
5851 Vector<const uc16> y_chars = y->ToUC16Vector();
5852 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5853 }
5854 }
5855 Object* result;
5856 if (r == 0) {
5857 result = equal_prefix_result;
5858 } else {
5859 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
5860 }
5861 ASSERT(result == StringInputBufferCompare(x, y));
5862 return result;
5863}
5864
5865
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005866static Object* Runtime_StringCompare(Arguments args) {
5867 NoHandleAllocation ha;
5868 ASSERT(args.length() == 2);
5869
5870 CONVERT_CHECKED(String, x, args[0]);
5871 CONVERT_CHECKED(String, y, args[1]);
5872
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005873 Counters::string_compare_runtime.Increment();
5874
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005875 // A few fast case tests before we flatten.
5876 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005877 if (y->length() == 0) {
5878 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005879 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005880 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005881 return Smi::FromInt(LESS);
5882 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005883
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005884 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005885 if (d < 0) return Smi::FromInt(LESS);
5886 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005887
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005888 Object* obj = Heap::PrepareForCompare(x);
5889 if (obj->IsFailure()) return obj;
5890 obj = Heap::PrepareForCompare(y);
5891 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005892
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005893 return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
5894 : StringInputBufferCompare(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005895}
5896
5897
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005898static Object* Runtime_Math_acos(Arguments args) {
5899 NoHandleAllocation ha;
5900 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005901 Counters::math_acos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005902
5903 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005904 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005905}
5906
5907
5908static Object* Runtime_Math_asin(Arguments args) {
5909 NoHandleAllocation ha;
5910 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005911 Counters::math_asin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005912
5913 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005914 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005915}
5916
5917
5918static Object* Runtime_Math_atan(Arguments args) {
5919 NoHandleAllocation ha;
5920 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005921 Counters::math_atan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005922
5923 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005924 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005925}
5926
5927
5928static Object* Runtime_Math_atan2(Arguments args) {
5929 NoHandleAllocation ha;
5930 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005931 Counters::math_atan2.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005932
5933 CONVERT_DOUBLE_CHECKED(x, args[0]);
5934 CONVERT_DOUBLE_CHECKED(y, args[1]);
5935 double result;
5936 if (isinf(x) && isinf(y)) {
5937 // Make sure that the result in case of two infinite arguments
5938 // is a multiple of Pi / 4. The sign of the result is determined
5939 // by the first argument (x) and the sign of the second argument
5940 // determines the multiplier: one or three.
5941 static double kPiDividedBy4 = 0.78539816339744830962;
5942 int multiplier = (x < 0) ? -1 : 1;
5943 if (y < 0) multiplier *= 3;
5944 result = multiplier * kPiDividedBy4;
5945 } else {
5946 result = atan2(x, y);
5947 }
5948 return Heap::AllocateHeapNumber(result);
5949}
5950
5951
5952static Object* Runtime_Math_ceil(Arguments args) {
5953 NoHandleAllocation ha;
5954 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005955 Counters::math_ceil.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005956
5957 CONVERT_DOUBLE_CHECKED(x, args[0]);
5958 return Heap::NumberFromDouble(ceiling(x));
5959}
5960
5961
5962static Object* Runtime_Math_cos(Arguments args) {
5963 NoHandleAllocation ha;
5964 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005965 Counters::math_cos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005966
5967 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005968 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005969}
5970
5971
5972static Object* Runtime_Math_exp(Arguments args) {
5973 NoHandleAllocation ha;
5974 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005975 Counters::math_exp.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005976
5977 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005978 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005979}
5980
5981
5982static Object* Runtime_Math_floor(Arguments args) {
5983 NoHandleAllocation ha;
5984 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005985 Counters::math_floor.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005986
5987 CONVERT_DOUBLE_CHECKED(x, args[0]);
5988 return Heap::NumberFromDouble(floor(x));
5989}
5990
5991
5992static Object* Runtime_Math_log(Arguments args) {
5993 NoHandleAllocation ha;
5994 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005995 Counters::math_log.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005996
5997 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005998 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005999}
6000
6001
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006002// Helper function to compute x^y, where y is known to be an
6003// integer. Uses binary decomposition to limit the number of
6004// multiplications; see the discussion in "Hacker's Delight" by Henry
6005// S. Warren, Jr., figure 11-6, page 213.
6006static double powi(double x, int y) {
6007 ASSERT(y != kMinInt);
6008 unsigned n = (y < 0) ? -y : y;
6009 double m = x;
6010 double p = 1;
6011 while (true) {
6012 if ((n & 1) != 0) p *= m;
6013 n >>= 1;
6014 if (n == 0) {
6015 if (y < 0) {
6016 // Unfortunately, we have to be careful when p has reached
6017 // infinity in the computation, because sometimes the higher
6018 // internal precision in the pow() implementation would have
6019 // given us a finite p. This happens very rarely.
6020 double result = 1.0 / p;
6021 return (result == 0 && isinf(p))
6022 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
6023 : result;
6024 } else {
6025 return p;
6026 }
6027 }
6028 m *= m;
6029 }
6030}
6031
6032
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006033static Object* Runtime_Math_pow(Arguments args) {
6034 NoHandleAllocation ha;
6035 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006036 Counters::math_pow.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006037
6038 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006039
6040 // If the second argument is a smi, it is much faster to call the
6041 // custom powi() function than the generic pow().
6042 if (args[1]->IsSmi()) {
6043 int y = Smi::cast(args[1])->value();
6044 return Heap::AllocateHeapNumber(powi(x, y));
6045 }
6046
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006047 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00006048
6049 if (!isinf(x)) {
6050 if (y == 0.5) {
6051 // It's not uncommon to use Math.pow(x, 0.5) to compute the
6052 // square root of a number. To speed up such computations, we
6053 // explictly check for this case and use the sqrt() function
6054 // which is faster than pow().
6055 return Heap::AllocateHeapNumber(sqrt(x));
6056 } else if (y == -0.5) {
6057 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
6058 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
6059 }
6060 }
6061
6062 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006063 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006064 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6065 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006066 } else {
6067 return Heap::AllocateHeapNumber(pow(x, y));
6068 }
6069}
6070
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006071// Fast version of Math.pow if we know that y is not an integer and
6072// y is not -0.5 or 0.5. Used as slowcase from codegen.
6073static Object* Runtime_Math_pow_cfunction(Arguments args) {
6074 NoHandleAllocation ha;
6075 ASSERT(args.length() == 2);
6076 CONVERT_DOUBLE_CHECKED(x, args[0]);
6077 CONVERT_DOUBLE_CHECKED(y, args[1]);
6078 if (y == 0) {
6079 return Smi::FromInt(1);
6080 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6081 return Heap::nan_value();
6082 } else {
6083 return Heap::AllocateHeapNumber(pow(x, y));
6084 }
6085}
6086
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006087
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006088static Object* Runtime_RoundNumber(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006089 NoHandleAllocation ha;
6090 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006091 Counters::math_round.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006092
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006093 if (!args[0]->IsHeapNumber()) {
6094 // Must be smi. Return the argument unchanged for all the other types
6095 // to make fuzz-natives test happy.
6096 return args[0];
6097 }
6098
6099 HeapNumber* number = reinterpret_cast<HeapNumber*>(args[0]);
6100
6101 double value = number->value();
6102 int exponent = number->get_exponent();
6103 int sign = number->get_sign();
6104
6105 // We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
6106 // should be rounded to 2^30, which is not smi.
6107 if (!sign && exponent <= kSmiValueSize - 3) {
6108 return Smi::FromInt(static_cast<int>(value + 0.5));
6109 }
6110
6111 // If the magnitude is big enough, there's no place for fraction part. If we
6112 // try to add 0.5 to this number, 1.0 will be added instead.
6113 if (exponent >= 52) {
6114 return number;
6115 }
6116
6117 if (sign && value >= -0.5) return Heap::minus_zero_value();
6118
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006119 // Do not call NumberFromDouble() to avoid extra checks.
6120 return Heap::AllocateHeapNumber(floor(value + 0.5));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006121}
6122
6123
6124static Object* Runtime_Math_sin(Arguments args) {
6125 NoHandleAllocation ha;
6126 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006127 Counters::math_sin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006128
6129 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006130 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006131}
6132
6133
6134static Object* Runtime_Math_sqrt(Arguments args) {
6135 NoHandleAllocation ha;
6136 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006137 Counters::math_sqrt.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006138
6139 CONVERT_DOUBLE_CHECKED(x, args[0]);
6140 return Heap::AllocateHeapNumber(sqrt(x));
6141}
6142
6143
6144static Object* Runtime_Math_tan(Arguments args) {
6145 NoHandleAllocation ha;
6146 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006147 Counters::math_tan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006148
6149 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006150 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006151}
6152
6153
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006154static int MakeDay(int year, int month, int day) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006155 static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
6156 181, 212, 243, 273, 304, 334};
6157 static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
6158 182, 213, 244, 274, 305, 335};
6159
6160 year += month / 12;
6161 month %= 12;
6162 if (month < 0) {
6163 year--;
6164 month += 12;
6165 }
6166
6167 ASSERT(month >= 0);
6168 ASSERT(month < 12);
6169
6170 // year_delta is an arbitrary number such that:
6171 // a) year_delta = -1 (mod 400)
6172 // b) year + year_delta > 0 for years in the range defined by
6173 // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
6174 // Jan 1 1970. This is required so that we don't run into integer
6175 // division of negative numbers.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006176 // c) there shouldn't be an overflow for 32-bit integers in the following
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006177 // operations.
6178 static const int year_delta = 399999;
6179 static const int base_day = 365 * (1970 + year_delta) +
6180 (1970 + year_delta) / 4 -
6181 (1970 + year_delta) / 100 +
6182 (1970 + year_delta) / 400;
6183
6184 int year1 = year + year_delta;
6185 int day_from_year = 365 * year1 +
6186 year1 / 4 -
6187 year1 / 100 +
6188 year1 / 400 -
6189 base_day;
6190
6191 if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006192 return day_from_year + day_from_month[month] + day - 1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006193 }
6194
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006195 return day_from_year + day_from_month_leap[month] + day - 1;
6196}
6197
6198
6199static Object* Runtime_DateMakeDay(Arguments args) {
6200 NoHandleAllocation ha;
6201 ASSERT(args.length() == 3);
6202
6203 CONVERT_SMI_CHECKED(year, args[0]);
6204 CONVERT_SMI_CHECKED(month, args[1]);
6205 CONVERT_SMI_CHECKED(date, args[2]);
6206
6207 return Smi::FromInt(MakeDay(year, month, date));
6208}
6209
6210
6211static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
6212static const int kDaysIn4Years = 4 * 365 + 1;
6213static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
6214static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
6215static const int kDays1970to2000 = 30 * 365 + 7;
6216static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
6217 kDays1970to2000;
6218static const int kYearsOffset = 400000;
6219
6220static const char kDayInYear[] = {
6221 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6222 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6223 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6224 22, 23, 24, 25, 26, 27, 28,
6225 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6226 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6227 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6228 22, 23, 24, 25, 26, 27, 28, 29, 30,
6229 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6230 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6231 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6232 22, 23, 24, 25, 26, 27, 28, 29, 30,
6233 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6234 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6235 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6236 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
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,
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, 31,
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,
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, 31,
6245
6246 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6247 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6248 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6249 22, 23, 24, 25, 26, 27, 28,
6250 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6251 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6252 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6253 22, 23, 24, 25, 26, 27, 28, 29, 30,
6254 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6255 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6256 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6257 22, 23, 24, 25, 26, 27, 28, 29, 30,
6258 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6259 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6260 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6261 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
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,
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, 31,
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,
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, 31,
6270
6271 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6272 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6273 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6274 22, 23, 24, 25, 26, 27, 28, 29,
6275 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6276 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6277 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6278 22, 23, 24, 25, 26, 27, 28, 29, 30,
6279 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6280 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6281 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6282 22, 23, 24, 25, 26, 27, 28, 29, 30,
6283 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6284 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6285 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6286 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6287 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6288 22, 23, 24, 25, 26, 27, 28, 29, 30,
6289 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6290 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6291 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6292 22, 23, 24, 25, 26, 27, 28, 29, 30,
6293 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6294 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6295
6296 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6297 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6298 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6299 22, 23, 24, 25, 26, 27, 28,
6300 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6301 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6302 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6303 22, 23, 24, 25, 26, 27, 28, 29, 30,
6304 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6305 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6306 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6307 22, 23, 24, 25, 26, 27, 28, 29, 30,
6308 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6309 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6310 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6311 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6312 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6313 22, 23, 24, 25, 26, 27, 28, 29, 30,
6314 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6315 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6316 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6317 22, 23, 24, 25, 26, 27, 28, 29, 30,
6318 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6319 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
6320
6321static const char kMonthInYear[] = {
6322 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,
6323 0, 0, 0, 0, 0, 0,
6324 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,
6325 1, 1, 1,
6326 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,
6327 2, 2, 2, 2, 2, 2,
6328 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,
6329 3, 3, 3, 3, 3,
6330 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,
6331 4, 4, 4, 4, 4, 4,
6332 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,
6333 5, 5, 5, 5, 5,
6334 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,
6335 6, 6, 6, 6, 6, 6,
6336 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,
6337 7, 7, 7, 7, 7, 7,
6338 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,
6339 8, 8, 8, 8, 8,
6340 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,
6341 9, 9, 9, 9, 9, 9,
6342 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6343 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6344 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6345 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6346
6347 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,
6348 0, 0, 0, 0, 0, 0,
6349 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,
6350 1, 1, 1,
6351 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,
6352 2, 2, 2, 2, 2, 2,
6353 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,
6354 3, 3, 3, 3, 3,
6355 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,
6356 4, 4, 4, 4, 4, 4,
6357 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,
6358 5, 5, 5, 5, 5,
6359 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,
6360 6, 6, 6, 6, 6, 6,
6361 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,
6362 7, 7, 7, 7, 7, 7,
6363 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,
6364 8, 8, 8, 8, 8,
6365 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,
6366 9, 9, 9, 9, 9, 9,
6367 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6368 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6369 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6370 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6371
6372 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,
6373 0, 0, 0, 0, 0, 0,
6374 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,
6375 1, 1, 1, 1,
6376 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,
6377 2, 2, 2, 2, 2, 2,
6378 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,
6379 3, 3, 3, 3, 3,
6380 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,
6381 4, 4, 4, 4, 4, 4,
6382 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,
6383 5, 5, 5, 5, 5,
6384 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,
6385 6, 6, 6, 6, 6, 6,
6386 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,
6387 7, 7, 7, 7, 7, 7,
6388 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,
6389 8, 8, 8, 8, 8,
6390 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,
6391 9, 9, 9, 9, 9, 9,
6392 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6393 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6394 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6395 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6396
6397 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,
6398 0, 0, 0, 0, 0, 0,
6399 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,
6400 1, 1, 1,
6401 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,
6402 2, 2, 2, 2, 2, 2,
6403 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,
6404 3, 3, 3, 3, 3,
6405 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,
6406 4, 4, 4, 4, 4, 4,
6407 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,
6408 5, 5, 5, 5, 5,
6409 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,
6410 6, 6, 6, 6, 6, 6,
6411 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,
6412 7, 7, 7, 7, 7, 7,
6413 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,
6414 8, 8, 8, 8, 8,
6415 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,
6416 9, 9, 9, 9, 9, 9,
6417 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6418 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6419 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6420 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
6421
6422
6423// This function works for dates from 1970 to 2099.
6424static inline void DateYMDFromTimeAfter1970(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006425 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006426#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006427 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006428#endif
6429
6430 year = 1970 + (4 * date + 2) / kDaysIn4Years;
6431 date %= kDaysIn4Years;
6432
6433 month = kMonthInYear[date];
6434 day = kDayInYear[date];
6435
6436 ASSERT(MakeDay(year, month, day) == save_date);
6437}
6438
6439
6440static inline void DateYMDFromTimeSlow(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006441 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006442#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006443 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006444#endif
6445
6446 date += kDaysOffset;
6447 year = 400 * (date / kDaysIn400Years) - kYearsOffset;
6448 date %= kDaysIn400Years;
6449
6450 ASSERT(MakeDay(year, 0, 1) + date == save_date);
6451
6452 date--;
6453 int yd1 = date / kDaysIn100Years;
6454 date %= kDaysIn100Years;
6455 year += 100 * yd1;
6456
6457 date++;
6458 int yd2 = date / kDaysIn4Years;
6459 date %= kDaysIn4Years;
6460 year += 4 * yd2;
6461
6462 date--;
6463 int yd3 = date / 365;
6464 date %= 365;
6465 year += yd3;
6466
6467 bool is_leap = (!yd1 || yd2) && !yd3;
6468
6469 ASSERT(date >= -1);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006470 ASSERT(is_leap || (date >= 0));
6471 ASSERT((date < 365) || (is_leap && (date < 366)));
6472 ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0))));
6473 ASSERT(is_leap || ((MakeDay(year, 0, 1) + date) == save_date));
6474 ASSERT(!is_leap || ((MakeDay(year, 0, 1) + date + 1) == save_date));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006475
6476 if (is_leap) {
6477 day = kDayInYear[2*365 + 1 + date];
6478 month = kMonthInYear[2*365 + 1 + date];
6479 } else {
6480 day = kDayInYear[date];
6481 month = kMonthInYear[date];
6482 }
6483
6484 ASSERT(MakeDay(year, month, day) == save_date);
6485}
6486
6487
6488static inline void DateYMDFromTime(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006489 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006490 if (date >= 0 && date < 32 * kDaysIn4Years) {
6491 DateYMDFromTimeAfter1970(date, year, month, day);
6492 } else {
6493 DateYMDFromTimeSlow(date, year, month, day);
6494 }
6495}
6496
6497
6498static Object* Runtime_DateYMDFromTime(Arguments args) {
6499 NoHandleAllocation ha;
6500 ASSERT(args.length() == 2);
6501
6502 CONVERT_DOUBLE_CHECKED(t, args[0]);
6503 CONVERT_CHECKED(JSArray, res_array, args[1]);
6504
6505 int year, month, day;
6506 DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
6507
6508 res_array->SetElement(0, Smi::FromInt(year));
6509 res_array->SetElement(1, Smi::FromInt(month));
6510 res_array->SetElement(2, Smi::FromInt(day));
6511
6512 return Heap::undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006513}
6514
6515
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006516static Object* Runtime_NewArgumentsFast(Arguments args) {
6517 NoHandleAllocation ha;
6518 ASSERT(args.length() == 3);
6519
6520 JSFunction* callee = JSFunction::cast(args[0]);
6521 Object** parameters = reinterpret_cast<Object**>(args[1]);
6522 const int length = Smi::cast(args[2])->value();
6523
6524 Object* result = Heap::AllocateArgumentsObject(callee, length);
6525 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006526 // Allocate the elements if needed.
6527 if (length > 0) {
6528 // Allocate the fixed array.
6529 Object* obj = Heap::AllocateRawFixedArray(length);
6530 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006531
6532 AssertNoAllocation no_gc;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006533 FixedArray* array = reinterpret_cast<FixedArray*>(obj);
6534 array->set_map(Heap::fixed_array_map());
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006535 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006536
6537 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006538 for (int i = 0; i < length; i++) {
6539 array->set(i, *--parameters, mode);
6540 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006541 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006542 }
6543 return result;
6544}
6545
6546
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006547static Object* Runtime_NewClosure(Arguments args) {
6548 HandleScope scope;
6549 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00006550 CONVERT_ARG_CHECKED(Context, context, 0);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006551 CONVERT_ARG_CHECKED(SharedFunctionInfo, shared, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006552
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00006553 PretenureFlag pretenure = (context->global_context() == *context)
6554 ? TENURED // Allocate global closures in old space.
6555 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006556 Handle<JSFunction> result =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006557 Factory::NewFunctionFromSharedFunctionInfo(shared, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006558 return *result;
6559}
6560
6561
ager@chromium.org5c838252010-02-19 08:53:10 +00006562static Code* ComputeConstructStub(Handle<JSFunction> function) {
6563 Handle<Object> prototype = Factory::null_value();
6564 if (function->has_instance_prototype()) {
6565 prototype = Handle<Object>(function->instance_prototype());
6566 }
6567 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006568 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00006569 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006570 if (code->IsFailure()) {
6571 return Builtins::builtin(Builtins::JSConstructStubGeneric);
6572 }
6573 return Code::cast(code);
6574 }
6575
ager@chromium.org5c838252010-02-19 08:53:10 +00006576 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006577}
6578
6579
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006580static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006581 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006582 ASSERT(args.length() == 1);
6583
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006584 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006585
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006586 // If the constructor isn't a proper function we throw a type error.
6587 if (!constructor->IsJSFunction()) {
6588 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6589 Handle<Object> type_error =
6590 Factory::NewTypeError("not_constructor", arguments);
6591 return Top::Throw(*type_error);
6592 }
6593
6594 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006595
6596 // If function should not have prototype, construction is not allowed. In this
6597 // case generated code bailouts here, since function has no initial_map.
6598 if (!function->should_have_prototype()) {
6599 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6600 Handle<Object> type_error =
6601 Factory::NewTypeError("not_constructor", arguments);
6602 return Top::Throw(*type_error);
6603 }
6604
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006605#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006606 // Handle stepping into constructors if step into is active.
6607 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006608 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006609 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006610#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006611
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006612 if (function->has_initial_map()) {
6613 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006614 // The 'Function' function ignores the receiver object when
6615 // called using 'new' and creates a new JSFunction object that
6616 // is returned. The receiver object is only used for error
6617 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006618 // JSFunction. Factory::NewJSObject() should not be used to
6619 // allocate JSFunctions since it does not properly initialize
6620 // the shared part of the function. Since the receiver is
6621 // ignored anyway, we use the global object as the receiver
6622 // instead of a new JSFunction object. This way, errors are
6623 // reported the same way whether or not 'Function' is called
6624 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006625 return Top::context()->global();
6626 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006627 }
6628
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006629 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006630 Handle<SharedFunctionInfo> shared(function->shared());
6631 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006632
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006633 bool first_allocation = !function->has_initial_map();
6634 Handle<JSObject> result = Factory::NewJSObject(function);
6635 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006636 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00006637 ComputeConstructStub(Handle<JSFunction>(function)));
6638 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006639 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006640
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006641 Counters::constructed_objects.Increment();
6642 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006643
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006644 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006645}
6646
6647
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006648static Object* Runtime_LazyCompile(Arguments args) {
6649 HandleScope scope;
6650 ASSERT(args.length() == 1);
6651
6652 Handle<JSFunction> function = args.at<JSFunction>(0);
6653#ifdef DEBUG
6654 if (FLAG_trace_lazy) {
6655 PrintF("[lazy: ");
6656 function->shared()->name()->Print();
6657 PrintF("]\n");
6658 }
6659#endif
6660
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006661 // Compile the target function. Here we compile using CompileLazyInLoop in
6662 // order to get the optimized version. This helps code like delta-blue
6663 // that calls performance-critical routines through constructors. A
6664 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
6665 // direct call. Since the in-loop tracking takes place through CallICs
6666 // this means that things called through constructors are never known to
6667 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006668 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006669 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006670 return Failure::Exception();
6671 }
6672
6673 return function->code();
6674}
6675
6676
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006677static Object* Runtime_GetFunctionDelegate(Arguments args) {
6678 HandleScope scope;
6679 ASSERT(args.length() == 1);
6680 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6681 return *Execution::GetFunctionDelegate(args.at<Object>(0));
6682}
6683
6684
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00006685static Object* Runtime_GetConstructorDelegate(Arguments args) {
6686 HandleScope scope;
6687 ASSERT(args.length() == 1);
6688 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6689 return *Execution::GetConstructorDelegate(args.at<Object>(0));
6690}
6691
6692
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006693static Object* Runtime_NewContext(Arguments args) {
6694 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00006695 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006696
kasper.lund7276f142008-07-30 08:49:36 +00006697 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006698 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
6699 Object* result = Heap::AllocateFunctionContext(length, function);
6700 if (result->IsFailure()) return result;
6701
6702 Top::set_context(Context::cast(result));
6703
kasper.lund7276f142008-07-30 08:49:36 +00006704 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006705}
6706
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006707static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006708 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006709 Object* js_object = object;
6710 if (!js_object->IsJSObject()) {
6711 js_object = js_object->ToObject();
6712 if (js_object->IsFailure()) {
6713 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006714 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006715 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006716 Handle<Object> result =
6717 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
6718 return Top::Throw(*result);
6719 }
6720 }
6721
6722 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006723 Heap::AllocateWithContext(Top::context(),
6724 JSObject::cast(js_object),
6725 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006726 if (result->IsFailure()) return result;
6727
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006728 Context* context = Context::cast(result);
6729 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006730
kasper.lund7276f142008-07-30 08:49:36 +00006731 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006732}
6733
6734
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006735static Object* Runtime_PushContext(Arguments args) {
6736 NoHandleAllocation ha;
6737 ASSERT(args.length() == 1);
6738 return PushContextHelper(args[0], false);
6739}
6740
6741
6742static Object* Runtime_PushCatchContext(Arguments args) {
6743 NoHandleAllocation ha;
6744 ASSERT(args.length() == 1);
6745 return PushContextHelper(args[0], true);
6746}
6747
6748
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006749static Object* Runtime_LookupContext(Arguments args) {
6750 HandleScope scope;
6751 ASSERT(args.length() == 2);
6752
6753 CONVERT_ARG_CHECKED(Context, context, 0);
6754 CONVERT_ARG_CHECKED(String, name, 1);
6755
6756 int index;
6757 PropertyAttributes attributes;
6758 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006759 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006760 context->Lookup(name, flags, &index, &attributes);
6761
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006762 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006763 ASSERT(holder->IsJSObject());
6764 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006765 }
6766
6767 // No intermediate context found. Use global object by default.
6768 return Top::context()->global();
6769}
6770
6771
ager@chromium.orga1645e22009-09-09 19:27:10 +00006772// A mechanism to return a pair of Object pointers in registers (if possible).
6773// How this is achieved is calling convention-dependent.
6774// All currently supported x86 compiles uses calling conventions that are cdecl
6775// variants where a 64-bit value is returned in two 32-bit registers
6776// (edx:eax on ia32, r1:r0 on ARM).
6777// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
6778// In Win64 calling convention, a struct of two pointers is returned in memory,
6779// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006780#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006781struct ObjectPair {
6782 Object* x;
6783 Object* y;
6784};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006785
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006786static inline ObjectPair MakePair(Object* x, Object* y) {
6787 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006788 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
6789 // In Win64 they are assigned to a hidden first argument.
6790 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006791}
6792#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006793typedef uint64_t ObjectPair;
6794static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006795 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006796 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006797}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006798#endif
6799
6800
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006801static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006802 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
6803 USE(attributes);
6804 return x->IsTheHole() ? Heap::undefined_value() : x;
6805}
6806
6807
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006808static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
6809 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006810 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006811 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006812 JSFunction* context_extension_function =
6813 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006814 // If the holder isn't a context extension object, we just return it
6815 // as the receiver. This allows arguments objects to be used as
6816 // receivers, but only if they are put in the context scope chain
6817 // explicitly via a with-statement.
6818 Object* constructor = holder->map()->constructor();
6819 if (constructor != context_extension_function) return holder;
6820 // Fall back to using the global object as the receiver if the
6821 // property turns out to be a local variable allocated in a context
6822 // extension object - introduced via eval.
6823 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006824}
6825
6826
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006827static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006828 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00006829 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006830
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006831 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00006832 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006833 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006834 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006835 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006836
6837 int index;
6838 PropertyAttributes attributes;
6839 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006840 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006841 context->Lookup(name, flags, &index, &attributes);
6842
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006843 // If the index is non-negative, the slot has been found in a local
6844 // variable or a parameter. Read it from the context object or the
6845 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006846 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006847 // If the "property" we were looking for is a local variable or an
6848 // argument in a context, the receiver is the global object; see
6849 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
6850 JSObject* receiver = Top::context()->global()->global_receiver();
6851 Object* value = (holder->IsContext())
6852 ? Context::cast(*holder)->get(index)
6853 : JSObject::cast(*holder)->GetElement(index);
6854 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006855 }
6856
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006857 // If the holder is found, we read the property from it.
6858 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006859 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006860 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006861 JSObject* receiver;
6862 if (object->IsGlobalObject()) {
6863 receiver = GlobalObject::cast(object)->global_receiver();
6864 } else if (context->is_exception_holder(*holder)) {
6865 receiver = Top::context()->global()->global_receiver();
6866 } else {
6867 receiver = ComputeReceiverForNonGlobal(object);
6868 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006869 // No need to unhole the value here. This is taken care of by the
6870 // GetProperty function.
6871 Object* value = object->GetProperty(*name);
6872 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006873 }
6874
6875 if (throw_error) {
6876 // The property doesn't exist - throw exception.
6877 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006878 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006879 return MakePair(Top::Throw(*reference_error), NULL);
6880 } else {
6881 // The property doesn't exist - return undefined
6882 return MakePair(Heap::undefined_value(), Heap::undefined_value());
6883 }
6884}
6885
6886
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006887static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006888 return LoadContextSlotHelper(args, true);
6889}
6890
6891
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006892static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006893 return LoadContextSlotHelper(args, false);
6894}
6895
6896
6897static Object* Runtime_StoreContextSlot(Arguments args) {
6898 HandleScope scope;
6899 ASSERT(args.length() == 3);
6900
6901 Handle<Object> value(args[0]);
6902 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006903 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006904
6905 int index;
6906 PropertyAttributes attributes;
6907 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006908 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006909 context->Lookup(name, flags, &index, &attributes);
6910
6911 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006912 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006913 // Ignore if read_only variable.
6914 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006915 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006916 }
6917 } else {
6918 ASSERT((attributes & READ_ONLY) == 0);
6919 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006920 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006921 USE(result);
6922 ASSERT(!result->IsFailure());
6923 }
6924 return *value;
6925 }
6926
6927 // Slow case: The property is not in a FixedArray context.
6928 // It is either in an JSObject extension context or it was not found.
6929 Handle<JSObject> context_ext;
6930
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006931 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006932 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006933 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006934 } else {
6935 // The property was not found. It needs to be stored in the global context.
6936 ASSERT(attributes == ABSENT);
6937 attributes = NONE;
6938 context_ext = Handle<JSObject>(Top::context()->global());
6939 }
6940
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006941 // Set the property, but ignore if read_only variable on the context
6942 // extension object itself.
6943 if ((attributes & READ_ONLY) == 0 ||
6944 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006945 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
6946 if (set.is_null()) {
6947 // Failure::Exception is converted to a null handle in the
6948 // handle-based methods such as SetProperty. We therefore need
6949 // to convert null handles back to exceptions.
6950 ASSERT(Top::has_pending_exception());
6951 return Failure::Exception();
6952 }
6953 }
6954 return *value;
6955}
6956
6957
6958static Object* Runtime_Throw(Arguments args) {
6959 HandleScope scope;
6960 ASSERT(args.length() == 1);
6961
6962 return Top::Throw(args[0]);
6963}
6964
6965
6966static Object* Runtime_ReThrow(Arguments args) {
6967 HandleScope scope;
6968 ASSERT(args.length() == 1);
6969
6970 return Top::ReThrow(args[0]);
6971}
6972
6973
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006974static Object* Runtime_PromoteScheduledException(Arguments args) {
6975 ASSERT_EQ(0, args.length());
6976 return Top::PromoteScheduledException();
6977}
6978
6979
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006980static Object* Runtime_ThrowReferenceError(Arguments args) {
6981 HandleScope scope;
6982 ASSERT(args.length() == 1);
6983
6984 Handle<Object> name(args[0]);
6985 Handle<Object> reference_error =
6986 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
6987 return Top::Throw(*reference_error);
6988}
6989
6990
6991static Object* Runtime_StackOverflow(Arguments args) {
6992 NoHandleAllocation na;
6993 return Top::StackOverflow();
6994}
6995
6996
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006997static Object* Runtime_StackGuard(Arguments args) {
6998 ASSERT(args.length() == 1);
6999
7000 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007001 if (StackGuard::IsStackOverflow()) {
7002 return Runtime_StackOverflow(args);
7003 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007004
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007005 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007006}
7007
7008
7009// NOTE: These PrintXXX functions are defined for all builds (not just
7010// DEBUG builds) because we may want to be able to trace function
7011// calls in all modes.
7012static void PrintString(String* str) {
7013 // not uncommon to have empty strings
7014 if (str->length() > 0) {
7015 SmartPointer<char> s =
7016 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
7017 PrintF("%s", *s);
7018 }
7019}
7020
7021
7022static void PrintObject(Object* obj) {
7023 if (obj->IsSmi()) {
7024 PrintF("%d", Smi::cast(obj)->value());
7025 } else if (obj->IsString() || obj->IsSymbol()) {
7026 PrintString(String::cast(obj));
7027 } else if (obj->IsNumber()) {
7028 PrintF("%g", obj->Number());
7029 } else if (obj->IsFailure()) {
7030 PrintF("<failure>");
7031 } else if (obj->IsUndefined()) {
7032 PrintF("<undefined>");
7033 } else if (obj->IsNull()) {
7034 PrintF("<null>");
7035 } else if (obj->IsTrue()) {
7036 PrintF("<true>");
7037 } else if (obj->IsFalse()) {
7038 PrintF("<false>");
7039 } else {
7040 PrintF("%p", obj);
7041 }
7042}
7043
7044
7045static int StackSize() {
7046 int n = 0;
7047 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
7048 return n;
7049}
7050
7051
7052static void PrintTransition(Object* result) {
7053 // indentation
7054 { const int nmax = 80;
7055 int n = StackSize();
7056 if (n <= nmax)
7057 PrintF("%4d:%*s", n, n, "");
7058 else
7059 PrintF("%4d:%*s", n, nmax, "...");
7060 }
7061
7062 if (result == NULL) {
7063 // constructor calls
7064 JavaScriptFrameIterator it;
7065 JavaScriptFrame* frame = it.frame();
7066 if (frame->IsConstructor()) PrintF("new ");
7067 // function name
7068 Object* fun = frame->function();
7069 if (fun->IsJSFunction()) {
7070 PrintObject(JSFunction::cast(fun)->shared()->name());
7071 } else {
7072 PrintObject(fun);
7073 }
7074 // function arguments
7075 // (we are intentionally only printing the actually
7076 // supplied parameters, not all parameters required)
7077 PrintF("(this=");
7078 PrintObject(frame->receiver());
7079 const int length = frame->GetProvidedParametersCount();
7080 for (int i = 0; i < length; i++) {
7081 PrintF(", ");
7082 PrintObject(frame->GetParameter(i));
7083 }
7084 PrintF(") {\n");
7085
7086 } else {
7087 // function result
7088 PrintF("} -> ");
7089 PrintObject(result);
7090 PrintF("\n");
7091 }
7092}
7093
7094
7095static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007096 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007097 NoHandleAllocation ha;
7098 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007099 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007100}
7101
7102
7103static Object* Runtime_TraceExit(Arguments args) {
7104 NoHandleAllocation ha;
7105 PrintTransition(args[0]);
7106 return args[0]; // return TOS
7107}
7108
7109
7110static Object* Runtime_DebugPrint(Arguments args) {
7111 NoHandleAllocation ha;
7112 ASSERT(args.length() == 1);
7113
7114#ifdef DEBUG
7115 if (args[0]->IsString()) {
7116 // If we have a string, assume it's a code "marker"
7117 // and print some interesting cpu debugging info.
7118 JavaScriptFrameIterator it;
7119 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007120 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
7121 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007122 } else {
7123 PrintF("DebugPrint: ");
7124 }
7125 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007126 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007127 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007128 HeapObject::cast(args[0])->map()->Print();
7129 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007130#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007131 // ShortPrint is available in release mode. Print is not.
7132 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007133#endif
7134 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00007135 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007136
7137 return args[0]; // return TOS
7138}
7139
7140
7141static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007142 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007143 NoHandleAllocation ha;
7144 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007145 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007146}
7147
7148
mads.s.ager31e71382008-08-13 09:32:07 +00007149static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007150 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007151 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007152
7153 // According to ECMA-262, section 15.9.1, page 117, the precision of
7154 // the number in a Date object representing a particular instant in
7155 // time is milliseconds. Therefore, we floor the result of getting
7156 // the OS time.
7157 double millis = floor(OS::TimeCurrentMillis());
7158 return Heap::NumberFromDouble(millis);
7159}
7160
7161
7162static Object* Runtime_DateParseString(Arguments args) {
7163 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007164 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007165
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007166 CONVERT_ARG_CHECKED(String, str, 0);
7167 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007168
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007169 CONVERT_ARG_CHECKED(JSArray, output, 1);
7170 RUNTIME_ASSERT(output->HasFastElements());
7171
7172 AssertNoAllocation no_allocation;
7173
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007174 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007175 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
7176 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007177 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007178 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007179 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007180 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007181 result = DateParser::Parse(str->ToUC16Vector(), output_array);
7182 }
7183
7184 if (result) {
7185 return *output;
7186 } else {
7187 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007188 }
7189}
7190
7191
7192static Object* Runtime_DateLocalTimezone(Arguments args) {
7193 NoHandleAllocation ha;
7194 ASSERT(args.length() == 1);
7195
7196 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007197 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007198 return Heap::AllocateStringFromUtf8(CStrVector(zone));
7199}
7200
7201
7202static Object* Runtime_DateLocalTimeOffset(Arguments args) {
7203 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007204 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007205
7206 return Heap::NumberFromDouble(OS::LocalTimeOffset());
7207}
7208
7209
7210static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
7211 NoHandleAllocation ha;
7212 ASSERT(args.length() == 1);
7213
7214 CONVERT_DOUBLE_CHECKED(x, args[0]);
7215 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
7216}
7217
7218
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007219static Object* Runtime_GlobalReceiver(Arguments args) {
7220 ASSERT(args.length() == 1);
7221 Object* global = args[0];
7222 if (!global->IsJSGlobalObject()) return Heap::null_value();
7223 return JSGlobalObject::cast(global)->global_receiver();
7224}
7225
7226
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007227static Object* Runtime_CompileString(Arguments args) {
7228 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007229 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007230 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007231 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007232
ager@chromium.org381abbb2009-02-25 13:23:22 +00007233 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007234 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007235 Compiler::ValidationState validate = (is_json->IsTrue())
7236 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007237 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
7238 context,
7239 true,
7240 validate);
7241 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007242 Handle<JSFunction> fun =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007243 Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007244 return *fun;
7245}
7246
7247
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007248static ObjectPair CompileGlobalEval(Handle<String> source,
7249 Handle<Object> receiver) {
7250 // Deal with a normal eval call with a string argument. Compile it
7251 // and return the compiled function bound in the local context.
7252 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
7253 source,
7254 Handle<Context>(Top::context()),
7255 Top::context()->IsGlobalContext(),
7256 Compiler::DONT_VALIDATE_JSON);
7257 if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
7258 Handle<JSFunction> compiled = Factory::NewFunctionFromSharedFunctionInfo(
7259 shared,
7260 Handle<Context>(Top::context()),
7261 NOT_TENURED);
7262 return MakePair(*compiled, *receiver);
7263}
7264
7265
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007266static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
7267 ASSERT(args.length() == 3);
7268 if (!args[0]->IsJSFunction()) {
7269 return MakePair(Top::ThrowIllegalOperation(), NULL);
7270 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007271
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007272 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007273 Handle<JSFunction> callee = args.at<JSFunction>(0);
7274 Handle<Object> receiver; // Will be overwritten.
7275
7276 // Compute the calling context.
7277 Handle<Context> context = Handle<Context>(Top::context());
7278#ifdef DEBUG
7279 // Make sure Top::context() agrees with the old code that traversed
7280 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007281 StackFrameLocator locator;
7282 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007283 ASSERT(Context::cast(frame->context()) == *context);
7284#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007285
7286 // Find where the 'eval' symbol is bound. It is unaliased only if
7287 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007288 int index = -1;
7289 PropertyAttributes attributes = ABSENT;
7290 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007291 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
7292 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007293 // Stop search when eval is found or when the global context is
7294 // reached.
7295 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007296 if (context->is_function_context()) {
7297 context = Handle<Context>(Context::cast(context->closure()->context()));
7298 } else {
7299 context = Handle<Context>(context->previous());
7300 }
7301 }
7302
iposva@chromium.org245aa852009-02-10 00:49:54 +00007303 // If eval could not be resolved, it has been deleted and we need to
7304 // throw a reference error.
7305 if (attributes == ABSENT) {
7306 Handle<Object> name = Factory::eval_symbol();
7307 Handle<Object> reference_error =
7308 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007309 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007310 }
7311
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007312 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007313 // 'eval' is not bound in the global context. Just call the function
7314 // with the given arguments. This is not necessarily the global eval.
7315 if (receiver->IsContext()) {
7316 context = Handle<Context>::cast(receiver);
7317 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007318 } else if (receiver->IsJSContextExtensionObject()) {
7319 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007320 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007321 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007322 }
7323
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007324 // 'eval' is bound in the global context, but it may have been overwritten.
7325 // Compare it to the builtin 'GlobalEval' function to make sure.
7326 if (*callee != Top::global_context()->global_eval_fun() ||
7327 !args[1]->IsString()) {
7328 return MakePair(*callee, Top::context()->global()->global_receiver());
7329 }
7330
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007331 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
7332}
7333
7334
7335static ObjectPair Runtime_ResolvePossiblyDirectEvalNoLookup(Arguments args) {
7336 ASSERT(args.length() == 3);
7337 if (!args[0]->IsJSFunction()) {
7338 return MakePair(Top::ThrowIllegalOperation(), NULL);
7339 }
7340
7341 HandleScope scope;
7342 Handle<JSFunction> callee = args.at<JSFunction>(0);
7343
7344 // 'eval' is bound in the global context, but it may have been overwritten.
7345 // Compare it to the builtin 'GlobalEval' function to make sure.
7346 if (*callee != Top::global_context()->global_eval_fun() ||
7347 !args[1]->IsString()) {
7348 return MakePair(*callee, Top::context()->global()->global_receiver());
7349 }
7350
7351 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007352}
7353
7354
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007355static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
7356 // This utility adjusts the property attributes for newly created Function
7357 // object ("new Function(...)") by changing the map.
7358 // All it does is changing the prototype property to enumerable
7359 // as specified in ECMA262, 15.3.5.2.
7360 HandleScope scope;
7361 ASSERT(args.length() == 1);
7362 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7363 ASSERT(func->map()->instance_type() ==
7364 Top::function_instance_map()->instance_type());
7365 ASSERT(func->map()->instance_size() ==
7366 Top::function_instance_map()->instance_size());
7367 func->set_map(*Top::function_instance_map());
7368 return *func;
7369}
7370
7371
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007372// Push an array unto an array of arrays if it is not already in the
7373// array. Returns true if the element was pushed on the stack and
7374// false otherwise.
7375static Object* Runtime_PushIfAbsent(Arguments args) {
7376 ASSERT(args.length() == 2);
7377 CONVERT_CHECKED(JSArray, array, args[0]);
7378 CONVERT_CHECKED(JSArray, element, args[1]);
7379 RUNTIME_ASSERT(array->HasFastElements());
7380 int length = Smi::cast(array->length())->value();
7381 FixedArray* elements = FixedArray::cast(array->elements());
7382 for (int i = 0; i < length; i++) {
7383 if (elements->get(i) == element) return Heap::false_value();
7384 }
7385 Object* obj = array->SetFastElement(length, element);
7386 if (obj->IsFailure()) return obj;
7387 return Heap::true_value();
7388}
7389
7390
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007391/**
7392 * A simple visitor visits every element of Array's.
7393 * The backend storage can be a fixed array for fast elements case,
7394 * or a dictionary for sparse array. Since Dictionary is a subtype
7395 * of FixedArray, the class can be used by both fast and slow cases.
7396 * The second parameter of the constructor, fast_elements, specifies
7397 * whether the storage is a FixedArray or Dictionary.
7398 *
7399 * An index limit is used to deal with the situation that a result array
7400 * length overflows 32-bit non-negative integer.
7401 */
7402class ArrayConcatVisitor {
7403 public:
7404 ArrayConcatVisitor(Handle<FixedArray> storage,
7405 uint32_t index_limit,
7406 bool fast_elements) :
7407 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007408 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007409
7410 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007411 if (i >= index_limit_ - index_offset_) return;
7412 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007413
7414 if (fast_elements_) {
7415 ASSERT(index < static_cast<uint32_t>(storage_->length()));
7416 storage_->set(index, *elm);
7417
7418 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007419 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
7420 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007421 Factory::DictionaryAtNumberPut(dict, index, elm);
7422 if (!result.is_identical_to(dict))
7423 storage_ = result;
7424 }
7425 }
7426
7427 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007428 if (index_limit_ - index_offset_ < delta) {
7429 index_offset_ = index_limit_;
7430 } else {
7431 index_offset_ += delta;
7432 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007433 }
7434
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007435 Handle<FixedArray> storage() { return storage_; }
7436
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007437 private:
7438 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007439 // Limit on the accepted indices. Elements with indices larger than the
7440 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007441 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007442 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007443 uint32_t index_offset_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007444 bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007445};
7446
7447
ager@chromium.org3811b432009-10-28 14:53:37 +00007448template<class ExternalArrayClass, class ElementType>
7449static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
7450 bool elements_are_ints,
7451 bool elements_are_guaranteed_smis,
7452 uint32_t range,
7453 ArrayConcatVisitor* visitor) {
7454 Handle<ExternalArrayClass> array(
7455 ExternalArrayClass::cast(receiver->elements()));
7456 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
7457
7458 if (visitor != NULL) {
7459 if (elements_are_ints) {
7460 if (elements_are_guaranteed_smis) {
7461 for (uint32_t j = 0; j < len; j++) {
7462 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
7463 visitor->visit(j, e);
7464 }
7465 } else {
7466 for (uint32_t j = 0; j < len; j++) {
7467 int64_t val = static_cast<int64_t>(array->get(j));
7468 if (Smi::IsValid(static_cast<intptr_t>(val))) {
7469 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
7470 visitor->visit(j, e);
7471 } else {
7472 Handle<Object> e(
7473 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
7474 visitor->visit(j, e);
7475 }
7476 }
7477 }
7478 } else {
7479 for (uint32_t j = 0; j < len; j++) {
7480 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
7481 visitor->visit(j, e);
7482 }
7483 }
7484 }
7485
7486 return len;
7487}
7488
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007489/**
7490 * A helper function that visits elements of a JSObject. Only elements
7491 * whose index between 0 and range (exclusive) are visited.
7492 *
7493 * If the third parameter, visitor, is not NULL, the visitor is called
7494 * with parameters, 'visitor_index_offset + element index' and the element.
7495 *
7496 * It returns the number of visisted elements.
7497 */
7498static uint32_t IterateElements(Handle<JSObject> receiver,
7499 uint32_t range,
7500 ArrayConcatVisitor* visitor) {
7501 uint32_t num_of_elements = 0;
7502
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007503 switch (receiver->GetElementsKind()) {
7504 case JSObject::FAST_ELEMENTS: {
7505 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
7506 uint32_t len = elements->length();
7507 if (range < len) {
7508 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007509 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007510
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007511 for (uint32_t j = 0; j < len; j++) {
7512 Handle<Object> e(elements->get(j));
7513 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007514 num_of_elements++;
7515 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007516 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007517 }
7518 }
7519 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007520 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007521 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007522 case JSObject::PIXEL_ELEMENTS: {
7523 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
7524 uint32_t len = pixels->length();
7525 if (range < len) {
7526 len = range;
7527 }
7528
7529 for (uint32_t j = 0; j < len; j++) {
7530 num_of_elements++;
7531 if (visitor != NULL) {
7532 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
7533 visitor->visit(j, e);
7534 }
7535 }
7536 break;
7537 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007538 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
7539 num_of_elements =
7540 IterateExternalArrayElements<ExternalByteArray, int8_t>(
7541 receiver, true, true, range, visitor);
7542 break;
7543 }
7544 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7545 num_of_elements =
7546 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
7547 receiver, true, true, range, visitor);
7548 break;
7549 }
7550 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
7551 num_of_elements =
7552 IterateExternalArrayElements<ExternalShortArray, int16_t>(
7553 receiver, true, true, range, visitor);
7554 break;
7555 }
7556 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7557 num_of_elements =
7558 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
7559 receiver, true, true, range, visitor);
7560 break;
7561 }
7562 case JSObject::EXTERNAL_INT_ELEMENTS: {
7563 num_of_elements =
7564 IterateExternalArrayElements<ExternalIntArray, int32_t>(
7565 receiver, true, false, range, visitor);
7566 break;
7567 }
7568 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7569 num_of_elements =
7570 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
7571 receiver, true, false, range, visitor);
7572 break;
7573 }
7574 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
7575 num_of_elements =
7576 IterateExternalArrayElements<ExternalFloatArray, float>(
7577 receiver, false, false, range, visitor);
7578 break;
7579 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007580 case JSObject::DICTIONARY_ELEMENTS: {
7581 Handle<NumberDictionary> dict(receiver->element_dictionary());
7582 uint32_t capacity = dict->Capacity();
7583 for (uint32_t j = 0; j < capacity; j++) {
7584 Handle<Object> k(dict->KeyAt(j));
7585 if (dict->IsKey(*k)) {
7586 ASSERT(k->IsNumber());
7587 uint32_t index = static_cast<uint32_t>(k->Number());
7588 if (index < range) {
7589 num_of_elements++;
7590 if (visitor) {
7591 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
7592 }
7593 }
7594 }
7595 }
7596 break;
7597 }
7598 default:
7599 UNREACHABLE();
7600 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007601 }
7602
7603 return num_of_elements;
7604}
7605
7606
7607/**
7608 * A helper function that visits elements of an Array object, and elements
7609 * on its prototypes.
7610 *
7611 * Elements on prototypes are visited first, and only elements whose indices
7612 * less than Array length are visited.
7613 *
7614 * If a ArrayConcatVisitor object is given, the visitor is called with
7615 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007616 *
7617 * The returned number of elements is an upper bound on the actual number
7618 * of elements added. If the same element occurs in more than one object
7619 * in the array's prototype chain, it will be counted more than once, but
7620 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007621 */
7622static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
7623 ArrayConcatVisitor* visitor) {
7624 uint32_t range = static_cast<uint32_t>(array->length()->Number());
7625 Handle<Object> obj = array;
7626
7627 static const int kEstimatedPrototypes = 3;
7628 List< Handle<JSObject> > objects(kEstimatedPrototypes);
7629
7630 // Visit prototype first. If an element on the prototype is shadowed by
7631 // the inheritor using the same index, the ArrayConcatVisitor visits
7632 // the prototype element before the shadowing element.
7633 // The visitor can simply overwrite the old value by new value using
7634 // the same index. This follows Array::concat semantics.
7635 while (!obj->IsNull()) {
7636 objects.Add(Handle<JSObject>::cast(obj));
7637 obj = Handle<Object>(obj->GetPrototype());
7638 }
7639
7640 uint32_t nof_elements = 0;
7641 for (int i = objects.length() - 1; i >= 0; i--) {
7642 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007643 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007644 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007645
7646 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
7647 nof_elements = JSObject::kMaxElementCount;
7648 } else {
7649 nof_elements += encountered_elements;
7650 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007651 }
7652
7653 return nof_elements;
7654}
7655
7656
7657/**
7658 * A helper function of Runtime_ArrayConcat.
7659 *
7660 * The first argument is an Array of arrays and objects. It is the
7661 * same as the arguments array of Array::concat JS function.
7662 *
7663 * If an argument is an Array object, the function visits array
7664 * elements. If an argument is not an Array object, the function
7665 * visits the object as if it is an one-element array.
7666 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007667 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007668 * non-negative number is used as new length. For example, if one
7669 * array length is 2^32 - 1, second array length is 1, the
7670 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007671 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
7672 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007673 */
7674static uint32_t IterateArguments(Handle<JSArray> arguments,
7675 ArrayConcatVisitor* visitor) {
7676 uint32_t visited_elements = 0;
7677 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7678
7679 for (uint32_t i = 0; i < num_of_args; i++) {
7680 Handle<Object> obj(arguments->GetElement(i));
7681 if (obj->IsJSArray()) {
7682 Handle<JSArray> array = Handle<JSArray>::cast(obj);
7683 uint32_t len = static_cast<uint32_t>(array->length()->Number());
7684 uint32_t nof_elements =
7685 IterateArrayAndPrototypeElements(array, visitor);
7686 // Total elements of array and its prototype chain can be more than
7687 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007688 // the array length number of elements. We use the length as an estimate
7689 // for the actual number of elements added.
7690 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
7691 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
7692 visited_elements = JSArray::kMaxElementCount;
7693 } else {
7694 visited_elements += added_elements;
7695 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007696 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007697 } else {
7698 if (visitor) {
7699 visitor->visit(0, obj);
7700 visitor->increase_index_offset(1);
7701 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007702 if (visited_elements < JSArray::kMaxElementCount) {
7703 visited_elements++;
7704 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007705 }
7706 }
7707 return visited_elements;
7708}
7709
7710
7711/**
7712 * Array::concat implementation.
7713 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007714 * TODO(lrn): Fix non-compliance for very large concatenations and update to
7715 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007716 */
7717static Object* Runtime_ArrayConcat(Arguments args) {
7718 ASSERT(args.length() == 1);
7719 HandleScope handle_scope;
7720
7721 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
7722 Handle<JSArray> arguments(arg_arrays);
7723
7724 // Pass 1: estimate the number of elements of the result
7725 // (it could be more than real numbers if prototype has elements).
7726 uint32_t result_length = 0;
7727 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7728
7729 { AssertNoAllocation nogc;
7730 for (uint32_t i = 0; i < num_of_args; i++) {
7731 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007732 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007733 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007734 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007735 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
7736 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007737 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007738 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007739 if (JSObject::kMaxElementCount - result_length < length_estimate) {
7740 result_length = JSObject::kMaxElementCount;
7741 break;
7742 }
7743 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007744 }
7745 }
7746
7747 // Allocate an empty array, will set length and content later.
7748 Handle<JSArray> result = Factory::NewJSArray(0);
7749
7750 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
7751 // If estimated number of elements is more than half of length, a
7752 // fixed array (fast case) is more time and space-efficient than a
7753 // dictionary.
7754 bool fast_case = (estimate_nof_elements * 2) >= result_length;
7755
7756 Handle<FixedArray> storage;
7757 if (fast_case) {
7758 // The backing storage array must have non-existing elements to
7759 // preserve holes across concat operations.
7760 storage = Factory::NewFixedArrayWithHoles(result_length);
7761
7762 } else {
7763 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
7764 uint32_t at_least_space_for = estimate_nof_elements +
7765 (estimate_nof_elements >> 2);
7766 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007767 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007768 }
7769
7770 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
7771
7772 ArrayConcatVisitor visitor(storage, result_length, fast_case);
7773
7774 IterateArguments(arguments, &visitor);
7775
7776 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007777 // Please note the storage might have changed in the visitor.
7778 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007779
7780 return *result;
7781}
7782
7783
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007784// This will not allocate (flatten the string), but it may run
7785// very slowly for very deeply nested ConsStrings. For debugging use only.
7786static Object* Runtime_GlobalPrint(Arguments args) {
7787 NoHandleAllocation ha;
7788 ASSERT(args.length() == 1);
7789
7790 CONVERT_CHECKED(String, string, args[0]);
7791 StringInputBuffer buffer(string);
7792 while (buffer.has_more()) {
7793 uint16_t character = buffer.GetNext();
7794 PrintF("%c", character);
7795 }
7796 return string;
7797}
7798
ager@chromium.org5ec48922009-05-05 07:25:34 +00007799// Moves all own elements of an object, that are below a limit, to positions
7800// starting at zero. All undefined values are placed after non-undefined values,
7801// and are followed by non-existing element. Does not change the length
7802// property.
7803// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007804static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007805 ASSERT(args.length() == 2);
7806 CONVERT_CHECKED(JSObject, object, args[0]);
7807 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
7808 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007809}
7810
7811
7812// Move contents of argument 0 (an array) to argument 1 (an array)
7813static Object* Runtime_MoveArrayContents(Arguments args) {
7814 ASSERT(args.length() == 2);
7815 CONVERT_CHECKED(JSArray, from, args[0]);
7816 CONVERT_CHECKED(JSArray, to, args[1]);
7817 to->SetContent(FixedArray::cast(from->elements()));
7818 to->set_length(from->length());
7819 from->SetContent(Heap::empty_fixed_array());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007820 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007821 return to;
7822}
7823
7824
7825// How many elements does this array have?
7826static Object* Runtime_EstimateNumberOfElements(Arguments args) {
7827 ASSERT(args.length() == 1);
7828 CONVERT_CHECKED(JSArray, array, args[0]);
7829 HeapObject* elements = array->elements();
7830 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007831 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007832 } else {
7833 return array->length();
7834 }
7835}
7836
7837
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007838static Object* Runtime_SwapElements(Arguments args) {
7839 HandleScope handle_scope;
7840
7841 ASSERT_EQ(3, args.length());
7842
ager@chromium.orgac091b72010-05-05 07:34:42 +00007843 CONVERT_ARG_CHECKED(JSObject, object, 0);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007844 Handle<Object> key1 = args.at<Object>(1);
7845 Handle<Object> key2 = args.at<Object>(2);
7846
7847 uint32_t index1, index2;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007848 if (!key1->ToArrayIndex(&index1)
7849 || !key2->ToArrayIndex(&index2)) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00007850 return Top::ThrowIllegalOperation();
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007851 }
7852
ager@chromium.orgac091b72010-05-05 07:34:42 +00007853 Handle<JSObject> jsobject = Handle<JSObject>::cast(object);
7854 Handle<Object> tmp1 = GetElement(jsobject, index1);
7855 Handle<Object> tmp2 = GetElement(jsobject, index2);
7856
7857 SetElement(jsobject, index1, tmp2);
7858 SetElement(jsobject, index2, tmp1);
7859
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007860 return Heap::undefined_value();
7861}
7862
7863
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007864// Returns an array that tells you where in the [0, length) interval an array
7865// might have elements. Can either return keys or intervals. Keys can have
7866// gaps in (undefined). Intervals can also span over some undefined keys.
7867static Object* Runtime_GetArrayKeys(Arguments args) {
7868 ASSERT(args.length() == 2);
7869 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007870 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007871 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007872 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007873 // Create an array and get all the keys into it, then remove all the
7874 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007875 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007876 int keys_length = keys->length();
7877 for (int i = 0; i < keys_length; i++) {
7878 Object* key = keys->get(i);
7879 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007880 if (!key->ToArrayIndex(&index) || index >= length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007881 // Zap invalid keys.
7882 keys->set_undefined(i);
7883 }
7884 }
7885 return *Factory::NewJSArrayWithElements(keys);
7886 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007887 ASSERT(array->HasFastElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007888 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
7889 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007890 single_interval->set(0, Smi::FromInt(-1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007891 uint32_t actual_length =
7892 static_cast<uint32_t>(FixedArray::cast(array->elements())->length());
ager@chromium.org5ec48922009-05-05 07:25:34 +00007893 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007894 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00007895 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007896 single_interval->set(1, *length_object);
7897 return *Factory::NewJSArrayWithElements(single_interval);
7898 }
7899}
7900
7901
7902// DefineAccessor takes an optional final argument which is the
7903// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
7904// to the way accessors are implemented, it is set for both the getter
7905// and setter on the first call to DefineAccessor and ignored on
7906// subsequent calls.
7907static Object* Runtime_DefineAccessor(Arguments args) {
7908 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
7909 // Compute attributes.
7910 PropertyAttributes attributes = NONE;
7911 if (args.length() == 5) {
7912 CONVERT_CHECKED(Smi, attrs, args[4]);
7913 int value = attrs->value();
7914 // Only attribute bits should be set.
7915 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
7916 attributes = static_cast<PropertyAttributes>(value);
7917 }
7918
7919 CONVERT_CHECKED(JSObject, obj, args[0]);
7920 CONVERT_CHECKED(String, name, args[1]);
7921 CONVERT_CHECKED(Smi, flag, args[2]);
7922 CONVERT_CHECKED(JSFunction, fun, args[3]);
7923 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
7924}
7925
7926
7927static Object* Runtime_LookupAccessor(Arguments args) {
7928 ASSERT(args.length() == 3);
7929 CONVERT_CHECKED(JSObject, obj, args[0]);
7930 CONVERT_CHECKED(String, name, args[1]);
7931 CONVERT_CHECKED(Smi, flag, args[2]);
7932 return obj->LookupAccessor(name, flag->value() == 0);
7933}
7934
7935
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007936#ifdef ENABLE_DEBUGGER_SUPPORT
7937static Object* Runtime_DebugBreak(Arguments args) {
7938 ASSERT(args.length() == 0);
7939 return Execution::DebugBreakHelper();
7940}
7941
7942
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007943// Helper functions for wrapping and unwrapping stack frame ids.
7944static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007945 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007946 return Smi::FromInt(id >> 2);
7947}
7948
7949
7950static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
7951 return static_cast<StackFrame::Id>(wrapped->value() << 2);
7952}
7953
7954
7955// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00007956// args[0]: debug event listener function to set or null or undefined for
7957// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007958// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00007959static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007960 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007961 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
7962 args[0]->IsUndefined() ||
7963 args[0]->IsNull());
7964 Handle<Object> callback = args.at<Object>(0);
7965 Handle<Object> data = args.at<Object>(1);
7966 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007967
7968 return Heap::undefined_value();
7969}
7970
7971
7972static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007973 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007974 StackGuard::DebugBreak();
7975 return Heap::undefined_value();
7976}
7977
7978
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007979static Object* DebugLookupResultValue(Object* receiver, String* name,
7980 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00007981 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007982 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007983 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007984 case NORMAL:
7985 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007986 if (value->IsTheHole()) {
7987 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007988 }
7989 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007990 case FIELD:
7991 value =
7992 JSObject::cast(
7993 result->holder())->FastPropertyAt(result->GetFieldIndex());
7994 if (value->IsTheHole()) {
7995 return Heap::undefined_value();
7996 }
7997 return value;
7998 case CONSTANT_FUNCTION:
7999 return result->GetConstantFunction();
8000 case CALLBACKS: {
8001 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008002 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008003 value = receiver->GetPropertyWithCallback(
8004 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00008005 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008006 value = Top::pending_exception();
8007 Top::clear_pending_exception();
8008 if (caught_exception != NULL) {
8009 *caught_exception = true;
8010 }
8011 }
8012 return value;
8013 } else {
8014 return Heap::undefined_value();
8015 }
8016 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008017 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008018 case MAP_TRANSITION:
8019 case CONSTANT_TRANSITION:
8020 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008021 return Heap::undefined_value();
8022 default:
8023 UNREACHABLE();
8024 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008025 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008026 return Heap::undefined_value();
8027}
8028
8029
ager@chromium.org32912102009-01-16 10:38:43 +00008030// Get debugger related details for an object property.
8031// args[0]: object holding property
8032// args[1]: name of the property
8033//
8034// The array returned contains the following information:
8035// 0: Property value
8036// 1: Property details
8037// 2: Property value is exception
8038// 3: Getter function if defined
8039// 4: Setter function if defined
8040// Items 2-4 are only filled if the property has either a getter or a setter
8041// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00008042static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008043 HandleScope scope;
8044
8045 ASSERT(args.length() == 2);
8046
8047 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8048 CONVERT_ARG_CHECKED(String, name, 1);
8049
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008050 // Make sure to set the current context to the context before the debugger was
8051 // entered (if the debugger is entered). The reason for switching context here
8052 // is that for some property lookups (accessors and interceptors) callbacks
8053 // into the embedding application can occour, and the embedding application
8054 // could have the assumption that its own global context is the current
8055 // context and not some internal debugger context.
8056 SaveContext save;
8057 if (Debug::InDebugger()) {
8058 Top::set_context(*Debug::debugger_entry()->GetContext());
8059 }
8060
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008061 // Skip the global proxy as it has no properties and always delegates to the
8062 // real global object.
8063 if (obj->IsJSGlobalProxy()) {
8064 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
8065 }
8066
8067
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008068 // Check if the name is trivially convertible to an index and get the element
8069 // if so.
8070 uint32_t index;
8071 if (name->AsArrayIndex(&index)) {
8072 Handle<FixedArray> details = Factory::NewFixedArray(2);
8073 details->set(0, Runtime::GetElementOrCharAt(obj, index));
8074 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
8075 return *Factory::NewJSArrayWithElements(details);
8076 }
8077
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008078 // Find the number of objects making up this.
8079 int length = LocalPrototypeChainLength(*obj);
8080
8081 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008082 Handle<JSObject> jsproto = obj;
8083 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008084 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008085 jsproto->LocalLookup(*name, &result);
8086 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008087 // LookupResult is not GC safe as it holds raw object pointers.
8088 // GC can happen later in this code so put the required fields into
8089 // local variables using handles when required for later use.
8090 PropertyType result_type = result.type();
8091 Handle<Object> result_callback_obj;
8092 if (result_type == CALLBACKS) {
8093 result_callback_obj = Handle<Object>(result.GetCallbackObject());
8094 }
8095 Smi* property_details = result.GetPropertyDetails().AsSmi();
8096 // DebugLookupResultValue can cause GC so details from LookupResult needs
8097 // to be copied to handles before this.
8098 bool caught_exception = false;
8099 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
8100 &caught_exception);
8101 if (raw_value->IsFailure()) return raw_value;
8102 Handle<Object> value(raw_value);
8103
8104 // If the callback object is a fixed array then it contains JavaScript
8105 // getter and/or setter.
8106 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
8107 result_callback_obj->IsFixedArray();
8108 Handle<FixedArray> details =
8109 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
8110 details->set(0, *value);
8111 details->set(1, property_details);
8112 if (hasJavaScriptAccessors) {
8113 details->set(2,
8114 caught_exception ? Heap::true_value()
8115 : Heap::false_value());
8116 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
8117 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
8118 }
8119
8120 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008121 }
8122 if (i < length - 1) {
8123 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
8124 }
8125 }
8126
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008127 return Heap::undefined_value();
8128}
8129
8130
8131static Object* Runtime_DebugGetProperty(Arguments args) {
8132 HandleScope scope;
8133
8134 ASSERT(args.length() == 2);
8135
8136 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8137 CONVERT_ARG_CHECKED(String, name, 1);
8138
8139 LookupResult result;
8140 obj->Lookup(*name, &result);
8141 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008142 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008143 }
8144 return Heap::undefined_value();
8145}
8146
8147
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008148// Return the property type calculated from the property details.
8149// args[0]: smi with property details.
8150static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
8151 ASSERT(args.length() == 1);
8152 CONVERT_CHECKED(Smi, details, args[0]);
8153 PropertyType type = PropertyDetails(details).type();
8154 return Smi::FromInt(static_cast<int>(type));
8155}
8156
8157
8158// Return the property attribute calculated from the property details.
8159// args[0]: smi with property details.
8160static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
8161 ASSERT(args.length() == 1);
8162 CONVERT_CHECKED(Smi, details, args[0]);
8163 PropertyAttributes attributes = PropertyDetails(details).attributes();
8164 return Smi::FromInt(static_cast<int>(attributes));
8165}
8166
8167
8168// Return the property insertion index calculated from the property details.
8169// args[0]: smi with property details.
8170static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
8171 ASSERT(args.length() == 1);
8172 CONVERT_CHECKED(Smi, details, args[0]);
8173 int index = PropertyDetails(details).index();
8174 return Smi::FromInt(index);
8175}
8176
8177
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008178// Return property value from named interceptor.
8179// args[0]: object
8180// args[1]: property name
8181static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
8182 HandleScope scope;
8183 ASSERT(args.length() == 2);
8184 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8185 RUNTIME_ASSERT(obj->HasNamedInterceptor());
8186 CONVERT_ARG_CHECKED(String, name, 1);
8187
8188 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008189 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008190}
8191
8192
8193// Return element value from indexed interceptor.
8194// args[0]: object
8195// args[1]: index
8196static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
8197 HandleScope scope;
8198 ASSERT(args.length() == 2);
8199 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8200 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
8201 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
8202
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008203 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008204}
8205
8206
8207static Object* Runtime_CheckExecutionState(Arguments args) {
8208 ASSERT(args.length() >= 1);
8209 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00008210 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008211 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008212 return Top::Throw(Heap::illegal_execution_state_symbol());
8213 }
8214
8215 return Heap::true_value();
8216}
8217
8218
8219static Object* Runtime_GetFrameCount(Arguments args) {
8220 HandleScope scope;
8221 ASSERT(args.length() == 1);
8222
8223 // Check arguments.
8224 Object* result = Runtime_CheckExecutionState(args);
8225 if (result->IsFailure()) return result;
8226
8227 // Count all frames which are relevant to debugging stack trace.
8228 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008229 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008230 if (id == StackFrame::NO_ID) {
8231 // If there is no JavaScript stack frame count is 0.
8232 return Smi::FromInt(0);
8233 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008234 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
8235 return Smi::FromInt(n);
8236}
8237
8238
8239static const int kFrameDetailsFrameIdIndex = 0;
8240static const int kFrameDetailsReceiverIndex = 1;
8241static const int kFrameDetailsFunctionIndex = 2;
8242static const int kFrameDetailsArgumentCountIndex = 3;
8243static const int kFrameDetailsLocalCountIndex = 4;
8244static const int kFrameDetailsSourcePositionIndex = 5;
8245static const int kFrameDetailsConstructCallIndex = 6;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008246static const int kFrameDetailsAtReturnIndex = 7;
8247static const int kFrameDetailsDebuggerFrameIndex = 8;
8248static const int kFrameDetailsFirstDynamicIndex = 9;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008249
8250// Return an array with frame details
8251// args[0]: number: break id
8252// args[1]: number: frame index
8253//
8254// The array returned contains the following information:
8255// 0: Frame id
8256// 1: Receiver
8257// 2: Function
8258// 3: Argument count
8259// 4: Local count
8260// 5: Source position
8261// 6: Constructor call
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008262// 7: Is at return
8263// 8: Debugger frame
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008264// Arguments name, value
8265// Locals name, value
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008266// Return value if any
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008267static Object* Runtime_GetFrameDetails(Arguments args) {
8268 HandleScope scope;
8269 ASSERT(args.length() == 2);
8270
8271 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008272 Object* check = Runtime_CheckExecutionState(args);
8273 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008274 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8275
8276 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008277 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008278 if (id == StackFrame::NO_ID) {
8279 // If there are no JavaScript stack frames return undefined.
8280 return Heap::undefined_value();
8281 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008282 int count = 0;
8283 JavaScriptFrameIterator it(id);
8284 for (; !it.done(); it.Advance()) {
8285 if (count == index) break;
8286 count++;
8287 }
8288 if (it.done()) return Heap::undefined_value();
8289
8290 // Traverse the saved contexts chain to find the active context for the
8291 // selected frame.
8292 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008293 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008294 save = save->prev();
8295 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008296 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008297
8298 // Get the frame id.
8299 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
8300
8301 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008302 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008303
8304 // Check for constructor frame.
8305 bool constructor = it.frame()->IsConstructor();
8306
8307 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008308 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008309 ScopeInfo<> info(*code);
8310
8311 // Get the context.
8312 Handle<Context> context(Context::cast(it.frame()->context()));
8313
8314 // Get the locals names and values into a temporary array.
8315 //
8316 // TODO(1240907): Hide compiler-introduced stack variables
8317 // (e.g. .result)? For users of the debugger, they will probably be
8318 // confusing.
8319 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
8320 for (int i = 0; i < info.NumberOfLocals(); i++) {
8321 // Name of the local.
8322 locals->set(i * 2, *info.LocalName(i));
8323
8324 // Fetch the value of the local - either from the stack or from a
8325 // heap-allocated context.
8326 if (i < info.number_of_stack_slots()) {
8327 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
8328 } else {
8329 Handle<String> name = info.LocalName(i);
8330 // Traverse the context chain to the function context as all local
8331 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008332 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008333 context = Handle<Context>(context->previous());
8334 }
8335 ASSERT(context->is_function_context());
8336 locals->set(i * 2 + 1,
8337 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
8338 NULL)));
8339 }
8340 }
8341
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008342 // Check whether this frame is positioned at return.
8343 int at_return = (index == 0) ? Debug::IsBreakAtReturn(it.frame()) : false;
8344
8345 // If positioned just before return find the value to be returned and add it
8346 // to the frame information.
8347 Handle<Object> return_value = Factory::undefined_value();
8348 if (at_return) {
8349 StackFrameIterator it2;
8350 Address internal_frame_sp = NULL;
8351 while (!it2.done()) {
8352 if (it2.frame()->is_internal()) {
8353 internal_frame_sp = it2.frame()->sp();
8354 } else {
8355 if (it2.frame()->is_java_script()) {
8356 if (it2.frame()->id() == it.frame()->id()) {
8357 // The internal frame just before the JavaScript frame contains the
8358 // value to return on top. A debug break at return will create an
8359 // internal frame to store the return value (eax/rax/r0) before
8360 // entering the debug break exit frame.
8361 if (internal_frame_sp != NULL) {
8362 return_value =
8363 Handle<Object>(Memory::Object_at(internal_frame_sp));
8364 break;
8365 }
8366 }
8367 }
8368
8369 // Indicate that the previous frame was not an internal frame.
8370 internal_frame_sp = NULL;
8371 }
8372 it2.Advance();
8373 }
8374 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008375
8376 // Now advance to the arguments adapter frame (if any). It contains all
8377 // the provided parameters whereas the function frame always have the number
8378 // of arguments matching the functions parameters. The rest of the
8379 // information (except for what is collected above) is the same.
8380 it.AdvanceToArgumentsFrame();
8381
8382 // Find the number of arguments to fill. At least fill the number of
8383 // parameters for the function and fill more if more parameters are provided.
8384 int argument_count = info.number_of_parameters();
8385 if (argument_count < it.frame()->GetProvidedParametersCount()) {
8386 argument_count = it.frame()->GetProvidedParametersCount();
8387 }
8388
8389 // Calculate the size of the result.
8390 int details_size = kFrameDetailsFirstDynamicIndex +
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008391 2 * (argument_count + info.NumberOfLocals()) +
8392 (at_return ? 1 : 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008393 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8394
8395 // Add the frame id.
8396 details->set(kFrameDetailsFrameIdIndex, *frame_id);
8397
8398 // Add the function (same as in function frame).
8399 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
8400
8401 // Add the arguments count.
8402 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
8403
8404 // Add the locals count
8405 details->set(kFrameDetailsLocalCountIndex,
8406 Smi::FromInt(info.NumberOfLocals()));
8407
8408 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00008409 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008410 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
8411 } else {
8412 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
8413 }
8414
8415 // Add the constructor information.
8416 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
8417
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008418 // Add the at return information.
8419 details->set(kFrameDetailsAtReturnIndex, Heap::ToBoolean(at_return));
8420
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008421 // Add information on whether this frame is invoked in the debugger context.
8422 details->set(kFrameDetailsDebuggerFrameIndex,
8423 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
8424
8425 // Fill the dynamic part.
8426 int details_index = kFrameDetailsFirstDynamicIndex;
8427
8428 // Add arguments name and value.
8429 for (int i = 0; i < argument_count; i++) {
8430 // Name of the argument.
8431 if (i < info.number_of_parameters()) {
8432 details->set(details_index++, *info.parameter_name(i));
8433 } else {
8434 details->set(details_index++, Heap::undefined_value());
8435 }
8436
8437 // Parameter value.
8438 if (i < it.frame()->GetProvidedParametersCount()) {
8439 details->set(details_index++, it.frame()->GetParameter(i));
8440 } else {
8441 details->set(details_index++, Heap::undefined_value());
8442 }
8443 }
8444
8445 // Add locals name and value from the temporary copy from the function frame.
8446 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
8447 details->set(details_index++, locals->get(i));
8448 }
8449
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008450 // Add the value being returned.
8451 if (at_return) {
8452 details->set(details_index++, *return_value);
8453 }
8454
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008455 // Add the receiver (same as in function frame).
8456 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
8457 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
8458 Handle<Object> receiver(it.frame()->receiver());
8459 if (!receiver->IsJSObject()) {
8460 // If the receiver is NOT a JSObject we have hit an optimization
8461 // where a value object is not converted into a wrapped JS objects.
8462 // To hide this optimization from the debugger, we wrap the receiver
8463 // by creating correct wrapper object based on the calling frame's
8464 // global context.
8465 it.Advance();
8466 Handle<Context> calling_frames_global_context(
8467 Context::cast(Context::cast(it.frame()->context())->global_context()));
8468 receiver = Factory::ToObject(receiver, calling_frames_global_context);
8469 }
8470 details->set(kFrameDetailsReceiverIndex, *receiver);
8471
8472 ASSERT_EQ(details_size, details_index);
8473 return *Factory::NewJSArrayWithElements(details);
8474}
8475
8476
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008477// Copy all the context locals into an object used to materialize a scope.
8478static void CopyContextLocalsToScopeObject(Handle<Code> code,
8479 ScopeInfo<>& scope_info,
8480 Handle<Context> context,
8481 Handle<JSObject> scope_object) {
8482 // Fill all context locals to the context extension.
8483 for (int i = Context::MIN_CONTEXT_SLOTS;
8484 i < scope_info.number_of_context_slots();
8485 i++) {
8486 int context_index =
8487 ScopeInfo<>::ContextSlotIndex(*code,
8488 *scope_info.context_slot_name(i),
8489 NULL);
8490
8491 // Don't include the arguments shadow (.arguments) context variable.
8492 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
8493 SetProperty(scope_object,
8494 scope_info.context_slot_name(i),
8495 Handle<Object>(context->get(context_index)), NONE);
8496 }
8497 }
8498}
8499
8500
8501// Create a plain JSObject which materializes the local scope for the specified
8502// frame.
8503static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
8504 Handle<JSFunction> function(JSFunction::cast(frame->function()));
8505 Handle<Code> code(function->code());
8506 ScopeInfo<> scope_info(*code);
8507
8508 // Allocate and initialize a JSObject with all the arguments, stack locals
8509 // heap locals and extension properties of the debugged function.
8510 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
8511
8512 // First fill all parameters.
8513 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8514 SetProperty(local_scope,
8515 scope_info.parameter_name(i),
8516 Handle<Object>(frame->GetParameter(i)), NONE);
8517 }
8518
8519 // Second fill all stack locals.
8520 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
8521 SetProperty(local_scope,
8522 scope_info.stack_slot_name(i),
8523 Handle<Object>(frame->GetExpression(i)), NONE);
8524 }
8525
8526 // Third fill all context locals.
8527 Handle<Context> frame_context(Context::cast(frame->context()));
8528 Handle<Context> function_context(frame_context->fcontext());
8529 CopyContextLocalsToScopeObject(code, scope_info,
8530 function_context, local_scope);
8531
8532 // Finally copy any properties from the function context extension. This will
8533 // be variables introduced by eval.
8534 if (function_context->closure() == *function) {
8535 if (function_context->has_extension() &&
8536 !function_context->IsGlobalContext()) {
8537 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008538 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008539 for (int i = 0; i < keys->length(); i++) {
8540 // Names of variables introduced by eval are strings.
8541 ASSERT(keys->get(i)->IsString());
8542 Handle<String> key(String::cast(keys->get(i)));
8543 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
8544 }
8545 }
8546 }
8547 return local_scope;
8548}
8549
8550
8551// Create a plain JSObject which materializes the closure content for the
8552// context.
8553static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
8554 ASSERT(context->is_function_context());
8555
8556 Handle<Code> code(context->closure()->code());
8557 ScopeInfo<> scope_info(*code);
8558
8559 // Allocate and initialize a JSObject with all the content of theis function
8560 // closure.
8561 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
8562
8563 // Check whether the arguments shadow object exists.
8564 int arguments_shadow_index =
8565 ScopeInfo<>::ContextSlotIndex(*code,
8566 Heap::arguments_shadow_symbol(),
8567 NULL);
8568 if (arguments_shadow_index >= 0) {
8569 // In this case all the arguments are available in the arguments shadow
8570 // object.
8571 Handle<JSObject> arguments_shadow(
8572 JSObject::cast(context->get(arguments_shadow_index)));
8573 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8574 SetProperty(closure_scope,
8575 scope_info.parameter_name(i),
8576 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
8577 }
8578 }
8579
8580 // Fill all context locals to the context extension.
8581 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
8582
8583 // Finally copy any properties from the function context extension. This will
8584 // be variables introduced by eval.
8585 if (context->has_extension()) {
8586 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008587 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008588 for (int i = 0; i < keys->length(); i++) {
8589 // Names of variables introduced by eval are strings.
8590 ASSERT(keys->get(i)->IsString());
8591 Handle<String> key(String::cast(keys->get(i)));
8592 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
8593 }
8594 }
8595
8596 return closure_scope;
8597}
8598
8599
8600// Iterate over the actual scopes visible from a stack frame. All scopes are
8601// backed by an actual context except the local scope, which is inserted
8602// "artifically" in the context chain.
8603class ScopeIterator {
8604 public:
8605 enum ScopeType {
8606 ScopeTypeGlobal = 0,
8607 ScopeTypeLocal,
8608 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00008609 ScopeTypeClosure,
8610 // Every catch block contains an implicit with block (its parameter is
8611 // a JSContextExtensionObject) that extends current scope with a variable
8612 // holding exception object. Such with blocks are treated as scopes of their
8613 // own type.
8614 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008615 };
8616
8617 explicit ScopeIterator(JavaScriptFrame* frame)
8618 : frame_(frame),
8619 function_(JSFunction::cast(frame->function())),
8620 context_(Context::cast(frame->context())),
8621 local_done_(false),
8622 at_local_(false) {
8623
8624 // Check whether the first scope is actually a local scope.
8625 if (context_->IsGlobalContext()) {
8626 // If there is a stack slot for .result then this local scope has been
8627 // created for evaluating top level code and it is not a real local scope.
8628 // Checking for the existence of .result seems fragile, but the scope info
8629 // saved with the code object does not otherwise have that information.
8630 Handle<Code> code(function_->code());
8631 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
8632 at_local_ = index < 0;
8633 } else if (context_->is_function_context()) {
8634 at_local_ = true;
8635 }
8636 }
8637
8638 // More scopes?
8639 bool Done() { return context_.is_null(); }
8640
8641 // Move to the next scope.
8642 void Next() {
8643 // If at a local scope mark the local scope as passed.
8644 if (at_local_) {
8645 at_local_ = false;
8646 local_done_ = true;
8647
8648 // If the current context is not associated with the local scope the
8649 // current context is the next real scope, so don't move to the next
8650 // context in this case.
8651 if (context_->closure() != *function_) {
8652 return;
8653 }
8654 }
8655
8656 // The global scope is always the last in the chain.
8657 if (context_->IsGlobalContext()) {
8658 context_ = Handle<Context>();
8659 return;
8660 }
8661
8662 // Move to the next context.
8663 if (context_->is_function_context()) {
8664 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
8665 } else {
8666 context_ = Handle<Context>(context_->previous());
8667 }
8668
8669 // If passing the local scope indicate that the current scope is now the
8670 // local scope.
8671 if (!local_done_ &&
8672 (context_->IsGlobalContext() || (context_->is_function_context()))) {
8673 at_local_ = true;
8674 }
8675 }
8676
8677 // Return the type of the current scope.
8678 int Type() {
8679 if (at_local_) {
8680 return ScopeTypeLocal;
8681 }
8682 if (context_->IsGlobalContext()) {
8683 ASSERT(context_->global()->IsGlobalObject());
8684 return ScopeTypeGlobal;
8685 }
8686 if (context_->is_function_context()) {
8687 return ScopeTypeClosure;
8688 }
8689 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00008690 // Current scope is either an explicit with statement or a with statement
8691 // implicitely generated for a catch block.
8692 // If the extension object here is a JSContextExtensionObject then
8693 // current with statement is one frome a catch block otherwise it's a
8694 // regular with statement.
8695 if (context_->extension()->IsJSContextExtensionObject()) {
8696 return ScopeTypeCatch;
8697 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008698 return ScopeTypeWith;
8699 }
8700
8701 // Return the JavaScript object with the content of the current scope.
8702 Handle<JSObject> ScopeObject() {
8703 switch (Type()) {
8704 case ScopeIterator::ScopeTypeGlobal:
8705 return Handle<JSObject>(CurrentContext()->global());
8706 break;
8707 case ScopeIterator::ScopeTypeLocal:
8708 // Materialize the content of the local scope into a JSObject.
8709 return MaterializeLocalScope(frame_);
8710 break;
8711 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00008712 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008713 // Return the with object.
8714 return Handle<JSObject>(CurrentContext()->extension());
8715 break;
8716 case ScopeIterator::ScopeTypeClosure:
8717 // Materialize the content of the closure scope into a JSObject.
8718 return MaterializeClosure(CurrentContext());
8719 break;
8720 }
8721 UNREACHABLE();
8722 return Handle<JSObject>();
8723 }
8724
8725 // Return the context for this scope. For the local context there might not
8726 // be an actual context.
8727 Handle<Context> CurrentContext() {
8728 if (at_local_ && context_->closure() != *function_) {
8729 return Handle<Context>();
8730 }
8731 return context_;
8732 }
8733
8734#ifdef DEBUG
8735 // Debug print of the content of the current scope.
8736 void DebugPrint() {
8737 switch (Type()) {
8738 case ScopeIterator::ScopeTypeGlobal:
8739 PrintF("Global:\n");
8740 CurrentContext()->Print();
8741 break;
8742
8743 case ScopeIterator::ScopeTypeLocal: {
8744 PrintF("Local:\n");
8745 Handle<Code> code(function_->code());
8746 ScopeInfo<> scope_info(*code);
8747 scope_info.Print();
8748 if (!CurrentContext().is_null()) {
8749 CurrentContext()->Print();
8750 if (CurrentContext()->has_extension()) {
8751 Handle<JSObject> extension =
8752 Handle<JSObject>(CurrentContext()->extension());
8753 if (extension->IsJSContextExtensionObject()) {
8754 extension->Print();
8755 }
8756 }
8757 }
8758 break;
8759 }
8760
8761 case ScopeIterator::ScopeTypeWith: {
8762 PrintF("With:\n");
8763 Handle<JSObject> extension =
8764 Handle<JSObject>(CurrentContext()->extension());
8765 extension->Print();
8766 break;
8767 }
8768
ager@chromium.orga1645e22009-09-09 19:27:10 +00008769 case ScopeIterator::ScopeTypeCatch: {
8770 PrintF("Catch:\n");
8771 Handle<JSObject> extension =
8772 Handle<JSObject>(CurrentContext()->extension());
8773 extension->Print();
8774 break;
8775 }
8776
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008777 case ScopeIterator::ScopeTypeClosure: {
8778 PrintF("Closure:\n");
8779 CurrentContext()->Print();
8780 if (CurrentContext()->has_extension()) {
8781 Handle<JSObject> extension =
8782 Handle<JSObject>(CurrentContext()->extension());
8783 if (extension->IsJSContextExtensionObject()) {
8784 extension->Print();
8785 }
8786 }
8787 break;
8788 }
8789
8790 default:
8791 UNREACHABLE();
8792 }
8793 PrintF("\n");
8794 }
8795#endif
8796
8797 private:
8798 JavaScriptFrame* frame_;
8799 Handle<JSFunction> function_;
8800 Handle<Context> context_;
8801 bool local_done_;
8802 bool at_local_;
8803
8804 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
8805};
8806
8807
8808static Object* Runtime_GetScopeCount(Arguments args) {
8809 HandleScope scope;
8810 ASSERT(args.length() == 2);
8811
8812 // Check arguments.
8813 Object* check = Runtime_CheckExecutionState(args);
8814 if (check->IsFailure()) return check;
8815 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8816
8817 // Get the frame where the debugging is performed.
8818 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8819 JavaScriptFrameIterator it(id);
8820 JavaScriptFrame* frame = it.frame();
8821
8822 // Count the visible scopes.
8823 int n = 0;
8824 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8825 n++;
8826 }
8827
8828 return Smi::FromInt(n);
8829}
8830
8831
8832static const int kScopeDetailsTypeIndex = 0;
8833static const int kScopeDetailsObjectIndex = 1;
8834static const int kScopeDetailsSize = 2;
8835
8836// Return an array with scope details
8837// args[0]: number: break id
8838// args[1]: number: frame index
8839// args[2]: number: scope index
8840//
8841// The array returned contains the following information:
8842// 0: Scope type
8843// 1: Scope object
8844static Object* Runtime_GetScopeDetails(Arguments args) {
8845 HandleScope scope;
8846 ASSERT(args.length() == 3);
8847
8848 // Check arguments.
8849 Object* check = Runtime_CheckExecutionState(args);
8850 if (check->IsFailure()) return check;
8851 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8852 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
8853
8854 // Get the frame where the debugging is performed.
8855 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8856 JavaScriptFrameIterator frame_it(id);
8857 JavaScriptFrame* frame = frame_it.frame();
8858
8859 // Find the requested scope.
8860 int n = 0;
8861 ScopeIterator it(frame);
8862 for (; !it.Done() && n < index; it.Next()) {
8863 n++;
8864 }
8865 if (it.Done()) {
8866 return Heap::undefined_value();
8867 }
8868
8869 // Calculate the size of the result.
8870 int details_size = kScopeDetailsSize;
8871 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8872
8873 // Fill in scope details.
8874 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
8875 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
8876
8877 return *Factory::NewJSArrayWithElements(details);
8878}
8879
8880
8881static Object* Runtime_DebugPrintScopes(Arguments args) {
8882 HandleScope scope;
8883 ASSERT(args.length() == 0);
8884
8885#ifdef DEBUG
8886 // Print the scopes for the top frame.
8887 StackFrameLocator locator;
8888 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
8889 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8890 it.DebugPrint();
8891 }
8892#endif
8893 return Heap::undefined_value();
8894}
8895
8896
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008897static Object* Runtime_GetCFrames(Arguments args) {
8898 HandleScope scope;
8899 ASSERT(args.length() == 1);
8900 Object* result = Runtime_CheckExecutionState(args);
8901 if (result->IsFailure()) return result;
8902
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008903#if V8_HOST_ARCH_64_BIT
8904 UNIMPLEMENTED();
8905 return Heap::undefined_value();
8906#else
8907
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008908 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008909 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
8910 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008911 if (frames_count == OS::kStackWalkError) {
8912 return Heap::undefined_value();
8913 }
8914
8915 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
8916 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
8917 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
8918 for (int i = 0; i < frames_count; i++) {
8919 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
8920 frame_value->SetProperty(
8921 *address_str,
8922 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
8923 NONE);
8924
8925 // Get the stack walk text for this frame.
8926 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008927 int frame_text_length = StrLength(frames[i].text);
8928 if (frame_text_length > 0) {
8929 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008930 frame_text = Factory::NewStringFromAscii(str);
8931 }
8932
8933 if (!frame_text.is_null()) {
8934 frame_value->SetProperty(*text_str, *frame_text, NONE);
8935 }
8936
8937 frames_array->set(i, *frame_value);
8938 }
8939 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008940#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008941}
8942
8943
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00008944static Object* Runtime_GetThreadCount(Arguments args) {
8945 HandleScope scope;
8946 ASSERT(args.length() == 1);
8947
8948 // Check arguments.
8949 Object* result = Runtime_CheckExecutionState(args);
8950 if (result->IsFailure()) return result;
8951
8952 // Count all archived V8 threads.
8953 int n = 0;
8954 for (ThreadState* thread = ThreadState::FirstInUse();
8955 thread != NULL;
8956 thread = thread->Next()) {
8957 n++;
8958 }
8959
8960 // Total number of threads is current thread and archived threads.
8961 return Smi::FromInt(n + 1);
8962}
8963
8964
8965static const int kThreadDetailsCurrentThreadIndex = 0;
8966static const int kThreadDetailsThreadIdIndex = 1;
8967static const int kThreadDetailsSize = 2;
8968
8969// Return an array with thread details
8970// args[0]: number: break id
8971// args[1]: number: thread index
8972//
8973// The array returned contains the following information:
8974// 0: Is current thread?
8975// 1: Thread id
8976static Object* Runtime_GetThreadDetails(Arguments args) {
8977 HandleScope scope;
8978 ASSERT(args.length() == 2);
8979
8980 // Check arguments.
8981 Object* check = Runtime_CheckExecutionState(args);
8982 if (check->IsFailure()) return check;
8983 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8984
8985 // Allocate array for result.
8986 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
8987
8988 // Thread index 0 is current thread.
8989 if (index == 0) {
8990 // Fill the details.
8991 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
8992 details->set(kThreadDetailsThreadIdIndex,
8993 Smi::FromInt(ThreadManager::CurrentId()));
8994 } else {
8995 // Find the thread with the requested index.
8996 int n = 1;
8997 ThreadState* thread = ThreadState::FirstInUse();
8998 while (index != n && thread != NULL) {
8999 thread = thread->Next();
9000 n++;
9001 }
9002 if (thread == NULL) {
9003 return Heap::undefined_value();
9004 }
9005
9006 // Fill the details.
9007 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
9008 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
9009 }
9010
9011 // Convert to JS array and return.
9012 return *Factory::NewJSArrayWithElements(details);
9013}
9014
9015
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009016static Object* Runtime_GetBreakLocations(Arguments args) {
9017 HandleScope scope;
9018 ASSERT(args.length() == 1);
9019
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009020 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9021 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009022 // Find the number of break points
9023 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
9024 if (break_locations->IsUndefined()) return Heap::undefined_value();
9025 // Return array as JS array
9026 return *Factory::NewJSArrayWithElements(
9027 Handle<FixedArray>::cast(break_locations));
9028}
9029
9030
9031// Set a break point in a function
9032// args[0]: function
9033// args[1]: number: break source position (within the function source)
9034// args[2]: number: break point object
9035static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
9036 HandleScope scope;
9037 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009038 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9039 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009040 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9041 RUNTIME_ASSERT(source_position >= 0);
9042 Handle<Object> break_point_object_arg = args.at<Object>(2);
9043
9044 // Set break point.
9045 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
9046
9047 return Heap::undefined_value();
9048}
9049
9050
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009051Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
9052 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009053 // Iterate the heap looking for SharedFunctionInfo generated from the
9054 // script. The inner most SharedFunctionInfo containing the source position
9055 // for the requested break point is found.
9056 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
9057 // which is found is not compiled it is compiled and the heap is iterated
9058 // again as the compilation might create inner functions from the newly
9059 // compiled function and the actual requested break point might be in one of
9060 // these functions.
9061 bool done = false;
9062 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00009063 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009064 Handle<SharedFunctionInfo> target;
9065 // The current candidate for the last function in script:
9066 Handle<SharedFunctionInfo> last;
9067 while (!done) {
9068 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009069 for (HeapObject* obj = iterator.next();
9070 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009071 if (obj->IsSharedFunctionInfo()) {
9072 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
9073 if (shared->script() == *script) {
9074 // If the SharedFunctionInfo found has the requested script data and
9075 // contains the source position it is a candidate.
9076 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00009077 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009078 start_position = shared->start_position();
9079 }
9080 if (start_position <= position &&
9081 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00009082 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009083 // candidate this is the new candidate.
9084 if (target.is_null()) {
9085 target_start_position = start_position;
9086 target = shared;
9087 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00009088 if (target_start_position == start_position &&
9089 shared->end_position() == target->end_position()) {
9090 // If a top-level function contain only one function
9091 // declartion the source for the top-level and the function is
9092 // the same. In that case prefer the non top-level function.
9093 if (!shared->is_toplevel()) {
9094 target_start_position = start_position;
9095 target = shared;
9096 }
9097 } else if (target_start_position <= start_position &&
9098 shared->end_position() <= target->end_position()) {
9099 // This containment check includes equality as a function inside
9100 // a top-level function can share either start or end position
9101 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009102 target_start_position = start_position;
9103 target = shared;
9104 }
9105 }
9106 }
9107
9108 // Keep track of the last function in the script.
9109 if (last.is_null() ||
9110 shared->end_position() > last->start_position()) {
9111 last = shared;
9112 }
9113 }
9114 }
9115 }
9116
9117 // Make sure some candidate is selected.
9118 if (target.is_null()) {
9119 if (!last.is_null()) {
9120 // Position after the last function - use last.
9121 target = last;
9122 } else {
9123 // Unable to find function - possibly script without any function.
9124 return Heap::undefined_value();
9125 }
9126 }
9127
9128 // If the candidate found is compiled we are done. NOTE: when lazy
9129 // compilation of inner functions is introduced some additional checking
9130 // needs to be done here to compile inner functions.
9131 done = target->is_compiled();
9132 if (!done) {
9133 // If the candidate is not compiled compile it to reveal any inner
9134 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009135 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009136 }
9137 }
9138
9139 return *target;
9140}
9141
9142
9143// Change the state of a break point in a script. NOTE: Regarding performance
9144// see the NOTE for GetScriptFromScriptData.
9145// args[0]: script to set break point in
9146// args[1]: number: break source position (within the script source)
9147// args[2]: number: break point object
9148static Object* Runtime_SetScriptBreakPoint(Arguments args) {
9149 HandleScope scope;
9150 ASSERT(args.length() == 3);
9151 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
9152 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9153 RUNTIME_ASSERT(source_position >= 0);
9154 Handle<Object> break_point_object_arg = args.at<Object>(2);
9155
9156 // Get the script from the script wrapper.
9157 RUNTIME_ASSERT(wrapper->value()->IsScript());
9158 Handle<Script> script(Script::cast(wrapper->value()));
9159
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009160 Object* result = Runtime::FindSharedFunctionInfoInScript(
9161 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009162 if (!result->IsUndefined()) {
9163 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
9164 // Find position within function. The script position might be before the
9165 // source position of the first function.
9166 int position;
9167 if (shared->start_position() > source_position) {
9168 position = 0;
9169 } else {
9170 position = source_position - shared->start_position();
9171 }
9172 Debug::SetBreakPoint(shared, position, break_point_object_arg);
9173 }
9174 return Heap::undefined_value();
9175}
9176
9177
9178// Clear a break point
9179// args[0]: number: break point object
9180static Object* Runtime_ClearBreakPoint(Arguments args) {
9181 HandleScope scope;
9182 ASSERT(args.length() == 1);
9183 Handle<Object> break_point_object_arg = args.at<Object>(0);
9184
9185 // Clear break point.
9186 Debug::ClearBreakPoint(break_point_object_arg);
9187
9188 return Heap::undefined_value();
9189}
9190
9191
9192// Change the state of break on exceptions
9193// args[0]: boolean indicating uncaught exceptions
9194// args[1]: boolean indicating on/off
9195static Object* Runtime_ChangeBreakOnException(Arguments args) {
9196 HandleScope scope;
9197 ASSERT(args.length() == 2);
9198 ASSERT(args[0]->IsNumber());
9199 ASSERT(args[1]->IsBoolean());
9200
9201 // Update break point state
9202 ExceptionBreakType type =
9203 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
9204 bool enable = args[1]->ToBoolean()->IsTrue();
9205 Debug::ChangeBreakOnException(type, enable);
9206 return Heap::undefined_value();
9207}
9208
9209
9210// Prepare for stepping
9211// args[0]: break id for checking execution state
9212// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00009213// args[2]: number of times to perform the step, for step out it is the number
9214// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009215static Object* Runtime_PrepareStep(Arguments args) {
9216 HandleScope scope;
9217 ASSERT(args.length() == 3);
9218 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009219 Object* check = Runtime_CheckExecutionState(args);
9220 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009221 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
9222 return Top::Throw(Heap::illegal_argument_symbol());
9223 }
9224
9225 // Get the step action and check validity.
9226 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
9227 if (step_action != StepIn &&
9228 step_action != StepNext &&
9229 step_action != StepOut &&
9230 step_action != StepInMin &&
9231 step_action != StepMin) {
9232 return Top::Throw(Heap::illegal_argument_symbol());
9233 }
9234
9235 // Get the number of steps.
9236 int step_count = NumberToInt32(args[2]);
9237 if (step_count < 1) {
9238 return Top::Throw(Heap::illegal_argument_symbol());
9239 }
9240
ager@chromium.orga1645e22009-09-09 19:27:10 +00009241 // Clear all current stepping setup.
9242 Debug::ClearStepping();
9243
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009244 // Prepare step.
9245 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
9246 return Heap::undefined_value();
9247}
9248
9249
9250// Clear all stepping set by PrepareStep.
9251static Object* Runtime_ClearStepping(Arguments args) {
9252 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009253 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009254 Debug::ClearStepping();
9255 return Heap::undefined_value();
9256}
9257
9258
9259// Creates a copy of the with context chain. The copy of the context chain is
9260// is linked to the function context supplied.
9261static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
9262 Handle<Context> function_context) {
9263 // At the bottom of the chain. Return the function context to link to.
9264 if (context_chain->is_function_context()) {
9265 return function_context;
9266 }
9267
9268 // Recursively copy the with contexts.
9269 Handle<Context> previous(context_chain->previous());
9270 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
9271 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009272 CopyWithContextChain(function_context, previous),
9273 extension,
9274 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009275}
9276
9277
9278// Helper function to find or create the arguments object for
9279// Runtime_DebugEvaluate.
9280static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
9281 Handle<JSFunction> function,
9282 Handle<Code> code,
9283 const ScopeInfo<>* sinfo,
9284 Handle<Context> function_context) {
9285 // Try to find the value of 'arguments' to pass as parameter. If it is not
9286 // found (that is the debugged function does not reference 'arguments' and
9287 // does not support eval) then create an 'arguments' object.
9288 int index;
9289 if (sinfo->number_of_stack_slots() > 0) {
9290 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
9291 if (index != -1) {
9292 return Handle<Object>(frame->GetExpression(index));
9293 }
9294 }
9295
9296 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
9297 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
9298 NULL);
9299 if (index != -1) {
9300 return Handle<Object>(function_context->get(index));
9301 }
9302 }
9303
9304 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009305 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
9306 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009307
9308 AssertNoAllocation no_gc;
9309 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009310 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009311 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009312 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009313 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009314 return arguments;
9315}
9316
9317
9318// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00009319// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009320// extension part has all the parameters and locals of the function on the
9321// stack frame. A function which calls eval with the code to evaluate is then
9322// compiled in this context and called in this context. As this context
9323// replaces the context of the function on the stack frame a new (empty)
9324// function is created as well to be used as the closure for the context.
9325// This function and the context acts as replacements for the function on the
9326// stack frame presenting the same view of the values of parameters and
9327// local variables as if the piece of JavaScript was evaluated at the point
9328// where the function on the stack frame is currently stopped.
9329static Object* Runtime_DebugEvaluate(Arguments args) {
9330 HandleScope scope;
9331
9332 // Check the execution state and decode arguments frame and source to be
9333 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009334 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009335 Object* check_result = Runtime_CheckExecutionState(args);
9336 if (check_result->IsFailure()) return check_result;
9337 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9338 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009339 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
9340
9341 // Handle the processing of break.
9342 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009343
9344 // Get the frame where the debugging is performed.
9345 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9346 JavaScriptFrameIterator it(id);
9347 JavaScriptFrame* frame = it.frame();
9348 Handle<JSFunction> function(JSFunction::cast(frame->function()));
9349 Handle<Code> code(function->code());
9350 ScopeInfo<> sinfo(*code);
9351
9352 // Traverse the saved contexts chain to find the active context for the
9353 // selected frame.
9354 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009355 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009356 save = save->prev();
9357 }
9358 ASSERT(save != NULL);
9359 SaveContext savex;
9360 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009361
9362 // Create the (empty) function replacing the function on the stack frame for
9363 // the purpose of evaluating in the context created below. It is important
9364 // that this function does not describe any parameters and local variables
9365 // in the context. If it does then this will cause problems with the lookup
9366 // in Context::Lookup, where context slots for parameters and local variables
9367 // are looked at before the extension object.
9368 Handle<JSFunction> go_between =
9369 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
9370 go_between->set_context(function->context());
9371#ifdef DEBUG
9372 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
9373 ASSERT(go_between_sinfo.number_of_parameters() == 0);
9374 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
9375#endif
9376
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009377 // Materialize the content of the local scope into a JSObject.
9378 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009379
9380 // Allocate a new context for the debug evaluation and set the extension
9381 // object build.
9382 Handle<Context> context =
9383 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009384 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009385 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009386 Handle<Context> frame_context(Context::cast(frame->context()));
9387 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009388 context = CopyWithContextChain(frame_context, context);
9389
9390 // Wrap the evaluation statement in a new function compiled in the newly
9391 // created context. The function has one parameter which has to be called
9392 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00009393 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009394 // function(arguments,__source__) {return eval(__source__);}
9395 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00009396 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009397 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009398 Handle<String> function_source =
9399 Factory::NewStringFromAscii(Vector<const char>(source_str,
9400 source_str_length));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009401 Handle<SharedFunctionInfo> shared =
ager@chromium.org381abbb2009-02-25 13:23:22 +00009402 Compiler::CompileEval(function_source,
9403 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00009404 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00009405 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009406 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009407 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009408 Factory::NewFunctionFromSharedFunctionInfo(shared, context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009409
9410 // Invoke the result of the compilation to get the evaluation function.
9411 bool has_pending_exception;
9412 Handle<Object> receiver(frame->receiver());
9413 Handle<Object> evaluation_function =
9414 Execution::Call(compiled_function, receiver, 0, NULL,
9415 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009416 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009417
9418 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
9419 function_context);
9420
9421 // Invoke the evaluation function and return the result.
9422 const int argc = 2;
9423 Object** argv[argc] = { arguments.location(),
9424 Handle<Object>::cast(source).location() };
9425 Handle<Object> result =
9426 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
9427 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009428 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009429
9430 // Skip the global proxy as it has no properties and always delegates to the
9431 // real global object.
9432 if (result->IsJSGlobalProxy()) {
9433 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
9434 }
9435
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009436 return *result;
9437}
9438
9439
9440static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
9441 HandleScope scope;
9442
9443 // Check the execution state and decode arguments frame and source to be
9444 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009445 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009446 Object* check_result = Runtime_CheckExecutionState(args);
9447 if (check_result->IsFailure()) return check_result;
9448 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009449 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
9450
9451 // Handle the processing of break.
9452 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009453
9454 // Enter the top context from before the debugger was invoked.
9455 SaveContext save;
9456 SaveContext* top = &save;
9457 while (top != NULL && *top->context() == *Debug::debug_context()) {
9458 top = top->prev();
9459 }
9460 if (top != NULL) {
9461 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009462 }
9463
9464 // Get the global context now set to the top context from before the
9465 // debugger was invoked.
9466 Handle<Context> context = Top::global_context();
9467
9468 // Compile the source to be evaluated.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009469 Handle<SharedFunctionInfo> shared =
9470 Compiler::CompileEval(source,
9471 context,
9472 true,
9473 Compiler::DONT_VALIDATE_JSON);
9474 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009475 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009476 Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
9477 context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009478
9479 // Invoke the result of the compilation to get the evaluation function.
9480 bool has_pending_exception;
9481 Handle<Object> receiver = Top::global();
9482 Handle<Object> result =
9483 Execution::Call(compiled_function, receiver, 0, NULL,
9484 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009485 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009486 return *result;
9487}
9488
9489
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009490static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
9491 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009492 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009493
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009494 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009495 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009496
9497 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009498 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00009499 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
9500 // Get the script wrapper in a local handle before calling GetScriptWrapper,
9501 // because using
9502 // instances->set(i, *GetScriptWrapper(script))
9503 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
9504 // already have deferenced the instances handle.
9505 Handle<JSValue> wrapper = GetScriptWrapper(script);
9506 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009507 }
9508
9509 // Return result as a JS array.
9510 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
9511 Handle<JSArray>::cast(result)->SetContent(*instances);
9512 return *result;
9513}
9514
9515
9516// Helper function used by Runtime_DebugReferencedBy below.
9517static int DebugReferencedBy(JSObject* target,
9518 Object* instance_filter, int max_references,
9519 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009520 JSFunction* arguments_function) {
9521 NoHandleAllocation ha;
9522 AssertNoAllocation no_alloc;
9523
9524 // Iterate the heap.
9525 int count = 0;
9526 JSObject* last = NULL;
9527 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009528 HeapObject* heap_obj = NULL;
9529 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009530 (max_references == 0 || count < max_references)) {
9531 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009532 if (heap_obj->IsJSObject()) {
9533 // Skip context extension objects and argument arrays as these are
9534 // checked in the context of functions using them.
9535 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00009536 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009537 obj->map()->constructor() == arguments_function) {
9538 continue;
9539 }
9540
9541 // Check if the JS object has a reference to the object looked for.
9542 if (obj->ReferencesObject(target)) {
9543 // Check instance filter if supplied. This is normally used to avoid
9544 // references from mirror objects (see Runtime_IsInPrototypeChain).
9545 if (!instance_filter->IsUndefined()) {
9546 Object* V = obj;
9547 while (true) {
9548 Object* prototype = V->GetPrototype();
9549 if (prototype->IsNull()) {
9550 break;
9551 }
9552 if (instance_filter == prototype) {
9553 obj = NULL; // Don't add this object.
9554 break;
9555 }
9556 V = prototype;
9557 }
9558 }
9559
9560 if (obj != NULL) {
9561 // Valid reference found add to instance array if supplied an update
9562 // count.
9563 if (instances != NULL && count < instances_size) {
9564 instances->set(count, obj);
9565 }
9566 last = obj;
9567 count++;
9568 }
9569 }
9570 }
9571 }
9572
9573 // Check for circular reference only. This can happen when the object is only
9574 // referenced from mirrors and has a circular reference in which case the
9575 // object is not really alive and would have been garbage collected if not
9576 // referenced from the mirror.
9577 if (count == 1 && last == target) {
9578 count = 0;
9579 }
9580
9581 // Return the number of referencing objects found.
9582 return count;
9583}
9584
9585
9586// Scan the heap for objects with direct references to an object
9587// args[0]: the object to find references to
9588// args[1]: constructor function for instances to exclude (Mirror)
9589// args[2]: the the maximum number of objects to return
9590static Object* Runtime_DebugReferencedBy(Arguments args) {
9591 ASSERT(args.length() == 3);
9592
9593 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009594 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009595
9596 // Check parameters.
9597 CONVERT_CHECKED(JSObject, target, args[0]);
9598 Object* instance_filter = args[1];
9599 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
9600 instance_filter->IsJSObject());
9601 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
9602 RUNTIME_ASSERT(max_references >= 0);
9603
9604 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009605 JSObject* arguments_boilerplate =
9606 Top::context()->global_context()->arguments_boilerplate();
9607 JSFunction* arguments_function =
9608 JSFunction::cast(arguments_boilerplate->map()->constructor());
9609
9610 // Get the number of referencing objects.
9611 int count;
9612 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009613 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009614
9615 // Allocate an array to hold the result.
9616 Object* object = Heap::AllocateFixedArray(count);
9617 if (object->IsFailure()) return object;
9618 FixedArray* instances = FixedArray::cast(object);
9619
9620 // Fill the referencing objects.
9621 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009622 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009623
9624 // Return result as JS array.
9625 Object* result =
9626 Heap::AllocateJSObject(
9627 Top::context()->global_context()->array_function());
9628 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9629 return result;
9630}
9631
9632
9633// Helper function used by Runtime_DebugConstructedBy below.
9634static int DebugConstructedBy(JSFunction* constructor, int max_references,
9635 FixedArray* instances, int instances_size) {
9636 AssertNoAllocation no_alloc;
9637
9638 // Iterate the heap.
9639 int count = 0;
9640 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009641 HeapObject* heap_obj = NULL;
9642 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009643 (max_references == 0 || count < max_references)) {
9644 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009645 if (heap_obj->IsJSObject()) {
9646 JSObject* obj = JSObject::cast(heap_obj);
9647 if (obj->map()->constructor() == constructor) {
9648 // Valid reference found add to instance array if supplied an update
9649 // count.
9650 if (instances != NULL && count < instances_size) {
9651 instances->set(count, obj);
9652 }
9653 count++;
9654 }
9655 }
9656 }
9657
9658 // Return the number of referencing objects found.
9659 return count;
9660}
9661
9662
9663// Scan the heap for objects constructed by a specific function.
9664// args[0]: the constructor to find instances of
9665// args[1]: the the maximum number of objects to return
9666static Object* Runtime_DebugConstructedBy(Arguments args) {
9667 ASSERT(args.length() == 2);
9668
9669 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009670 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009671
9672 // Check parameters.
9673 CONVERT_CHECKED(JSFunction, constructor, args[0]);
9674 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
9675 RUNTIME_ASSERT(max_references >= 0);
9676
9677 // Get the number of referencing objects.
9678 int count;
9679 count = DebugConstructedBy(constructor, max_references, NULL, 0);
9680
9681 // Allocate an array to hold the result.
9682 Object* object = Heap::AllocateFixedArray(count);
9683 if (object->IsFailure()) return object;
9684 FixedArray* instances = FixedArray::cast(object);
9685
9686 // Fill the referencing objects.
9687 count = DebugConstructedBy(constructor, max_references, instances, count);
9688
9689 // Return result as JS array.
9690 Object* result =
9691 Heap::AllocateJSObject(
9692 Top::context()->global_context()->array_function());
9693 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9694 return result;
9695}
9696
9697
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009698// Find the effective prototype object as returned by __proto__.
9699// args[0]: the object to find the prototype for.
9700static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009701 ASSERT(args.length() == 1);
9702
9703 CONVERT_CHECKED(JSObject, obj, args[0]);
9704
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009705 // Use the __proto__ accessor.
9706 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009707}
9708
9709
9710static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009711 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009712 CPU::DebugBreak();
9713 return Heap::undefined_value();
9714}
9715
9716
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009717static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009718#ifdef DEBUG
9719 HandleScope scope;
9720 ASSERT(args.length() == 1);
9721 // Get the function and make sure it is compiled.
9722 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009723 Handle<SharedFunctionInfo> shared(func->shared());
9724 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009725 return Failure::Exception();
9726 }
9727 func->code()->PrintLn();
9728#endif // DEBUG
9729 return Heap::undefined_value();
9730}
ager@chromium.org9085a012009-05-11 19:22:57 +00009731
9732
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009733static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
9734#ifdef DEBUG
9735 HandleScope scope;
9736 ASSERT(args.length() == 1);
9737 // Get the function and make sure it is compiled.
9738 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009739 Handle<SharedFunctionInfo> shared(func->shared());
9740 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009741 return Failure::Exception();
9742 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009743 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009744#endif // DEBUG
9745 return Heap::undefined_value();
9746}
9747
9748
ager@chromium.org9085a012009-05-11 19:22:57 +00009749static Object* Runtime_FunctionGetInferredName(Arguments args) {
9750 NoHandleAllocation ha;
9751 ASSERT(args.length() == 1);
9752
9753 CONVERT_CHECKED(JSFunction, f, args[0]);
9754 return f->shared()->inferred_name();
9755}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009756
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009757
9758static int FindSharedFunctionInfosForScript(Script* script,
9759 FixedArray* buffer) {
9760 AssertNoAllocation no_allocations;
9761
9762 int counter = 0;
9763 int buffer_size = buffer->length();
9764 HeapIterator iterator;
9765 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
9766 ASSERT(obj != NULL);
9767 if (!obj->IsSharedFunctionInfo()) {
9768 continue;
9769 }
9770 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
9771 if (shared->script() != script) {
9772 continue;
9773 }
9774 if (counter < buffer_size) {
9775 buffer->set(counter, shared);
9776 }
9777 counter++;
9778 }
9779 return counter;
9780}
9781
9782// For a script finds all SharedFunctionInfo's in the heap that points
9783// to this script. Returns JSArray of SharedFunctionInfo wrapped
9784// in OpaqueReferences.
9785static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
9786 Arguments args) {
9787 ASSERT(args.length() == 1);
9788 HandleScope scope;
9789 CONVERT_CHECKED(JSValue, script_value, args[0]);
9790
9791 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
9792
9793 const int kBufferSize = 32;
9794
9795 Handle<FixedArray> array;
9796 array = Factory::NewFixedArray(kBufferSize);
9797 int number = FindSharedFunctionInfosForScript(*script, *array);
9798 if (number > kBufferSize) {
9799 array = Factory::NewFixedArray(number);
9800 FindSharedFunctionInfosForScript(*script, *array);
9801 }
9802
9803 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
9804 result->set_length(Smi::FromInt(number));
9805
9806 LiveEdit::WrapSharedFunctionInfos(result);
9807
9808 return *result;
9809}
9810
9811// For a script calculates compilation information about all its functions.
9812// The script source is explicitly specified by the second argument.
9813// The source of the actual script is not used, however it is important that
9814// all generated code keeps references to this particular instance of script.
9815// Returns a JSArray of compilation infos. The array is ordered so that
9816// each function with all its descendant is always stored in a continues range
9817// with the function itself going first. The root function is a script function.
9818static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
9819 ASSERT(args.length() == 2);
9820 HandleScope scope;
9821 CONVERT_CHECKED(JSValue, script, args[0]);
9822 CONVERT_ARG_CHECKED(String, source, 1);
9823 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
9824
9825 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
9826
9827 if (Top::has_pending_exception()) {
9828 return Failure::Exception();
9829 }
9830
9831 return result;
9832}
9833
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009834// Changes the source of the script to a new_source.
9835// If old_script_name is provided (i.e. is a String), also creates a copy of
9836// the script with its original source and sends notification to debugger.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009837static Object* Runtime_LiveEditReplaceScript(Arguments args) {
9838 ASSERT(args.length() == 3);
9839 HandleScope scope;
9840 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
9841 CONVERT_ARG_CHECKED(String, new_source, 1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009842 Handle<Object> old_script_name(args[2]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009843
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009844 CONVERT_CHECKED(Script, original_script_pointer,
9845 original_script_value->value());
9846 Handle<Script> original_script(original_script_pointer);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009847
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009848 Object* old_script = LiveEdit::ChangeScriptSource(original_script,
9849 new_source,
9850 old_script_name);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009851
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009852 if (old_script->IsScript()) {
9853 Handle<Script> script_handle(Script::cast(old_script));
9854 return *(GetScriptWrapper(script_handle));
9855 } else {
9856 return Heap::null_value();
9857 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009858}
9859
9860// Replaces code of SharedFunctionInfo with a new one.
9861static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
9862 ASSERT(args.length() == 2);
9863 HandleScope scope;
9864 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
9865 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
9866
ager@chromium.orgac091b72010-05-05 07:34:42 +00009867 return LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009868}
9869
9870// Connects SharedFunctionInfo to another script.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009871static Object* Runtime_LiveEditFunctionSetScript(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009872 ASSERT(args.length() == 2);
9873 HandleScope scope;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009874 Handle<Object> function_object(args[0]);
9875 Handle<Object> script_object(args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009876
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009877 if (function_object->IsJSValue()) {
9878 Handle<JSValue> function_wrapper = Handle<JSValue>::cast(function_object);
9879 if (script_object->IsJSValue()) {
9880 CONVERT_CHECKED(Script, script, JSValue::cast(*script_object)->value());
9881 script_object = Handle<Object>(script);
9882 }
9883
9884 LiveEdit::SetFunctionScript(function_wrapper, script_object);
9885 } else {
9886 // Just ignore this. We may not have a SharedFunctionInfo for some functions
9887 // and we check it in this function.
9888 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009889
9890 return Heap::undefined_value();
9891}
9892
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009893
9894// In a code of a parent function replaces original function as embedded object
9895// with a substitution one.
9896static Object* Runtime_LiveEditReplaceRefToNestedFunction(Arguments args) {
9897 ASSERT(args.length() == 3);
9898 HandleScope scope;
9899
9900 CONVERT_ARG_CHECKED(JSValue, parent_wrapper, 0);
9901 CONVERT_ARG_CHECKED(JSValue, orig_wrapper, 1);
9902 CONVERT_ARG_CHECKED(JSValue, subst_wrapper, 2);
9903
9904 LiveEdit::ReplaceRefToNestedFunction(parent_wrapper, orig_wrapper,
9905 subst_wrapper);
9906
9907 return Heap::undefined_value();
9908}
9909
9910
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009911// Updates positions of a shared function info (first parameter) according
9912// to script source change. Text change is described in second parameter as
9913// array of groups of 3 numbers:
9914// (change_begin, change_end, change_end_new_position).
9915// Each group describes a change in text; groups are sorted by change_begin.
9916static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
9917 ASSERT(args.length() == 2);
9918 HandleScope scope;
9919 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
9920 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
9921
ager@chromium.orgac091b72010-05-05 07:34:42 +00009922 return LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009923}
9924
9925
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009926// For array of SharedFunctionInfo's (each wrapped in JSValue)
9927// checks that none of them have activations on stacks (of any thread).
9928// Returns array of the same length with corresponding results of
9929// LiveEdit::FunctionPatchabilityStatus type.
ager@chromium.org357bf652010-04-12 11:30:10 +00009930static Object* Runtime_LiveEditCheckAndDropActivations(Arguments args) {
9931 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009932 HandleScope scope;
9933 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +00009934 CONVERT_BOOLEAN_CHECKED(do_drop, args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009935
ager@chromium.org357bf652010-04-12 11:30:10 +00009936 return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009937}
9938
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009939// Compares 2 strings line-by-line and returns diff in form of JSArray of
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00009940// triplets (pos1, pos1_end, pos2_end) describing list of diff chunks.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009941static Object* Runtime_LiveEditCompareStringsLinewise(Arguments args) {
9942 ASSERT(args.length() == 2);
9943 HandleScope scope;
9944 CONVERT_ARG_CHECKED(String, s1, 0);
9945 CONVERT_ARG_CHECKED(String, s2, 1);
9946
9947 return *LiveEdit::CompareStringsLinewise(s1, s2);
9948}
9949
9950
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009951
fschneider@chromium.org086aac62010-03-17 13:18:24 +00009952// A testing entry. Returns statement position which is the closest to
9953// source_position.
9954static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
9955 ASSERT(args.length() == 2);
9956 HandleScope scope;
9957 CONVERT_ARG_CHECKED(JSFunction, function, 0);
9958 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9959
9960 Handle<Code> code(function->code());
9961
9962 RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
9963 int closest_pc = 0;
9964 int distance = kMaxInt;
9965 while (!it.done()) {
9966 int statement_position = static_cast<int>(it.rinfo()->data());
9967 // Check if this break point is closer that what was previously found.
9968 if (source_position <= statement_position &&
9969 statement_position - source_position < distance) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00009970 closest_pc =
9971 static_cast<int>(it.rinfo()->pc() - code->instruction_start());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00009972 distance = statement_position - source_position;
9973 // Check whether we can't get any closer.
9974 if (distance == 0) break;
9975 }
9976 it.next();
9977 }
9978
9979 return Smi::FromInt(closest_pc);
9980}
9981
9982
ager@chromium.org357bf652010-04-12 11:30:10 +00009983// Calls specified function with or without entering the debugger.
9984// This is used in unit tests to run code as if debugger is entered or simply
9985// to have a stack with C++ frame in the middle.
9986static Object* Runtime_ExecuteInDebugContext(Arguments args) {
9987 ASSERT(args.length() == 2);
9988 HandleScope scope;
9989 CONVERT_ARG_CHECKED(JSFunction, function, 0);
9990 CONVERT_BOOLEAN_CHECKED(without_debugger, args[1]);
9991
9992 Handle<Object> result;
9993 bool pending_exception;
9994 {
9995 if (without_debugger) {
9996 result = Execution::Call(function, Top::global(), 0, NULL,
9997 &pending_exception);
9998 } else {
9999 EnterDebugger enter_debugger;
10000 result = Execution::Call(function, Top::global(), 0, NULL,
10001 &pending_exception);
10002 }
10003 }
10004 if (!pending_exception) {
10005 return *result;
10006 } else {
10007 return Failure::Exception();
10008 }
10009}
10010
10011
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010012#endif // ENABLE_DEBUGGER_SUPPORT
10013
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010014#ifdef ENABLE_LOGGING_AND_PROFILING
10015
10016static Object* Runtime_ProfilerResume(Arguments args) {
10017 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010018 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010019
10020 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010021 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10022 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010023 return Heap::undefined_value();
10024}
10025
10026
10027static Object* Runtime_ProfilerPause(Arguments args) {
10028 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010029 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010030
10031 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010032 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10033 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010034 return Heap::undefined_value();
10035}
10036
10037#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010038
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010039// Finds the script object from the script data. NOTE: This operation uses
10040// heap traversal to find the function generated for the source position
10041// for the requested break point. For lazily compiled functions several heap
10042// traversals might be required rendering this operation as a rather slow
10043// operation. However for setting break points which is normally done through
10044// some kind of user interaction the performance is not crucial.
10045static Handle<Object> Runtime_GetScriptFromScriptName(
10046 Handle<String> script_name) {
10047 // Scan the heap for Script objects to find the script with the requested
10048 // script data.
10049 Handle<Script> script;
10050 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010051 HeapObject* obj = NULL;
10052 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010053 // If a script is found check if it has the script data requested.
10054 if (obj->IsScript()) {
10055 if (Script::cast(obj)->name()->IsString()) {
10056 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
10057 script = Handle<Script>(Script::cast(obj));
10058 }
10059 }
10060 }
10061 }
10062
10063 // If no script with the requested script data is found return undefined.
10064 if (script.is_null()) return Factory::undefined_value();
10065
10066 // Return the script found.
10067 return GetScriptWrapper(script);
10068}
10069
10070
10071// Get the script object from script data. NOTE: Regarding performance
10072// see the NOTE for GetScriptFromScriptData.
10073// args[0]: script data for the script to find the source for
10074static Object* Runtime_GetScript(Arguments args) {
10075 HandleScope scope;
10076
10077 ASSERT(args.length() == 1);
10078
10079 CONVERT_CHECKED(String, script_name, args[0]);
10080
10081 // Find the requested script.
10082 Handle<Object> result =
10083 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
10084 return *result;
10085}
10086
10087
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010088// Determines whether the given stack frame should be displayed in
10089// a stack trace. The caller is the error constructor that asked
10090// for the stack trace to be collected. The first time a construct
10091// call to this function is encountered it is skipped. The seen_caller
10092// in/out parameter is used to remember if the caller has been seen
10093// yet.
10094static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
10095 bool* seen_caller) {
10096 // Only display JS frames.
10097 if (!raw_frame->is_java_script())
10098 return false;
10099 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
10100 Object* raw_fun = frame->function();
10101 // Not sure when this can happen but skip it just in case.
10102 if (!raw_fun->IsJSFunction())
10103 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010104 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010105 *seen_caller = true;
10106 return false;
10107 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010108 // Skip all frames until we've seen the caller. Also, skip the most
10109 // obvious builtin calls. Some builtin calls (such as Number.ADD
10110 // which is invoked using 'call') are very difficult to recognize
10111 // so we're leaving them in for now.
10112 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010113}
10114
10115
10116// Collect the raw data for a stack trace. Returns an array of three
10117// element segments each containing a receiver, function and native
10118// code offset.
10119static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010120 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010121 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010122 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
10123
10124 HandleScope scope;
10125
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000010126 limit = Max(limit, 0); // Ensure that limit is not negative.
10127 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010128 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010129
10130 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010131 // If the caller parameter is a function we skip frames until we're
10132 // under it before starting to collect.
10133 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010134 int cursor = 0;
10135 int frames_seen = 0;
10136 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010137 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010138 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010139 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010140 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010141 Object* recv = frame->receiver();
10142 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010143 Address pc = frame->pc();
10144 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010145 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010146 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010147 if (cursor + 2 < elements->length()) {
10148 elements->set(cursor++, recv);
10149 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010150 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010151 } else {
10152 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010153 Handle<Object> recv_handle(recv);
10154 Handle<Object> fun_handle(fun);
10155 SetElement(result, cursor++, recv_handle);
10156 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010157 SetElement(result, cursor++, Handle<Smi>(offset));
10158 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010159 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010160 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010161 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010162
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010163 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010164 return *result;
10165}
10166
10167
ager@chromium.org3811b432009-10-28 14:53:37 +000010168// Returns V8 version as a string.
10169static Object* Runtime_GetV8Version(Arguments args) {
10170 ASSERT_EQ(args.length(), 0);
10171
10172 NoHandleAllocation ha;
10173
10174 const char* version_string = v8::V8::GetVersion();
10175
10176 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
10177}
10178
10179
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010180static Object* Runtime_Abort(Arguments args) {
10181 ASSERT(args.length() == 2);
10182 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
10183 Smi::cast(args[1])->value());
10184 Top::PrintStack();
10185 OS::Abort();
10186 UNREACHABLE();
10187 return NULL;
10188}
10189
10190
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010191static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
10192 ASSERT(args.length() == 0);
10193 HandleScope::DeleteExtensions();
10194 return Heap::undefined_value();
10195}
10196
10197
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010198static Object* CacheMiss(FixedArray* cache_obj, int index, Object* key_obj) {
10199 ASSERT(index % 2 == 0); // index of the key
10200 ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
10201 ASSERT(index < cache_obj->length());
10202
10203 HandleScope scope;
10204
10205 Handle<FixedArray> cache(cache_obj);
10206 Handle<Object> key(key_obj);
10207 Handle<JSFunction> factory(JSFunction::cast(
10208 cache->get(JSFunctionResultCache::kFactoryIndex)));
10209 // TODO(antonm): consider passing a receiver when constructing a cache.
10210 Handle<Object> receiver(Top::global_context()->global());
10211
10212 Handle<Object> value;
10213 {
10214 // This handle is nor shared, nor used later, so it's safe.
10215 Object** argv[] = { key.location() };
10216 bool pending_exception = false;
10217 value = Execution::Call(factory,
10218 receiver,
10219 1,
10220 argv,
10221 &pending_exception);
10222 if (pending_exception) return Failure::Exception();
10223 }
10224
10225 cache->set(index, *key);
10226 cache->set(index + 1, *value);
10227 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(index));
10228
10229 return *value;
10230}
10231
10232
10233static Object* Runtime_GetFromCache(Arguments args) {
10234 // This is only called from codegen, so checks might be more lax.
10235 CONVERT_CHECKED(FixedArray, cache, args[0]);
10236 Object* key = args[1];
10237
10238 const int finger_index =
10239 Smi::cast(cache->get(JSFunctionResultCache::kFingerIndex))->value();
10240
10241 Object* o = cache->get(finger_index);
10242 if (o == key) {
10243 // The fastest case: hit the same place again.
10244 return cache->get(finger_index + 1);
10245 }
10246
10247 for (int i = finger_index - 2;
10248 i >= JSFunctionResultCache::kEntriesIndex;
10249 i -= 2) {
10250 o = cache->get(i);
10251 if (o == key) {
10252 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10253 return cache->get(i + 1);
10254 }
10255 }
10256
10257 const int size =
10258 Smi::cast(cache->get(JSFunctionResultCache::kCacheSizeIndex))->value();
10259 ASSERT(size <= cache->length());
10260
10261 for (int i = size - 2; i > finger_index; i -= 2) {
10262 o = cache->get(i);
10263 if (o == key) {
10264 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10265 return cache->get(i + 1);
10266 }
10267 }
10268
10269 // Cache miss. If we have spare room, put new data into it, otherwise
10270 // evict post finger entry which must be least recently used.
10271 if (size < cache->length()) {
10272 cache->set(JSFunctionResultCache::kCacheSizeIndex, Smi::FromInt(size + 2));
10273 return CacheMiss(cache, size, key);
10274 } else {
antonm@chromium.org397e23c2010-04-21 12:00:05 +000010275 int target_index = finger_index + JSFunctionResultCache::kEntrySize;
10276 if (target_index == cache->length()) {
10277 target_index = JSFunctionResultCache::kEntriesIndex;
10278 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010279 return CacheMiss(cache, target_index, key);
10280 }
10281}
10282
kasper.lund44510672008-07-25 07:37:58 +000010283#ifdef DEBUG
10284// ListNatives is ONLY used by the fuzz-natives.js in debug mode
10285// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010286static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +000010287 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010288 HandleScope scope;
10289 Handle<JSArray> result = Factory::NewJSArray(0);
10290 int index = 0;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010291 bool inline_runtime_functions = false;
ager@chromium.orga1645e22009-09-09 19:27:10 +000010292#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010293 { \
10294 HandleScope inner; \
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010295 Handle<String> name; \
10296 /* Inline runtime functions have an underscore in front of the name. */ \
10297 if (inline_runtime_functions) { \
10298 name = Factory::NewStringFromAscii( \
10299 Vector<const char>("_" #Name, StrLength("_" #Name))); \
10300 } else { \
10301 name = Factory::NewStringFromAscii( \
10302 Vector<const char>(#Name, StrLength(#Name))); \
10303 } \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010304 Handle<JSArray> pair = Factory::NewJSArray(0); \
10305 SetElement(pair, 0, name); \
10306 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
10307 SetElement(result, index++, pair); \
10308 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010309 inline_runtime_functions = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010310 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010311 inline_runtime_functions = true;
10312 INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010313#undef ADD_ENTRY
10314 return *result;
10315}
kasper.lund44510672008-07-25 07:37:58 +000010316#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010317
10318
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010319static Object* Runtime_Log(Arguments args) {
10320 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +000010321 CONVERT_CHECKED(String, format, args[0]);
10322 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010323 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010324 Logger::LogRuntime(chars, elms);
10325 return Heap::undefined_value();
10326}
10327
10328
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010329static Object* Runtime_IS_VAR(Arguments args) {
10330 UNREACHABLE(); // implemented as macro in the parser
10331 return NULL;
10332}
10333
10334
10335// ----------------------------------------------------------------------------
10336// Implementation of Runtime
10337
ager@chromium.orga1645e22009-09-09 19:27:10 +000010338#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010339 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +000010340 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010341
10342static Runtime::Function Runtime_functions[] = {
10343 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010344 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010345};
10346
10347#undef F
10348
10349
10350Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
10351 ASSERT(0 <= fid && fid < kNofFunctions);
10352 return &Runtime_functions[fid];
10353}
10354
10355
10356Runtime::Function* Runtime::FunctionForName(const char* name) {
10357 for (Function* f = Runtime_functions; f->name != NULL; f++) {
10358 if (strcmp(f->name, name) == 0) {
10359 return f;
10360 }
10361 }
10362 return NULL;
10363}
10364
10365
10366void Runtime::PerformGC(Object* result) {
10367 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010368 if (failure->IsRetryAfterGC()) {
10369 // Try to do a garbage collection; ignore it if it fails. The C
10370 // entry stub will throw an out-of-memory exception in that case.
10371 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
10372 } else {
10373 // Handle last resort GC and make sure to allow future allocations
10374 // to grow the heap without causing GCs (if possible).
10375 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +000010376 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010377 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010378}
10379
10380
10381} } // namespace v8::internal