blob: 2eddaab42f00454f04c1126cb788cac497be2e3c [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());
ricow@chromium.org0b9f8502010-08-18 07:45:01 +0000163 if (elements->map() == Heap::fixed_cow_array_map()) {
164 Counters::cow_arrays_created_runtime.Increment();
165#ifdef DEBUG
166 for (int i = 0; i < elements->length(); i++) {
167 ASSERT(!elements->get(i)->IsJSObject());
168 }
169#endif
170 } else {
171 for (int i = 0; i < elements->length(); i++) {
172 Object* value = elements->get(i);
173 if (value->IsJSObject()) {
174 JSObject* js_object = JSObject::cast(value);
175 result = DeepCopyBoilerplate(js_object);
176 if (result->IsFailure()) return result;
177 elements->set(i, result);
178 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000179 }
180 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000181 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000182 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000183 case JSObject::DICTIONARY_ELEMENTS: {
184 NumberDictionary* element_dictionary = copy->element_dictionary();
185 int capacity = element_dictionary->Capacity();
186 for (int i = 0; i < capacity; i++) {
187 Object* k = element_dictionary->KeyAt(i);
188 if (element_dictionary->IsKey(k)) {
189 Object* value = element_dictionary->ValueAt(i);
190 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000191 JSObject* js_object = JSObject::cast(value);
192 result = DeepCopyBoilerplate(js_object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000193 if (result->IsFailure()) return result;
194 element_dictionary->ValueAtPut(i, result);
195 }
196 }
197 }
198 break;
199 }
200 default:
201 UNREACHABLE();
202 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000203 }
204 return copy;
205}
206
207
208static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
209 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
210 return DeepCopyBoilerplate(boilerplate);
211}
212
213
214static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000215 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000216 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000217}
218
219
ager@chromium.org236ad962008-09-25 09:45:57 +0000220static Handle<Map> ComputeObjectLiteralMap(
221 Handle<Context> context,
222 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000223 bool* is_result_from_cache) {
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +0000224 int properties_length = constant_properties->length();
225 int number_of_properties = properties_length / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000226 if (FLAG_canonicalize_object_literal_maps) {
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +0000227 // Check that there are only symbols and array indices among keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000228 int number_of_symbol_keys = 0;
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +0000229 for (int p = 0; p != properties_length; p += 2) {
230 Object* key = constant_properties->get(p);
231 uint32_t element_index = 0;
232 if (key->IsSymbol()) {
233 number_of_symbol_keys++;
234 } else if (key->ToArrayIndex(&element_index)) {
235 // An index key does not require space in the property backing store.
236 number_of_properties--;
237 } else {
238 // Bail out as a non-symbol non-index key makes caching impossible.
239 // ASSERT to make sure that the if condition after the loop is false.
240 ASSERT(number_of_symbol_keys != number_of_properties);
241 break;
242 }
ager@chromium.org236ad962008-09-25 09:45:57 +0000243 }
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +0000244 // If we only have symbols and array indices among keys then we can
245 // use the map cache in the global context.
ager@chromium.org236ad962008-09-25 09:45:57 +0000246 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000247 if ((number_of_symbol_keys == number_of_properties) &&
248 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000249 // Create the fixed array with the key.
250 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +0000251 if (number_of_symbol_keys > 0) {
252 int index = 0;
253 for (int p = 0; p < properties_length; p += 2) {
254 Object* key = constant_properties->get(p);
255 if (key->IsSymbol()) {
256 keys->set(index++, key);
257 }
258 }
259 ASSERT(index == number_of_symbol_keys);
ager@chromium.org236ad962008-09-25 09:45:57 +0000260 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000261 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000262 return Factory::ObjectLiteralMapFromCache(context, keys);
263 }
264 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000265 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000266 return Factory::CopyMap(
267 Handle<Map>(context->object_function()->initial_map()),
268 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000269}
270
271
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000272static Handle<Object> CreateLiteralBoilerplate(
273 Handle<FixedArray> literals,
274 Handle<FixedArray> constant_properties);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000275
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000276
277static Handle<Object> CreateObjectLiteralBoilerplate(
278 Handle<FixedArray> literals,
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000279 Handle<FixedArray> constant_properties,
280 bool should_have_fast_elements) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000281 // Get the global context from the literals array. This is the
282 // context in which the function was created and we use the object
283 // function from this context to create the object literal. We do
284 // not use the object function from the current global context
285 // because this might be the object function from another context
286 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000287 Handle<Context> context =
288 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
289
290 bool is_result_from_cache;
291 Handle<Map> map = ComputeObjectLiteralMap(context,
292 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000293 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000294
ager@chromium.org236ad962008-09-25 09:45:57 +0000295 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000296
297 // Normalize the elements of the boilerplate to save space if needed.
298 if (!should_have_fast_elements) NormalizeElements(boilerplate);
299
ager@chromium.org32912102009-01-16 10:38:43 +0000300 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000301 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000302 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000303 length / 2,
ager@chromium.org236ad962008-09-25 09:45:57 +0000304 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000305 for (int index = 0; index < length; index +=2) {
306 Handle<Object> key(constant_properties->get(index+0));
307 Handle<Object> value(constant_properties->get(index+1));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000308 if (value->IsFixedArray()) {
309 // The value contains the constant_properties of a
310 // simple object literal.
311 Handle<FixedArray> array = Handle<FixedArray>::cast(value);
312 value = CreateLiteralBoilerplate(literals, array);
313 if (value.is_null()) return value;
314 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000315 Handle<Object> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000316 uint32_t element_index = 0;
vegorov@chromium.org26c16f82010-08-11 13:41:03 +0000317 if (key->IsSymbol()) {
318 // If key is a symbol it is not an array element.
319 Handle<String> name(String::cast(*key));
320 ASSERT(!name->AsArrayIndex(&element_index));
321 result = SetProperty(boilerplate, name, value, NONE);
322 } else if (key->ToArrayIndex(&element_index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000323 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000324 result = SetElement(boilerplate, element_index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000325 } else {
326 // Non-uint32 number.
327 ASSERT(key->IsNumber());
328 double num = key->Number();
329 char arr[100];
330 Vector<char> buffer(arr, ARRAY_SIZE(arr));
331 const char* str = DoubleToCString(num, buffer);
332 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000333 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000334 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000335 // If setting the property on the boilerplate throws an
336 // exception, the exception is converted to an empty handle in
337 // the handle based operations. In that case, we need to
338 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000339 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000340 }
341 }
342
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000343 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000344}
345
346
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000347static Handle<Object> CreateArrayLiteralBoilerplate(
348 Handle<FixedArray> literals,
349 Handle<FixedArray> elements) {
350 // Create the JSArray.
351 Handle<JSFunction> constructor(
352 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
353 Handle<Object> object = Factory::NewJSObject(constructor);
354
ricow@chromium.org0b9f8502010-08-18 07:45:01 +0000355 const bool is_cow = (elements->map() == Heap::fixed_cow_array_map());
356 Handle<FixedArray> copied_elements =
357 is_cow ? elements : Factory::CopyFixedArray(elements);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000358
359 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
ricow@chromium.org0b9f8502010-08-18 07:45:01 +0000360 if (is_cow) {
361#ifdef DEBUG
362 // Copy-on-write arrays must be shallow (and simple).
363 for (int i = 0; i < content->length(); i++) {
364 ASSERT(!content->get(i)->IsFixedArray());
365 }
366#endif
367 } else {
368 for (int i = 0; i < content->length(); i++) {
369 if (content->get(i)->IsFixedArray()) {
370 // The value contains the constant_properties of a
371 // simple object literal.
372 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
373 Handle<Object> result =
374 CreateLiteralBoilerplate(literals, fa);
375 if (result.is_null()) return result;
376 content->set(i, *result);
377 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000378 }
379 }
380
381 // Set the elements.
382 Handle<JSArray>::cast(object)->SetContent(*content);
383 return object;
384}
385
386
387static Handle<Object> CreateLiteralBoilerplate(
388 Handle<FixedArray> literals,
389 Handle<FixedArray> array) {
390 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
391 switch (CompileTimeValue::GetType(array)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000392 case CompileTimeValue::OBJECT_LITERAL_FAST_ELEMENTS:
393 return CreateObjectLiteralBoilerplate(literals, elements, true);
394 case CompileTimeValue::OBJECT_LITERAL_SLOW_ELEMENTS:
395 return CreateObjectLiteralBoilerplate(literals, elements, false);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000396 case CompileTimeValue::ARRAY_LITERAL:
397 return CreateArrayLiteralBoilerplate(literals, elements);
398 default:
399 UNREACHABLE();
400 return Handle<Object>::null();
401 }
402}
403
404
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000405static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000406 // Takes a FixedArray of elements containing the literal elements of
407 // the array literal and produces JSArray with those elements.
408 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000409 // which contains the context from which to get the Array function
410 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000411 HandleScope scope;
412 ASSERT(args.length() == 3);
413 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
414 CONVERT_SMI_CHECKED(literals_index, args[1]);
415 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000416
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000417 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
418 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000419
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000420 // Update the functions literal and return the boilerplate.
421 literals->set(literals_index, *object);
422 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000423}
424
425
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000426static Object* Runtime_CreateObjectLiteral(Arguments args) {
427 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000428 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000429 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
430 CONVERT_SMI_CHECKED(literals_index, args[1]);
431 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000432 CONVERT_SMI_CHECKED(fast_elements, args[3]);
433 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000434
435 // Check if boilerplate exists. If not, create it first.
436 Handle<Object> boilerplate(literals->get(literals_index));
437 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000438 boilerplate = CreateObjectLiteralBoilerplate(literals,
439 constant_properties,
440 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000441 if (boilerplate.is_null()) return Failure::Exception();
442 // Update the functions literal and return the boilerplate.
443 literals->set(literals_index, *boilerplate);
444 }
445 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
446}
447
448
449static Object* Runtime_CreateObjectLiteralShallow(Arguments args) {
450 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000451 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000452 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
453 CONVERT_SMI_CHECKED(literals_index, args[1]);
454 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000455 CONVERT_SMI_CHECKED(fast_elements, args[3]);
456 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000457
458 // Check if boilerplate exists. If not, create it first.
459 Handle<Object> boilerplate(literals->get(literals_index));
460 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000461 boilerplate = CreateObjectLiteralBoilerplate(literals,
462 constant_properties,
463 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000464 if (boilerplate.is_null()) return Failure::Exception();
465 // Update the functions literal and return the boilerplate.
466 literals->set(literals_index, *boilerplate);
467 }
468 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
469}
470
471
472static Object* Runtime_CreateArrayLiteral(Arguments args) {
473 HandleScope scope;
474 ASSERT(args.length() == 3);
475 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
476 CONVERT_SMI_CHECKED(literals_index, args[1]);
477 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
478
479 // Check if boilerplate exists. If not, create it first.
480 Handle<Object> boilerplate(literals->get(literals_index));
481 if (*boilerplate == Heap::undefined_value()) {
482 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
483 if (boilerplate.is_null()) return Failure::Exception();
484 // Update the functions literal and return the boilerplate.
485 literals->set(literals_index, *boilerplate);
486 }
487 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
488}
489
490
491static Object* Runtime_CreateArrayLiteralShallow(Arguments args) {
492 HandleScope scope;
493 ASSERT(args.length() == 3);
494 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
495 CONVERT_SMI_CHECKED(literals_index, args[1]);
496 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
497
498 // Check if boilerplate exists. If not, create it first.
499 Handle<Object> boilerplate(literals->get(literals_index));
500 if (*boilerplate == Heap::undefined_value()) {
501 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
502 if (boilerplate.is_null()) return Failure::Exception();
503 // Update the functions literal and return the boilerplate.
504 literals->set(literals_index, *boilerplate);
505 }
ricow@chromium.org0b9f8502010-08-18 07:45:01 +0000506 if (JSObject::cast(*boilerplate)->elements()->map() ==
507 Heap::fixed_cow_array_map()) {
508 Counters::cow_arrays_created_runtime.Increment();
509 }
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000510 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
511}
512
513
ager@chromium.org32912102009-01-16 10:38:43 +0000514static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
515 ASSERT(args.length() == 2);
516 CONVERT_CHECKED(String, key, args[0]);
517 Object* value = args[1];
518 // Create a catch context extension object.
519 JSFunction* constructor =
520 Top::context()->global_context()->context_extension_function();
521 Object* object = Heap::AllocateJSObject(constructor);
522 if (object->IsFailure()) return object;
523 // Assign the exception value to the catch variable and make sure
524 // that the catch variable is DontDelete.
525 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
526 if (value->IsFailure()) return value;
527 return object;
528}
529
530
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000531static Object* Runtime_ClassOf(Arguments args) {
532 NoHandleAllocation ha;
533 ASSERT(args.length() == 1);
534 Object* obj = args[0];
535 if (!obj->IsJSObject()) return Heap::null_value();
536 return JSObject::cast(obj)->class_name();
537}
538
ager@chromium.org7c537e22008-10-16 08:43:32 +0000539
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000540static Object* Runtime_IsInPrototypeChain(Arguments args) {
541 NoHandleAllocation ha;
542 ASSERT(args.length() == 2);
543 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
544 Object* O = args[0];
545 Object* V = args[1];
546 while (true) {
547 Object* prototype = V->GetPrototype();
548 if (prototype->IsNull()) return Heap::false_value();
549 if (O == prototype) return Heap::true_value();
550 V = prototype;
551 }
552}
553
554
ager@chromium.org9085a012009-05-11 19:22:57 +0000555// Inserts an object as the hidden prototype of another object.
556static Object* Runtime_SetHiddenPrototype(Arguments args) {
557 NoHandleAllocation ha;
558 ASSERT(args.length() == 2);
559 CONVERT_CHECKED(JSObject, jsobject, args[0]);
560 CONVERT_CHECKED(JSObject, proto, args[1]);
561
562 // Sanity checks. The old prototype (that we are replacing) could
563 // theoretically be null, but if it is not null then check that we
564 // didn't already install a hidden prototype here.
565 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
566 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
567 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
568
569 // Allocate up front before we start altering state in case we get a GC.
570 Object* map_or_failure = proto->map()->CopyDropTransitions();
571 if (map_or_failure->IsFailure()) return map_or_failure;
572 Map* new_proto_map = Map::cast(map_or_failure);
573
574 map_or_failure = jsobject->map()->CopyDropTransitions();
575 if (map_or_failure->IsFailure()) return map_or_failure;
576 Map* new_map = Map::cast(map_or_failure);
577
578 // Set proto's prototype to be the old prototype of the object.
579 new_proto_map->set_prototype(jsobject->GetPrototype());
580 proto->set_map(new_proto_map);
581 new_proto_map->set_is_hidden_prototype();
582
583 // Set the object's prototype to proto.
584 new_map->set_prototype(proto);
585 jsobject->set_map(new_map);
586
587 return Heap::undefined_value();
588}
589
590
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000591static Object* Runtime_IsConstructCall(Arguments args) {
592 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000593 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000594 JavaScriptFrameIterator it;
595 return Heap::ToBoolean(it.frame()->IsConstructor());
596}
597
598
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000599// Recursively traverses hidden prototypes if property is not found
600static void GetOwnPropertyImplementation(JSObject* obj,
601 String* name,
602 LookupResult* result) {
603 obj->LocalLookupRealNamedProperty(name, result);
604
605 if (!result->IsProperty()) {
606 Object* proto = obj->GetPrototype();
607 if (proto->IsJSObject() &&
608 JSObject::cast(proto)->map()->is_hidden_prototype())
609 GetOwnPropertyImplementation(JSObject::cast(proto),
610 name, result);
611 }
612}
613
614
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000615// Enumerator used as indices into the array returned from GetOwnProperty
616enum PropertyDescriptorIndices {
617 IS_ACCESSOR_INDEX,
618 VALUE_INDEX,
619 GETTER_INDEX,
620 SETTER_INDEX,
621 WRITABLE_INDEX,
622 ENUMERABLE_INDEX,
623 CONFIGURABLE_INDEX,
624 DESCRIPTOR_SIZE
625};
626
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000627// Returns an array with the property description:
628// if args[1] is not a property on args[0]
629// returns undefined
630// if args[1] is a data property on args[0]
631// [false, value, Writeable, Enumerable, Configurable]
632// if args[1] is an accessor on args[0]
633// [true, GetFunction, SetFunction, Enumerable, Configurable]
634static Object* Runtime_GetOwnProperty(Arguments args) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000635 ASSERT(args.length() == 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000636 HandleScope scope;
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000637 Handle<FixedArray> elms = Factory::NewFixedArray(DESCRIPTOR_SIZE);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000638 Handle<JSArray> desc = Factory::NewJSArrayWithElements(elms);
639 LookupResult result;
640 CONVERT_CHECKED(JSObject, obj, args[0]);
641 CONVERT_CHECKED(String, name, args[1]);
642
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000643 // This could be an element.
644 uint32_t index;
645 if (name->AsArrayIndex(&index)) {
646 if (!obj->HasLocalElement(index)) {
647 return Heap::undefined_value();
648 }
649
650 // Special handling of string objects according to ECMAScript 5 15.5.5.2.
651 // Note that this might be a string object with elements other than the
652 // actual string value. This is covered by the subsequent cases.
653 if (obj->IsStringObjectWithCharacterAt(index)) {
654 JSValue* js_value = JSValue::cast(obj);
655 String* str = String::cast(js_value->value());
656 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
657 elms->set(VALUE_INDEX, str->SubString(index, index+1));
658 elms->set(WRITABLE_INDEX, Heap::false_value());
659 elms->set(ENUMERABLE_INDEX, Heap::false_value());
660 elms->set(CONFIGURABLE_INDEX, Heap::false_value());
661 return *desc;
662 }
663
664 // This can potentially be an element in the elements dictionary or
665 // a fast element.
666 if (obj->HasDictionaryElements()) {
667 NumberDictionary* dictionary = obj->element_dictionary();
668 int entry = dictionary->FindEntry(index);
669 PropertyDetails details = dictionary->DetailsAt(entry);
670 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
671 elms->set(VALUE_INDEX, dictionary->ValueAt(entry));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000672 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!details.IsReadOnly()));
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000673 elms->set(ENUMERABLE_INDEX, Heap::ToBoolean(!details.IsDontEnum()));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000674 elms->set(CONFIGURABLE_INDEX, Heap::ToBoolean(!details.IsDontDelete()));
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000675 return *desc;
676 } else {
677 // Elements that are stored as array elements always has:
678 // writable: true, configurable: true, enumerable: true.
679 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
680 elms->set(VALUE_INDEX, obj->GetElement(index));
681 elms->set(WRITABLE_INDEX, Heap::true_value());
682 elms->set(ENUMERABLE_INDEX, Heap::true_value());
683 elms->set(CONFIGURABLE_INDEX, Heap::true_value());
684 return *desc;
685 }
686 }
687
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000688 // Use recursive implementation to also traverse hidden prototypes
689 GetOwnPropertyImplementation(obj, name, &result);
690
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000691 if (!result.IsProperty()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000692 return Heap::undefined_value();
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000693 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000694 if (result.type() == CALLBACKS) {
695 Object* structure = result.GetCallbackObject();
ager@chromium.org5c838252010-02-19 08:53:10 +0000696 if (structure->IsProxy() || structure->IsAccessorInfo()) {
697 // Property that is internally implemented as a callback or
698 // an API defined callback.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000699 Object* value = obj->GetPropertyWithCallback(
700 obj, structure, name, result.holder());
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000701 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
702 elms->set(VALUE_INDEX, value);
703 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!result.IsReadOnly()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000704 } else if (structure->IsFixedArray()) {
705 // __defineGetter__/__defineSetter__ callback.
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000706 elms->set(IS_ACCESSOR_INDEX, Heap::true_value());
707 elms->set(GETTER_INDEX, FixedArray::cast(structure)->get(0));
708 elms->set(SETTER_INDEX, FixedArray::cast(structure)->get(1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000709 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000710 return Heap::undefined_value();
711 }
712 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000713 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
714 elms->set(VALUE_INDEX, result.GetLazyValue());
715 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!result.IsReadOnly()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000716 }
717
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000718 elms->set(ENUMERABLE_INDEX, Heap::ToBoolean(!result.IsDontEnum()));
719 elms->set(CONFIGURABLE_INDEX, Heap::ToBoolean(!result.IsDontDelete()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000720 return *desc;
721}
722
723
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +0000724static Object* Runtime_PreventExtensions(Arguments args) {
725 ASSERT(args.length() == 1);
726 CONVERT_CHECKED(JSObject, obj, args[0]);
727 return obj->PreventExtensions();
728}
729
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000730static Object* Runtime_IsExtensible(Arguments args) {
731 ASSERT(args.length() == 1);
732 CONVERT_CHECKED(JSObject, obj, args[0]);
733 return obj->map()->is_extensible() ? Heap::true_value()
734 : Heap::false_value();
735}
736
737
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000738static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000739 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000740 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000741 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
742 CONVERT_ARG_CHECKED(String, pattern, 1);
743 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000744 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
745 if (result.is_null()) return Failure::Exception();
746 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000747}
748
749
750static Object* Runtime_CreateApiFunction(Arguments args) {
751 HandleScope scope;
752 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000753 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000754 return *Factory::CreateApiFunction(data);
755}
756
757
758static Object* Runtime_IsTemplate(Arguments args) {
759 ASSERT(args.length() == 1);
760 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000761 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000762 return Heap::ToBoolean(result);
763}
764
765
766static Object* Runtime_GetTemplateField(Arguments args) {
767 ASSERT(args.length() == 2);
768 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000769 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000770 int index = field->value();
771 int offset = index * kPointerSize + HeapObject::kHeaderSize;
772 InstanceType type = templ->map()->instance_type();
773 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
774 type == OBJECT_TEMPLATE_INFO_TYPE);
775 RUNTIME_ASSERT(offset > 0);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000776 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000777 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
778 } else {
779 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
780 }
781 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000782}
783
784
ager@chromium.org870a0b62008-11-04 11:43:05 +0000785static Object* Runtime_DisableAccessChecks(Arguments args) {
786 ASSERT(args.length() == 1);
787 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000788 Map* old_map = object->map();
789 bool needs_access_checks = old_map->is_access_check_needed();
790 if (needs_access_checks) {
791 // Copy map so it won't interfere constructor's initial map.
792 Object* new_map = old_map->CopyDropTransitions();
793 if (new_map->IsFailure()) return new_map;
794
795 Map::cast(new_map)->set_is_access_check_needed(false);
796 object->set_map(Map::cast(new_map));
797 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000798 return needs_access_checks ? Heap::true_value() : Heap::false_value();
799}
800
801
802static Object* Runtime_EnableAccessChecks(Arguments args) {
803 ASSERT(args.length() == 1);
804 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000805 Map* old_map = object->map();
806 if (!old_map->is_access_check_needed()) {
807 // Copy map so it won't interfere constructor's initial map.
808 Object* new_map = old_map->CopyDropTransitions();
809 if (new_map->IsFailure()) return new_map;
810
811 Map::cast(new_map)->set_is_access_check_needed(true);
812 object->set_map(Map::cast(new_map));
813 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000814 return Heap::undefined_value();
815}
816
817
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000818static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
819 HandleScope scope;
820 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
821 Handle<Object> args[2] = { type_handle, name };
822 Handle<Object> error =
823 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
824 return Top::Throw(*error);
825}
826
827
828static Object* Runtime_DeclareGlobals(Arguments args) {
829 HandleScope scope;
830 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
831
ager@chromium.org3811b432009-10-28 14:53:37 +0000832 Handle<Context> context = args.at<Context>(0);
833 CONVERT_ARG_CHECKED(FixedArray, pairs, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000834 bool is_eval = Smi::cast(args[2])->value() == 1;
835
836 // Compute the property attributes. According to ECMA-262, section
837 // 13, page 71, the property must be read-only and
838 // non-deletable. However, neither SpiderMonkey nor KJS creates the
839 // property as read-only, so we don't either.
840 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
841
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000842 // Traverse the name/value pairs and set the properties.
843 int length = pairs->length();
844 for (int i = 0; i < length; i += 2) {
845 HandleScope scope;
846 Handle<String> name(String::cast(pairs->get(i)));
847 Handle<Object> value(pairs->get(i + 1));
848
849 // We have to declare a global const property. To capture we only
850 // assign to it when evaluating the assignment for "const x =
851 // <expr>" the initial value is the hole.
852 bool is_const_property = value->IsTheHole();
853
854 if (value->IsUndefined() || is_const_property) {
855 // Lookup the property in the global object, and don't set the
856 // value of the variable if the property is already there.
857 LookupResult lookup;
858 global->Lookup(*name, &lookup);
859 if (lookup.IsProperty()) {
860 // Determine if the property is local by comparing the holder
861 // against the global object. The information will be used to
862 // avoid throwing re-declaration errors when declaring
863 // variables or constants that exist in the prototype chain.
864 bool is_local = (*global == lookup.holder());
865 // Get the property attributes and determine if the property is
866 // read-only.
867 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
868 bool is_read_only = (attributes & READ_ONLY) != 0;
869 if (lookup.type() == INTERCEPTOR) {
870 // If the interceptor says the property is there, we
871 // just return undefined without overwriting the property.
872 // Otherwise, we continue to setting the property.
873 if (attributes != ABSENT) {
874 // Check if the existing property conflicts with regards to const.
875 if (is_local && (is_read_only || is_const_property)) {
876 const char* type = (is_read_only) ? "const" : "var";
877 return ThrowRedeclarationError(type, name);
878 };
879 // The property already exists without conflicting: Go to
880 // the next declaration.
881 continue;
882 }
883 // Fall-through and introduce the absent property by using
884 // SetProperty.
885 } else {
886 if (is_local && (is_read_only || is_const_property)) {
887 const char* type = (is_read_only) ? "const" : "var";
888 return ThrowRedeclarationError(type, name);
889 }
890 // The property already exists without conflicting: Go to
891 // the next declaration.
892 continue;
893 }
894 }
895 } else {
896 // Copy the function and update its context. Use it as value.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000897 Handle<SharedFunctionInfo> shared =
898 Handle<SharedFunctionInfo>::cast(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000899 Handle<JSFunction> function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000900 Factory::NewFunctionFromSharedFunctionInfo(shared, context, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000901 value = function;
902 }
903
904 LookupResult lookup;
905 global->LocalLookup(*name, &lookup);
906
907 PropertyAttributes attributes = is_const_property
908 ? static_cast<PropertyAttributes>(base | READ_ONLY)
909 : base;
910
911 if (lookup.IsProperty()) {
912 // There's a local property that we need to overwrite because
913 // we're either declaring a function or there's an interceptor
914 // that claims the property is absent.
915
916 // Check for conflicting re-declarations. We cannot have
917 // conflicting types in case of intercepted properties because
918 // they are absent.
919 if (lookup.type() != INTERCEPTOR &&
920 (lookup.IsReadOnly() || is_const_property)) {
921 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
922 return ThrowRedeclarationError(type, name);
923 }
924 SetProperty(global, name, value, attributes);
925 } else {
926 // If a property with this name does not already exist on the
927 // global object add the property locally. We take special
928 // precautions to always add it as a local property even in case
929 // of callbacks in the prototype chain (this rules out using
930 // SetProperty). Also, we must use the handle-based version to
931 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000932 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000933 }
934 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000935
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000936 return Heap::undefined_value();
937}
938
939
940static Object* Runtime_DeclareContextSlot(Arguments args) {
941 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000942 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000943
ager@chromium.org7c537e22008-10-16 08:43:32 +0000944 CONVERT_ARG_CHECKED(Context, context, 0);
945 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000946 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000947 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000948 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000949 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000950
951 // Declarations are always done in the function context.
952 context = Handle<Context>(context->fcontext());
953
954 int index;
955 PropertyAttributes attributes;
956 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000957 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000958 context->Lookup(name, flags, &index, &attributes);
959
960 if (attributes != ABSENT) {
961 // The name was declared before; check for conflicting
962 // re-declarations: This is similar to the code in parser.cc in
963 // the AstBuildingParser::Declare function.
964 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
965 // Functions are not read-only.
966 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
967 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
968 return ThrowRedeclarationError(type, name);
969 }
970
971 // Initialize it if necessary.
972 if (*initial_value != NULL) {
973 if (index >= 0) {
974 // The variable or constant context slot should always be in
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000975 // the function context or the arguments object.
976 if (holder->IsContext()) {
977 ASSERT(holder.is_identical_to(context));
978 if (((attributes & READ_ONLY) == 0) ||
979 context->get(index)->IsTheHole()) {
980 context->set(index, *initial_value);
981 }
982 } else {
983 Handle<JSObject>::cast(holder)->SetElement(index, *initial_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000984 }
985 } else {
986 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000987 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000988 SetProperty(context_ext, name, initial_value, mode);
989 }
990 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000991
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000992 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000993 // The property is not in the function context. It needs to be
994 // "declared" in the function context's extension context, or in the
995 // global context.
996 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000997 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000998 // The function context's extension context exists - use it.
999 context_ext = Handle<JSObject>(context->extension());
1000 } else {
1001 // The function context's extension context does not exists - allocate
1002 // it.
1003 context_ext = Factory::NewJSObject(Top::context_extension_function());
1004 // And store it in the extension slot.
1005 context->set_extension(*context_ext);
1006 }
1007 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001008
ager@chromium.org7c537e22008-10-16 08:43:32 +00001009 // Declare the property by setting it to the initial value if provided,
1010 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
1011 // constant declarations).
1012 ASSERT(!context_ext->HasLocalProperty(*name));
1013 Handle<Object> value(Heap::undefined_value());
1014 if (*initial_value != NULL) value = initial_value;
1015 SetProperty(context_ext, name, value, mode);
1016 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
1017 }
1018
1019 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001020}
1021
1022
1023static Object* Runtime_InitializeVarGlobal(Arguments args) {
1024 NoHandleAllocation nha;
1025
1026 // Determine if we need to assign to the variable if it already
1027 // exists (based on the number of arguments).
1028 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
1029 bool assign = args.length() == 2;
1030
1031 CONVERT_ARG_CHECKED(String, name, 0);
1032 GlobalObject* global = Top::context()->global();
1033
1034 // According to ECMA-262, section 12.2, page 62, the property must
1035 // not be deletable.
1036 PropertyAttributes attributes = DONT_DELETE;
1037
1038 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001039 // there, there is a property with this name in the prototype chain.
1040 // We follow Safari and Firefox behavior and only set the property
1041 // locally if there is an explicit initialization value that we have
1042 // to assign to the property. When adding the property we take
1043 // special precautions to always add it as a local property even in
1044 // case of callbacks in the prototype chain (this rules out using
1045 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
1046 // this.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001047 // Note that objects can have hidden prototypes, so we need to traverse
1048 // the whole chain of hidden prototypes to do a 'local' lookup.
1049 JSObject* real_holder = global;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001050 LookupResult lookup;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001051 while (true) {
1052 real_holder->LocalLookup(*name, &lookup);
1053 if (lookup.IsProperty()) {
1054 // Determine if this is a redeclaration of something read-only.
1055 if (lookup.IsReadOnly()) {
1056 // If we found readonly property on one of hidden prototypes,
1057 // just shadow it.
1058 if (real_holder != Top::context()->global()) break;
1059 return ThrowRedeclarationError("const", name);
1060 }
1061
1062 // Determine if this is a redeclaration of an intercepted read-only
1063 // property and figure out if the property exists at all.
1064 bool found = true;
1065 PropertyType type = lookup.type();
1066 if (type == INTERCEPTOR) {
1067 HandleScope handle_scope;
1068 Handle<JSObject> holder(real_holder);
1069 PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
1070 real_holder = *holder;
1071 if (intercepted == ABSENT) {
1072 // The interceptor claims the property isn't there. We need to
1073 // make sure to introduce it.
1074 found = false;
1075 } else if ((intercepted & READ_ONLY) != 0) {
1076 // The property is present, but read-only. Since we're trying to
1077 // overwrite it with a variable declaration we must throw a
1078 // re-declaration error. However if we found readonly property
1079 // on one of hidden prototypes, just shadow it.
1080 if (real_holder != Top::context()->global()) break;
1081 return ThrowRedeclarationError("const", name);
1082 }
1083 }
1084
1085 if (found && !assign) {
1086 // The global property is there and we're not assigning any value
1087 // to it. Just return.
1088 return Heap::undefined_value();
1089 }
1090
1091 // Assign the value (or undefined) to the property.
1092 Object* value = (assign) ? args[1] : Heap::undefined_value();
1093 return real_holder->SetProperty(&lookup, *name, value, attributes);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001094 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001095
1096 Object* proto = real_holder->GetPrototype();
1097 if (!proto->IsJSObject())
1098 break;
1099
1100 if (!JSObject::cast(proto)->map()->is_hidden_prototype())
1101 break;
1102
1103 real_holder = JSObject::cast(proto);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001104 }
1105
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001106 global = Top::context()->global();
1107 if (assign) {
1108 return global->IgnoreAttributesAndSetLocalProperty(*name,
1109 args[1],
1110 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001111 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001112 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001113}
1114
1115
1116static Object* Runtime_InitializeConstGlobal(Arguments args) {
1117 // All constants are declared with an initial value. The name
1118 // of the constant is the first argument and the initial value
1119 // is the second.
1120 RUNTIME_ASSERT(args.length() == 2);
1121 CONVERT_ARG_CHECKED(String, name, 0);
1122 Handle<Object> value = args.at<Object>(1);
1123
1124 // Get the current global object from top.
1125 GlobalObject* global = Top::context()->global();
1126
1127 // According to ECMA-262, section 12.2, page 62, the property must
1128 // not be deletable. Since it's a const, it must be READ_ONLY too.
1129 PropertyAttributes attributes =
1130 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
1131
1132 // Lookup the property locally in the global object. If it isn't
1133 // there, we add the property and take special precautions to always
1134 // add it as a local property even in case of callbacks in the
1135 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001136 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001137 LookupResult lookup;
1138 global->LocalLookup(*name, &lookup);
1139 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001140 return global->IgnoreAttributesAndSetLocalProperty(*name,
1141 *value,
1142 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001143 }
1144
1145 // Determine if this is a redeclaration of something not
1146 // read-only. In case the result is hidden behind an interceptor we
1147 // need to ask it for the property attributes.
1148 if (!lookup.IsReadOnly()) {
1149 if (lookup.type() != INTERCEPTOR) {
1150 return ThrowRedeclarationError("var", name);
1151 }
1152
1153 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
1154
1155 // Throw re-declaration error if the intercepted property is present
1156 // but not read-only.
1157 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
1158 return ThrowRedeclarationError("var", name);
1159 }
1160
1161 // Restore global object from context (in case of GC) and continue
1162 // with setting the value because the property is either absent or
1163 // read-only. We also have to do redo the lookup.
1164 global = Top::context()->global();
1165
1166 // BUG 1213579: Handle the case where we have to set a read-only
1167 // property through an interceptor and only do it if it's
1168 // uninitialized, e.g. the hole. Nirk...
1169 global->SetProperty(*name, *value, attributes);
1170 return *value;
1171 }
1172
1173 // Set the value, but only we're assigning the initial value to a
1174 // constant. For now, we determine this by checking if the
1175 // current value is the hole.
1176 PropertyType type = lookup.type();
1177 if (type == FIELD) {
1178 FixedArray* properties = global->properties();
1179 int index = lookup.GetFieldIndex();
1180 if (properties->get(index)->IsTheHole()) {
1181 properties->set(index, *value);
1182 }
1183 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001184 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
1185 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001186 }
1187 } else {
1188 // Ignore re-initialization of constants that have already been
1189 // assigned a function value.
1190 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
1191 }
1192
1193 // Use the set value as the result of the operation.
1194 return *value;
1195}
1196
1197
1198static Object* Runtime_InitializeConstContextSlot(Arguments args) {
1199 HandleScope scope;
1200 ASSERT(args.length() == 3);
1201
1202 Handle<Object> value(args[0]);
1203 ASSERT(!value->IsTheHole());
1204 CONVERT_ARG_CHECKED(Context, context, 1);
1205 Handle<String> name(String::cast(args[2]));
1206
1207 // Initializations are always done in the function context.
1208 context = Handle<Context>(context->fcontext());
1209
1210 int index;
1211 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001212 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001213 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001214 context->Lookup(name, flags, &index, &attributes);
1215
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001216 // In most situations, the property introduced by the const
1217 // declaration should be present in the context extension object.
1218 // However, because declaration and initialization are separate, the
1219 // property might have been deleted (if it was introduced by eval)
1220 // before we reach the initialization point.
1221 //
1222 // Example:
1223 //
1224 // function f() { eval("delete x; const x;"); }
1225 //
1226 // In that case, the initialization behaves like a normal assignment
1227 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001228 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001229 // Property was found in a context.
1230 if (holder->IsContext()) {
1231 // The holder cannot be the function context. If it is, there
1232 // should have been a const redeclaration error when declaring
1233 // the const property.
1234 ASSERT(!holder.is_identical_to(context));
1235 if ((attributes & READ_ONLY) == 0) {
1236 Handle<Context>::cast(holder)->set(index, *value);
1237 }
1238 } else {
1239 // The holder is an arguments object.
1240 ASSERT((attributes & READ_ONLY) == 0);
1241 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001242 }
1243 return *value;
1244 }
1245
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001246 // The property could not be found, we introduce it in the global
1247 // context.
1248 if (attributes == ABSENT) {
1249 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1250 SetProperty(global, name, value, NONE);
1251 return *value;
1252 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001253
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001254 // The property was present in a context extension object.
1255 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001256
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001257 if (*context_ext == context->extension()) {
1258 // This is the property that was introduced by the const
1259 // declaration. Set it if it hasn't been set before. NOTE: We
1260 // cannot use GetProperty() to get the current value as it
1261 // 'unholes' the value.
1262 LookupResult lookup;
1263 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1264 ASSERT(lookup.IsProperty()); // the property was declared
1265 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1266
1267 PropertyType type = lookup.type();
1268 if (type == FIELD) {
1269 FixedArray* properties = context_ext->properties();
1270 int index = lookup.GetFieldIndex();
1271 if (properties->get(index)->IsTheHole()) {
1272 properties->set(index, *value);
1273 }
1274 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001275 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
1276 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001277 }
1278 } else {
1279 // We should not reach here. Any real, named property should be
1280 // either a field or a dictionary slot.
1281 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001282 }
1283 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001284 // The property was found in a different context extension object.
1285 // Set it if it is not a read-only property.
1286 if ((attributes & READ_ONLY) == 0) {
1287 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1288 // Setting a property might throw an exception. Exceptions
1289 // are converted to empty handles in handle operations. We
1290 // need to convert back to exceptions here.
1291 if (set.is_null()) {
1292 ASSERT(Top::has_pending_exception());
1293 return Failure::Exception();
1294 }
1295 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001296 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001297
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001298 return *value;
1299}
1300
1301
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001302static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1303 Arguments args) {
1304 HandleScope scope;
1305 ASSERT(args.length() == 2);
1306 CONVERT_ARG_CHECKED(JSObject, object, 0);
1307 CONVERT_SMI_CHECKED(properties, args[1]);
1308 if (object->HasFastProperties()) {
1309 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1310 }
1311 return *object;
1312}
1313
1314
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001315static Object* Runtime_RegExpExec(Arguments args) {
1316 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001317 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001318 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1319 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001320 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001321 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001322 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001323 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001324 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001325 RUNTIME_ASSERT(index >= 0);
1326 RUNTIME_ASSERT(index <= subject->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001327 Counters::regexp_entry_runtime.Increment();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001328 Handle<Object> result = RegExpImpl::Exec(regexp,
1329 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001330 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001331 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001332 if (result.is_null()) return Failure::Exception();
1333 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001334}
1335
1336
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00001337static Object* Runtime_RegExpConstructResult(Arguments args) {
1338 ASSERT(args.length() == 3);
1339 CONVERT_SMI_CHECKED(elements_count, args[0]);
1340 if (elements_count > JSArray::kMaxFastElementsLength) {
1341 return Top::ThrowIllegalOperation();
1342 }
1343 Object* new_object = Heap::AllocateFixedArrayWithHoles(elements_count);
1344 if (new_object->IsFailure()) return new_object;
1345 FixedArray* elements = FixedArray::cast(new_object);
1346 new_object = Heap::AllocateRaw(JSRegExpResult::kSize,
1347 NEW_SPACE,
1348 OLD_POINTER_SPACE);
1349 if (new_object->IsFailure()) return new_object;
1350 {
1351 AssertNoAllocation no_gc;
1352 HandleScope scope;
1353 reinterpret_cast<HeapObject*>(new_object)->
1354 set_map(Top::global_context()->regexp_result_map());
1355 }
1356 JSArray* array = JSArray::cast(new_object);
1357 array->set_properties(Heap::empty_fixed_array());
1358 array->set_elements(elements);
1359 array->set_length(Smi::FromInt(elements_count));
1360 // Write in-object properties after the length of the array.
1361 array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, args[1]);
1362 array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, args[2]);
1363 return array;
1364}
1365
1366
lrn@chromium.org25156de2010-04-06 13:10:27 +00001367static Object* Runtime_RegExpInitializeObject(Arguments args) {
1368 AssertNoAllocation no_alloc;
1369 ASSERT(args.length() == 5);
1370 CONVERT_CHECKED(JSRegExp, regexp, args[0]);
1371 CONVERT_CHECKED(String, source, args[1]);
1372
1373 Object* global = args[2];
1374 if (!global->IsTrue()) global = Heap::false_value();
1375
1376 Object* ignoreCase = args[3];
1377 if (!ignoreCase->IsTrue()) ignoreCase = Heap::false_value();
1378
1379 Object* multiline = args[4];
1380 if (!multiline->IsTrue()) multiline = Heap::false_value();
1381
1382 Map* map = regexp->map();
1383 Object* constructor = map->constructor();
1384 if (constructor->IsJSFunction() &&
1385 JSFunction::cast(constructor)->initial_map() == map) {
1386 // If we still have the original map, set in-object properties directly.
1387 regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, source);
1388 // TODO(lrn): Consider skipping write barrier on booleans as well.
1389 // Both true and false should be in oldspace at all times.
1390 regexp->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, global);
1391 regexp->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex, ignoreCase);
1392 regexp->InObjectPropertyAtPut(JSRegExp::kMultilineFieldIndex, multiline);
1393 regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex,
1394 Smi::FromInt(0),
1395 SKIP_WRITE_BARRIER);
1396 return regexp;
1397 }
1398
1399 // Map has changed, so use generic, but slower, method.
1400 PropertyAttributes final =
1401 static_cast<PropertyAttributes>(READ_ONLY | DONT_ENUM | DONT_DELETE);
1402 PropertyAttributes writable =
1403 static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
1404 regexp->IgnoreAttributesAndSetLocalProperty(Heap::source_symbol(),
1405 source,
1406 final);
1407 regexp->IgnoreAttributesAndSetLocalProperty(Heap::global_symbol(),
1408 global,
1409 final);
1410 regexp->IgnoreAttributesAndSetLocalProperty(Heap::ignore_case_symbol(),
1411 ignoreCase,
1412 final);
1413 regexp->IgnoreAttributesAndSetLocalProperty(Heap::multiline_symbol(),
1414 multiline,
1415 final);
1416 regexp->IgnoreAttributesAndSetLocalProperty(Heap::last_index_symbol(),
1417 Smi::FromInt(0),
1418 writable);
1419 return regexp;
1420}
1421
1422
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001423static Object* Runtime_FinishArrayPrototypeSetup(Arguments args) {
1424 HandleScope scope;
1425 ASSERT(args.length() == 1);
1426 CONVERT_ARG_CHECKED(JSArray, prototype, 0);
1427 // This is necessary to enable fast checks for absence of elements
1428 // on Array.prototype and below.
1429 prototype->set_elements(Heap::empty_fixed_array());
1430 return Smi::FromInt(0);
1431}
1432
1433
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001434static Handle<JSFunction> InstallBuiltin(Handle<JSObject> holder,
1435 const char* name,
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001436 Builtins::Name builtin_name) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001437 Handle<String> key = Factory::LookupAsciiSymbol(name);
1438 Handle<Code> code(Builtins::builtin(builtin_name));
1439 Handle<JSFunction> optimized = Factory::NewFunction(key,
1440 JS_OBJECT_TYPE,
1441 JSObject::kHeaderSize,
1442 code,
1443 false);
1444 optimized->shared()->DontAdaptArguments();
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001445 SetProperty(holder, key, optimized, NONE);
1446 return optimized;
1447}
1448
1449
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001450static Object* Runtime_SpecialArrayFunctions(Arguments args) {
1451 HandleScope scope;
1452 ASSERT(args.length() == 1);
1453 CONVERT_ARG_CHECKED(JSObject, holder, 0);
1454
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001455 InstallBuiltin(holder, "pop", Builtins::ArrayPop);
1456 InstallBuiltin(holder, "push", Builtins::ArrayPush);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001457 InstallBuiltin(holder, "shift", Builtins::ArrayShift);
1458 InstallBuiltin(holder, "unshift", Builtins::ArrayUnshift);
1459 InstallBuiltin(holder, "slice", Builtins::ArraySlice);
1460 InstallBuiltin(holder, "splice", Builtins::ArraySplice);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001461 InstallBuiltin(holder, "concat", Builtins::ArrayConcat);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001462
1463 return *holder;
1464}
1465
1466
ager@chromium.org357bf652010-04-12 11:30:10 +00001467static Object* Runtime_GetGlobalReceiver(Arguments args) {
1468 // Returns a real global receiver, not one of builtins object.
1469 Context* global_context = Top::context()->global()->global_context();
1470 return global_context->global()->global_receiver();
1471}
1472
1473
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001474static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1475 HandleScope scope;
1476 ASSERT(args.length() == 4);
1477 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1478 int index = Smi::cast(args[1])->value();
1479 Handle<String> pattern = args.at<String>(2);
1480 Handle<String> flags = args.at<String>(3);
1481
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001482 // Get the RegExp function from the context in the literals array.
1483 // This is the RegExp function from the context in which the
1484 // function was created. We do not use the RegExp function from the
1485 // current global context because this might be the RegExp function
1486 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001487 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001488 Handle<JSFunction>(
1489 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001490 // Compute the regular expression literal.
1491 bool has_pending_exception;
1492 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001493 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1494 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001495 if (has_pending_exception) {
1496 ASSERT(Top::has_pending_exception());
1497 return Failure::Exception();
1498 }
1499 literals->set(index, *regexp);
1500 return *regexp;
1501}
1502
1503
1504static Object* Runtime_FunctionGetName(Arguments args) {
1505 NoHandleAllocation ha;
1506 ASSERT(args.length() == 1);
1507
1508 CONVERT_CHECKED(JSFunction, f, args[0]);
1509 return f->shared()->name();
1510}
1511
1512
ager@chromium.org236ad962008-09-25 09:45:57 +00001513static Object* Runtime_FunctionSetName(Arguments args) {
1514 NoHandleAllocation ha;
1515 ASSERT(args.length() == 2);
1516
1517 CONVERT_CHECKED(JSFunction, f, args[0]);
1518 CONVERT_CHECKED(String, name, args[1]);
1519 f->shared()->set_name(name);
1520 return Heap::undefined_value();
1521}
1522
1523
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001524static Object* Runtime_FunctionRemovePrototype(Arguments args) {
1525 NoHandleAllocation ha;
1526 ASSERT(args.length() == 1);
1527
1528 CONVERT_CHECKED(JSFunction, f, args[0]);
1529 Object* obj = f->RemovePrototype();
1530 if (obj->IsFailure()) return obj;
1531
1532 return Heap::undefined_value();
1533}
1534
1535
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001536static Object* Runtime_FunctionGetScript(Arguments args) {
1537 HandleScope scope;
1538 ASSERT(args.length() == 1);
1539
1540 CONVERT_CHECKED(JSFunction, fun, args[0]);
1541 Handle<Object> script = Handle<Object>(fun->shared()->script());
1542 if (!script->IsScript()) return Heap::undefined_value();
1543
1544 return *GetScriptWrapper(Handle<Script>::cast(script));
1545}
1546
1547
1548static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1549 NoHandleAllocation ha;
1550 ASSERT(args.length() == 1);
1551
1552 CONVERT_CHECKED(JSFunction, f, args[0]);
1553 return f->shared()->GetSourceCode();
1554}
1555
1556
1557static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1558 NoHandleAllocation ha;
1559 ASSERT(args.length() == 1);
1560
1561 CONVERT_CHECKED(JSFunction, fun, args[0]);
1562 int pos = fun->shared()->start_position();
1563 return Smi::FromInt(pos);
1564}
1565
1566
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001567static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1568 ASSERT(args.length() == 2);
1569
1570 CONVERT_CHECKED(JSFunction, fun, args[0]);
1571 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1572
1573 Code* code = fun->code();
1574 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1575
1576 Address pc = code->address() + offset;
1577 return Smi::FromInt(fun->code()->SourcePosition(pc));
1578}
1579
1580
1581
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001582static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1583 NoHandleAllocation ha;
1584 ASSERT(args.length() == 2);
1585
1586 CONVERT_CHECKED(JSFunction, fun, args[0]);
1587 CONVERT_CHECKED(String, name, args[1]);
1588 fun->SetInstanceClassName(name);
1589 return Heap::undefined_value();
1590}
1591
1592
1593static Object* Runtime_FunctionSetLength(Arguments args) {
1594 NoHandleAllocation ha;
1595 ASSERT(args.length() == 2);
1596
1597 CONVERT_CHECKED(JSFunction, fun, args[0]);
1598 CONVERT_CHECKED(Smi, length, args[1]);
1599 fun->shared()->set_length(length->value());
1600 return length;
1601}
1602
1603
1604static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001605 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001606 ASSERT(args.length() == 2);
1607
1608 CONVERT_CHECKED(JSFunction, fun, args[0]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001609 ASSERT(fun->should_have_prototype());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001610 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1611 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001612 return args[0]; // return TOS
1613}
1614
1615
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001616static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1617 NoHandleAllocation ha;
1618 ASSERT(args.length() == 1);
1619
1620 CONVERT_CHECKED(JSFunction, f, args[0]);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001621 return f->shared()->IsApiFunction() ? Heap::true_value()
1622 : Heap::false_value();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001623}
1624
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001625static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1626 NoHandleAllocation ha;
1627 ASSERT(args.length() == 1);
1628
1629 CONVERT_CHECKED(JSFunction, f, args[0]);
1630 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1631}
1632
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001633
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001634static Object* Runtime_SetCode(Arguments args) {
1635 HandleScope scope;
1636 ASSERT(args.length() == 2);
1637
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001638 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001639 Handle<Object> code = args.at<Object>(1);
1640
1641 Handle<Context> context(target->context());
1642
1643 if (!code->IsNull()) {
1644 RUNTIME_ASSERT(code->IsJSFunction());
1645 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001646 Handle<SharedFunctionInfo> shared(fun->shared());
1647 SetExpectedNofProperties(target, shared->expected_nof_properties());
1648
1649 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001650 return Failure::Exception();
1651 }
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00001652 // Set the code, scope info, formal parameter count,
1653 // and the length of the target function.
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001654 target->shared()->set_code(shared->code());
1655 target->set_code(shared->code());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00001656 target->shared()->set_scope_info(shared->scope_info());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001657 target->shared()->set_length(shared->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001658 target->shared()->set_formal_parameter_count(
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001659 shared->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001660 // Set the source code of the target function to undefined.
1661 // SetCode is only used for built-in constructors like String,
1662 // Array, and Object, and some web code
1663 // doesn't like seeing source code for constructors.
1664 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001665 // Clear the optimization hints related to the compiled code as these are no
1666 // longer valid when the code is overwritten.
1667 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001668 context = Handle<Context>(fun->context());
1669
1670 // Make sure we get a fresh copy of the literal vector to avoid
1671 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001672 int number_of_literals = fun->NumberOfLiterals();
1673 Handle<FixedArray> literals =
1674 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001675 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001676 // Insert the object, regexp and array functions in the literals
1677 // array prefix. These are the functions that will be used when
1678 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001679 literals->set(JSFunction::kLiteralGlobalContextIndex,
1680 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001681 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001682 // It's okay to skip the write barrier here because the literals
1683 // are guaranteed to be in old space.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001684 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001685 }
1686
1687 target->set_context(*context);
1688 return *target;
1689}
1690
1691
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001692static Object* CharFromCode(Object* char_code) {
1693 uint32_t code;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001694 if (char_code->ToArrayIndex(&code)) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001695 if (code <= 0xffff) {
1696 return Heap::LookupSingleCharacterStringFromCode(code);
1697 }
1698 }
1699 return Heap::empty_string();
1700}
1701
1702
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001703static Object* Runtime_StringCharCodeAt(Arguments args) {
1704 NoHandleAllocation ha;
1705 ASSERT(args.length() == 2);
1706
1707 CONVERT_CHECKED(String, subject, args[0]);
1708 Object* index = args[1];
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001709 RUNTIME_ASSERT(index->IsNumber());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001710
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001711 uint32_t i = 0;
1712 if (index->IsSmi()) {
1713 int value = Smi::cast(index)->value();
1714 if (value < 0) return Heap::nan_value();
1715 i = value;
1716 } else {
1717 ASSERT(index->IsHeapNumber());
1718 double value = HeapNumber::cast(index)->value();
1719 i = static_cast<uint32_t>(DoubleToInteger(value));
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001720 }
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001721
1722 // Flatten the string. If someone wants to get a char at an index
1723 // in a cons string, it is likely that more indices will be
1724 // accessed.
1725 Object* flat = subject->TryFlatten();
1726 if (flat->IsFailure()) return flat;
1727 subject = String::cast(flat);
1728
1729 if (i >= static_cast<uint32_t>(subject->length())) {
1730 return Heap::nan_value();
1731 }
1732
1733 return Smi::FromInt(subject->Get(i));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001734}
1735
1736
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001737static Object* Runtime_CharFromCode(Arguments args) {
1738 NoHandleAllocation ha;
1739 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001740 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001741}
1742
lrn@chromium.org25156de2010-04-06 13:10:27 +00001743
1744class FixedArrayBuilder {
1745 public:
1746 explicit FixedArrayBuilder(int initial_capacity)
1747 : array_(Factory::NewFixedArrayWithHoles(initial_capacity)),
1748 length_(0) {
1749 // Require a non-zero initial size. Ensures that doubling the size to
1750 // extend the array will work.
1751 ASSERT(initial_capacity > 0);
1752 }
1753
1754 explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
1755 : array_(backing_store),
1756 length_(0) {
1757 // Require a non-zero initial size. Ensures that doubling the size to
1758 // extend the array will work.
1759 ASSERT(backing_store->length() > 0);
1760 }
1761
1762 bool HasCapacity(int elements) {
1763 int length = array_->length();
1764 int required_length = length_ + elements;
1765 return (length >= required_length);
1766 }
1767
1768 void EnsureCapacity(int elements) {
1769 int length = array_->length();
1770 int required_length = length_ + elements;
1771 if (length < required_length) {
1772 int new_length = length;
1773 do {
1774 new_length *= 2;
1775 } while (new_length < required_length);
1776 Handle<FixedArray> extended_array =
1777 Factory::NewFixedArrayWithHoles(new_length);
1778 array_->CopyTo(0, *extended_array, 0, length_);
1779 array_ = extended_array;
1780 }
1781 }
1782
1783 void Add(Object* value) {
1784 ASSERT(length_ < capacity());
1785 array_->set(length_, value);
1786 length_++;
1787 }
1788
1789 void Add(Smi* value) {
1790 ASSERT(length_ < capacity());
1791 array_->set(length_, value);
1792 length_++;
1793 }
1794
1795 Handle<FixedArray> array() {
1796 return array_;
1797 }
1798
1799 int length() {
1800 return length_;
1801 }
1802
1803 int capacity() {
1804 return array_->length();
1805 }
1806
1807 Handle<JSArray> ToJSArray() {
1808 Handle<JSArray> result_array = Factory::NewJSArrayWithElements(array_);
1809 result_array->set_length(Smi::FromInt(length_));
1810 return result_array;
1811 }
1812
1813 Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
1814 target_array->set_elements(*array_);
1815 target_array->set_length(Smi::FromInt(length_));
1816 return target_array;
1817 }
1818
1819 private:
1820 Handle<FixedArray> array_;
1821 int length_;
1822};
1823
1824
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001825// Forward declarations.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001826const int kStringBuilderConcatHelperLengthBits = 11;
1827const int kStringBuilderConcatHelperPositionBits = 19;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001828
1829template <typename schar>
1830static inline void StringBuilderConcatHelper(String*,
1831 schar*,
1832 FixedArray*,
1833 int);
1834
lrn@chromium.org25156de2010-04-06 13:10:27 +00001835typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
1836 StringBuilderSubstringLength;
1837typedef BitField<int,
1838 kStringBuilderConcatHelperLengthBits,
1839 kStringBuilderConcatHelperPositionBits>
1840 StringBuilderSubstringPosition;
1841
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001842
1843class ReplacementStringBuilder {
1844 public:
1845 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
lrn@chromium.org25156de2010-04-06 13:10:27 +00001846 : array_builder_(estimated_part_count),
1847 subject_(subject),
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001848 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001849 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001850 // Require a non-zero initial size. Ensures that doubling the size to
1851 // extend the array will work.
1852 ASSERT(estimated_part_count > 0);
1853 }
1854
lrn@chromium.org25156de2010-04-06 13:10:27 +00001855 static inline void AddSubjectSlice(FixedArrayBuilder* builder,
1856 int from,
1857 int to) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001858 ASSERT(from >= 0);
1859 int length = to - from;
1860 ASSERT(length > 0);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001861 if (StringBuilderSubstringLength::is_valid(length) &&
1862 StringBuilderSubstringPosition::is_valid(from)) {
1863 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1864 StringBuilderSubstringPosition::encode(from);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001865 builder->Add(Smi::FromInt(encoded_slice));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001866 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001867 // Otherwise encode as two smis.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001868 builder->Add(Smi::FromInt(-length));
1869 builder->Add(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001870 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00001871 }
1872
1873
1874 void EnsureCapacity(int elements) {
1875 array_builder_.EnsureCapacity(elements);
1876 }
1877
1878
1879 void AddSubjectSlice(int from, int to) {
1880 AddSubjectSlice(&array_builder_, from, to);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001881 IncrementCharacterCount(to - from);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001882 }
1883
1884
1885 void AddString(Handle<String> string) {
1886 int length = string->length();
1887 ASSERT(length > 0);
1888 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001889 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001890 is_ascii_ = false;
1891 }
1892 IncrementCharacterCount(length);
1893 }
1894
1895
1896 Handle<String> ToString() {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001897 if (array_builder_.length() == 0) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001898 return Factory::empty_string();
1899 }
1900
1901 Handle<String> joined_string;
1902 if (is_ascii_) {
1903 joined_string = NewRawAsciiString(character_count_);
1904 AssertNoAllocation no_alloc;
1905 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1906 char* char_buffer = seq->GetChars();
1907 StringBuilderConcatHelper(*subject_,
1908 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001909 *array_builder_.array(),
1910 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001911 } else {
1912 // Non-ASCII.
1913 joined_string = NewRawTwoByteString(character_count_);
1914 AssertNoAllocation no_alloc;
1915 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1916 uc16* char_buffer = seq->GetChars();
1917 StringBuilderConcatHelper(*subject_,
1918 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001919 *array_builder_.array(),
1920 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001921 }
1922 return joined_string;
1923 }
1924
1925
1926 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001927 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001928 V8::FatalProcessOutOfMemory("String.replace result too large.");
1929 }
1930 character_count_ += by;
1931 }
1932
lrn@chromium.org25156de2010-04-06 13:10:27 +00001933 Handle<JSArray> GetParts() {
1934 Handle<JSArray> result =
1935 Factory::NewJSArrayWithElements(array_builder_.array());
1936 result->set_length(Smi::FromInt(array_builder_.length()));
1937 return result;
1938 }
kmillikin@chromium.orgd9825192010-03-30 08:36:16 +00001939
lrn@chromium.org25156de2010-04-06 13:10:27 +00001940 private:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001941 Handle<String> NewRawAsciiString(int size) {
1942 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1943 }
1944
1945
1946 Handle<String> NewRawTwoByteString(int size) {
1947 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1948 }
1949
1950
1951 void AddElement(Object* element) {
1952 ASSERT(element->IsSmi() || element->IsString());
lrn@chromium.org25156de2010-04-06 13:10:27 +00001953 ASSERT(array_builder_.capacity() > array_builder_.length());
1954 array_builder_.Add(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001955 }
1956
lrn@chromium.org25156de2010-04-06 13:10:27 +00001957 FixedArrayBuilder array_builder_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001958 Handle<String> subject_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001959 int character_count_;
1960 bool is_ascii_;
1961};
1962
1963
1964class CompiledReplacement {
1965 public:
1966 CompiledReplacement()
1967 : parts_(1), replacement_substrings_(0) {}
1968
1969 void Compile(Handle<String> replacement,
1970 int capture_count,
1971 int subject_length);
1972
1973 void Apply(ReplacementStringBuilder* builder,
1974 int match_from,
1975 int match_to,
1976 Handle<JSArray> last_match_info);
1977
1978 // Number of distinct parts of the replacement pattern.
1979 int parts() {
1980 return parts_.length();
1981 }
1982 private:
1983 enum PartType {
1984 SUBJECT_PREFIX = 1,
1985 SUBJECT_SUFFIX,
1986 SUBJECT_CAPTURE,
1987 REPLACEMENT_SUBSTRING,
1988 REPLACEMENT_STRING,
1989
1990 NUMBER_OF_PART_TYPES
1991 };
1992
1993 struct ReplacementPart {
1994 static inline ReplacementPart SubjectMatch() {
1995 return ReplacementPart(SUBJECT_CAPTURE, 0);
1996 }
1997 static inline ReplacementPart SubjectCapture(int capture_index) {
1998 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1999 }
2000 static inline ReplacementPart SubjectPrefix() {
2001 return ReplacementPart(SUBJECT_PREFIX, 0);
2002 }
2003 static inline ReplacementPart SubjectSuffix(int subject_length) {
2004 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
2005 }
2006 static inline ReplacementPart ReplacementString() {
2007 return ReplacementPart(REPLACEMENT_STRING, 0);
2008 }
2009 static inline ReplacementPart ReplacementSubString(int from, int to) {
2010 ASSERT(from >= 0);
2011 ASSERT(to > from);
2012 return ReplacementPart(-from, to);
2013 }
2014
2015 // If tag <= 0 then it is the negation of a start index of a substring of
2016 // the replacement pattern, otherwise it's a value from PartType.
2017 ReplacementPart(int tag, int data)
2018 : tag(tag), data(data) {
2019 // Must be non-positive or a PartType value.
2020 ASSERT(tag < NUMBER_OF_PART_TYPES);
2021 }
2022 // Either a value of PartType or a non-positive number that is
2023 // the negation of an index into the replacement string.
2024 int tag;
2025 // The data value's interpretation depends on the value of tag:
2026 // tag == SUBJECT_PREFIX ||
2027 // tag == SUBJECT_SUFFIX: data is unused.
2028 // tag == SUBJECT_CAPTURE: data is the number of the capture.
2029 // tag == REPLACEMENT_SUBSTRING ||
2030 // tag == REPLACEMENT_STRING: data is index into array of substrings
2031 // of the replacement string.
2032 // tag <= 0: Temporary representation of the substring of the replacement
2033 // string ranging over -tag .. data.
2034 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
2035 // substring objects.
2036 int data;
2037 };
2038
2039 template<typename Char>
2040 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
2041 Vector<Char> characters,
2042 int capture_count,
2043 int subject_length) {
2044 int length = characters.length();
2045 int last = 0;
2046 for (int i = 0; i < length; i++) {
2047 Char c = characters[i];
2048 if (c == '$') {
2049 int next_index = i + 1;
2050 if (next_index == length) { // No next character!
2051 break;
2052 }
2053 Char c2 = characters[next_index];
2054 switch (c2) {
2055 case '$':
2056 if (i > last) {
2057 // There is a substring before. Include the first "$".
2058 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
2059 last = next_index + 1; // Continue after the second "$".
2060 } else {
2061 // Let the next substring start with the second "$".
2062 last = next_index;
2063 }
2064 i = next_index;
2065 break;
2066 case '`':
2067 if (i > last) {
2068 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2069 }
2070 parts->Add(ReplacementPart::SubjectPrefix());
2071 i = next_index;
2072 last = i + 1;
2073 break;
2074 case '\'':
2075 if (i > last) {
2076 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2077 }
2078 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
2079 i = next_index;
2080 last = i + 1;
2081 break;
2082 case '&':
2083 if (i > last) {
2084 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2085 }
2086 parts->Add(ReplacementPart::SubjectMatch());
2087 i = next_index;
2088 last = i + 1;
2089 break;
2090 case '0':
2091 case '1':
2092 case '2':
2093 case '3':
2094 case '4':
2095 case '5':
2096 case '6':
2097 case '7':
2098 case '8':
2099 case '9': {
2100 int capture_ref = c2 - '0';
2101 if (capture_ref > capture_count) {
2102 i = next_index;
2103 continue;
2104 }
2105 int second_digit_index = next_index + 1;
2106 if (second_digit_index < length) {
2107 // Peek ahead to see if we have two digits.
2108 Char c3 = characters[second_digit_index];
2109 if ('0' <= c3 && c3 <= '9') { // Double digits.
2110 int double_digit_ref = capture_ref * 10 + c3 - '0';
2111 if (double_digit_ref <= capture_count) {
2112 next_index = second_digit_index;
2113 capture_ref = double_digit_ref;
2114 }
2115 }
2116 }
2117 if (capture_ref > 0) {
2118 if (i > last) {
2119 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2120 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002121 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002122 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
2123 last = next_index + 1;
2124 }
2125 i = next_index;
2126 break;
2127 }
2128 default:
2129 i = next_index;
2130 break;
2131 }
2132 }
2133 }
2134 if (length > last) {
2135 if (last == 0) {
2136 parts->Add(ReplacementPart::ReplacementString());
2137 } else {
2138 parts->Add(ReplacementPart::ReplacementSubString(last, length));
2139 }
2140 }
2141 }
2142
2143 ZoneList<ReplacementPart> parts_;
2144 ZoneList<Handle<String> > replacement_substrings_;
2145};
2146
2147
2148void CompiledReplacement::Compile(Handle<String> replacement,
2149 int capture_count,
2150 int subject_length) {
2151 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00002152 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002153 AssertNoAllocation no_alloc;
2154 ParseReplacementPattern(&parts_,
2155 replacement->ToAsciiVector(),
2156 capture_count,
2157 subject_length);
2158 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00002159 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002160 AssertNoAllocation no_alloc;
2161
2162 ParseReplacementPattern(&parts_,
2163 replacement->ToUC16Vector(),
2164 capture_count,
2165 subject_length);
2166 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002167 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002168 int substring_index = 0;
2169 for (int i = 0, n = parts_.length(); i < n; i++) {
2170 int tag = parts_[i].tag;
2171 if (tag <= 0) { // A replacement string slice.
2172 int from = -tag;
2173 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002174 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002175 parts_[i].tag = REPLACEMENT_SUBSTRING;
2176 parts_[i].data = substring_index;
2177 substring_index++;
2178 } else if (tag == REPLACEMENT_STRING) {
2179 replacement_substrings_.Add(replacement);
2180 parts_[i].data = substring_index;
2181 substring_index++;
2182 }
2183 }
2184}
2185
2186
2187void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
2188 int match_from,
2189 int match_to,
2190 Handle<JSArray> last_match_info) {
2191 for (int i = 0, n = parts_.length(); i < n; i++) {
2192 ReplacementPart part = parts_[i];
2193 switch (part.tag) {
2194 case SUBJECT_PREFIX:
2195 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
2196 break;
2197 case SUBJECT_SUFFIX: {
2198 int subject_length = part.data;
2199 if (match_to < subject_length) {
2200 builder->AddSubjectSlice(match_to, subject_length);
2201 }
2202 break;
2203 }
2204 case SUBJECT_CAPTURE: {
2205 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002206 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002207 int from = RegExpImpl::GetCapture(match_info, capture * 2);
2208 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
2209 if (from >= 0 && to > from) {
2210 builder->AddSubjectSlice(from, to);
2211 }
2212 break;
2213 }
2214 case REPLACEMENT_SUBSTRING:
2215 case REPLACEMENT_STRING:
2216 builder->AddString(replacement_substrings_[part.data]);
2217 break;
2218 default:
2219 UNREACHABLE();
2220 }
2221 }
2222}
2223
2224
2225
2226static Object* StringReplaceRegExpWithString(String* subject,
2227 JSRegExp* regexp,
2228 String* replacement,
2229 JSArray* last_match_info) {
2230 ASSERT(subject->IsFlat());
2231 ASSERT(replacement->IsFlat());
2232
2233 HandleScope handles;
2234
2235 int length = subject->length();
2236 Handle<String> subject_handle(subject);
2237 Handle<JSRegExp> regexp_handle(regexp);
2238 Handle<String> replacement_handle(replacement);
2239 Handle<JSArray> last_match_info_handle(last_match_info);
2240 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2241 subject_handle,
2242 0,
2243 last_match_info_handle);
2244 if (match.is_null()) {
2245 return Failure::Exception();
2246 }
2247 if (match->IsNull()) {
2248 return *subject_handle;
2249 }
2250
2251 int capture_count = regexp_handle->CaptureCount();
2252
2253 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002254 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002255 CompiledReplacement compiled_replacement;
2256 compiled_replacement.Compile(replacement_handle,
2257 capture_count,
2258 length);
2259
2260 bool is_global = regexp_handle->GetFlags().is_global();
2261
2262 // Guessing the number of parts that the final result string is built
2263 // from. Global regexps can match any number of times, so we guess
2264 // conservatively.
2265 int expected_parts =
2266 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
2267 ReplacementStringBuilder builder(subject_handle, expected_parts);
2268
2269 // Index of end of last match.
2270 int prev = 0;
2271
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002272 // Number of parts added by compiled replacement plus preceeding
2273 // string and possibly suffix after last match. It is possible for
2274 // all components to use two elements when encoded as two smis.
2275 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002276 bool matched = true;
2277 do {
2278 ASSERT(last_match_info_handle->HasFastElements());
2279 // Increase the capacity of the builder before entering local handle-scope,
2280 // so its internal buffer can safely allocate a new handle if it grows.
2281 builder.EnsureCapacity(parts_added_per_loop);
2282
2283 HandleScope loop_scope;
2284 int start, end;
2285 {
2286 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002287 FixedArray* match_info_array =
2288 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002289
2290 ASSERT_EQ(capture_count * 2 + 2,
2291 RegExpImpl::GetLastCaptureCount(match_info_array));
2292 start = RegExpImpl::GetCapture(match_info_array, 0);
2293 end = RegExpImpl::GetCapture(match_info_array, 1);
2294 }
2295
2296 if (prev < start) {
2297 builder.AddSubjectSlice(prev, start);
2298 }
2299 compiled_replacement.Apply(&builder,
2300 start,
2301 end,
2302 last_match_info_handle);
2303 prev = end;
2304
2305 // Only continue checking for global regexps.
2306 if (!is_global) break;
2307
2308 // Continue from where the match ended, unless it was an empty match.
2309 int next = end;
2310 if (start == end) {
2311 next = end + 1;
2312 if (next > length) break;
2313 }
2314
2315 match = RegExpImpl::Exec(regexp_handle,
2316 subject_handle,
2317 next,
2318 last_match_info_handle);
2319 if (match.is_null()) {
2320 return Failure::Exception();
2321 }
2322 matched = !match->IsNull();
2323 } while (matched);
2324
2325 if (prev < length) {
2326 builder.AddSubjectSlice(prev, length);
2327 }
2328
2329 return *(builder.ToString());
2330}
2331
2332
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00002333template <typename ResultSeqString>
2334static Object* StringReplaceRegExpWithEmptyString(String* subject,
2335 JSRegExp* regexp,
2336 JSArray* last_match_info) {
2337 ASSERT(subject->IsFlat());
2338
2339 HandleScope handles;
2340
2341 Handle<String> subject_handle(subject);
2342 Handle<JSRegExp> regexp_handle(regexp);
2343 Handle<JSArray> last_match_info_handle(last_match_info);
2344 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2345 subject_handle,
2346 0,
2347 last_match_info_handle);
2348 if (match.is_null()) return Failure::Exception();
2349 if (match->IsNull()) return *subject_handle;
2350
2351 ASSERT(last_match_info_handle->HasFastElements());
2352
2353 HandleScope loop_scope;
2354 int start, end;
2355 {
2356 AssertNoAllocation match_info_array_is_not_in_a_handle;
2357 FixedArray* match_info_array =
2358 FixedArray::cast(last_match_info_handle->elements());
2359
2360 start = RegExpImpl::GetCapture(match_info_array, 0);
2361 end = RegExpImpl::GetCapture(match_info_array, 1);
2362 }
2363
2364 int length = subject->length();
2365 int new_length = length - (end - start);
2366 if (new_length == 0) {
2367 return Heap::empty_string();
2368 }
2369 Handle<ResultSeqString> answer;
2370 if (ResultSeqString::kHasAsciiEncoding) {
2371 answer =
2372 Handle<ResultSeqString>::cast(Factory::NewRawAsciiString(new_length));
2373 } else {
2374 answer =
2375 Handle<ResultSeqString>::cast(Factory::NewRawTwoByteString(new_length));
2376 }
2377
2378 // If the regexp isn't global, only match once.
2379 if (!regexp_handle->GetFlags().is_global()) {
2380 if (start > 0) {
2381 String::WriteToFlat(*subject_handle,
2382 answer->GetChars(),
2383 0,
2384 start);
2385 }
2386 if (end < length) {
2387 String::WriteToFlat(*subject_handle,
2388 answer->GetChars() + start,
2389 end,
2390 length);
2391 }
2392 return *answer;
2393 }
2394
2395 int prev = 0; // Index of end of last match.
2396 int next = 0; // Start of next search (prev unless last match was empty).
2397 int position = 0;
2398
2399 do {
2400 if (prev < start) {
2401 // Add substring subject[prev;start] to answer string.
2402 String::WriteToFlat(*subject_handle,
2403 answer->GetChars() + position,
2404 prev,
2405 start);
2406 position += start - prev;
2407 }
2408 prev = end;
2409 next = end;
2410 // Continue from where the match ended, unless it was an empty match.
2411 if (start == end) {
2412 next++;
2413 if (next > length) break;
2414 }
2415 match = RegExpImpl::Exec(regexp_handle,
2416 subject_handle,
2417 next,
2418 last_match_info_handle);
2419 if (match.is_null()) return Failure::Exception();
2420 if (match->IsNull()) break;
2421
2422 ASSERT(last_match_info_handle->HasFastElements());
2423 HandleScope loop_scope;
2424 {
2425 AssertNoAllocation match_info_array_is_not_in_a_handle;
2426 FixedArray* match_info_array =
2427 FixedArray::cast(last_match_info_handle->elements());
2428 start = RegExpImpl::GetCapture(match_info_array, 0);
2429 end = RegExpImpl::GetCapture(match_info_array, 1);
2430 }
2431 } while (true);
2432
2433 if (prev < length) {
2434 // Add substring subject[prev;length] to answer string.
2435 String::WriteToFlat(*subject_handle,
2436 answer->GetChars() + position,
2437 prev,
2438 length);
2439 position += length - prev;
2440 }
2441
2442 if (position == 0) {
2443 return Heap::empty_string();
2444 }
2445
2446 // Shorten string and fill
2447 int string_size = ResultSeqString::SizeFor(position);
2448 int allocated_string_size = ResultSeqString::SizeFor(new_length);
2449 int delta = allocated_string_size - string_size;
2450
2451 answer->set_length(position);
2452 if (delta == 0) return *answer;
2453
2454 Address end_of_string = answer->address() + string_size;
2455 Heap::CreateFillerObjectAt(end_of_string, delta);
2456
2457 return *answer;
2458}
2459
2460
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002461static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2462 ASSERT(args.length() == 4);
2463
2464 CONVERT_CHECKED(String, subject, args[0]);
2465 if (!subject->IsFlat()) {
2466 Object* flat_subject = subject->TryFlatten();
2467 if (flat_subject->IsFailure()) {
2468 return flat_subject;
2469 }
2470 subject = String::cast(flat_subject);
2471 }
2472
2473 CONVERT_CHECKED(String, replacement, args[2]);
2474 if (!replacement->IsFlat()) {
2475 Object* flat_replacement = replacement->TryFlatten();
2476 if (flat_replacement->IsFailure()) {
2477 return flat_replacement;
2478 }
2479 replacement = String::cast(flat_replacement);
2480 }
2481
2482 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2483 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2484
2485 ASSERT(last_match_info->HasFastElements());
2486
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00002487 if (replacement->length() == 0) {
2488 if (subject->HasOnlyAsciiChars()) {
2489 return StringReplaceRegExpWithEmptyString<SeqAsciiString>(
2490 subject, regexp, last_match_info);
2491 } else {
2492 return StringReplaceRegExpWithEmptyString<SeqTwoByteString>(
2493 subject, regexp, last_match_info);
2494 }
2495 }
2496
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002497 return StringReplaceRegExpWithString(subject,
2498 regexp,
2499 replacement,
2500 last_match_info);
2501}
2502
2503
ager@chromium.org7c537e22008-10-16 08:43:32 +00002504// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2505// limit, we can fix the size of tables.
2506static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002507// Reduce alphabet to this size.
2508static const int kBMAlphabetSize = 0x100;
2509// For patterns below this length, the skip length of Boyer-Moore is too short
2510// to compensate for the algorithmic overhead compared to simple brute force.
2511static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002512
ager@chromium.org7c537e22008-10-16 08:43:32 +00002513// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2514// shift. Only allows the last kBMMaxShift characters of the needle
2515// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002516class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002517 public:
2518 BMGoodSuffixBuffers() {}
2519 inline void init(int needle_length) {
2520 ASSERT(needle_length > 1);
2521 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2522 int len = needle_length - start;
2523 biased_suffixes_ = suffixes_ - start;
2524 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2525 for (int i = 0; i <= len; i++) {
2526 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002527 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002528 }
2529 inline int& suffix(int index) {
2530 ASSERT(biased_suffixes_ + index >= suffixes_);
2531 return biased_suffixes_[index];
2532 }
2533 inline int& shift(int index) {
2534 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2535 return biased_good_suffix_shift_[index];
2536 }
2537 private:
2538 int suffixes_[kBMMaxShift + 1];
2539 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002540 int* biased_suffixes_;
2541 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002542 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2543};
2544
2545// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002546static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002547static BMGoodSuffixBuffers bmgs_buffers;
2548
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002549// State of the string match tables.
2550// SIMPLE: No usable content in the buffers.
2551// BOYER_MOORE_HORSPOOL: The bad_char_occurences table has been populated.
2552// BOYER_MOORE: The bmgs_buffers tables have also been populated.
2553// Whenever starting with a new needle, one should call InitializeStringSearch
2554// to determine which search strategy to use, and in the case of a long-needle
2555// strategy, the call also initializes the algorithm to SIMPLE.
2556enum StringSearchAlgorithm { SIMPLE_SEARCH, BOYER_MOORE_HORSPOOL, BOYER_MOORE };
2557static StringSearchAlgorithm algorithm;
2558
2559
ager@chromium.org7c537e22008-10-16 08:43:32 +00002560// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002561template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002562static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern) {
2563 // Only preprocess at most kBMMaxShift last characters of pattern.
2564 int start = pattern.length() < kBMMaxShift ? 0
2565 : pattern.length() - kBMMaxShift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002566 // Run forwards to populate bad_char_table, so that *last* instance
2567 // of character equivalence class is the one registered.
2568 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002569 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2570 : kBMAlphabetSize;
2571 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002572 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002573 } else {
2574 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002575 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002576 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002577 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002578 for (int i = start; i < pattern.length() - 1; i++) {
2579 pchar c = pattern[i];
2580 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002581 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002582 }
2583}
2584
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002585
ager@chromium.org7c537e22008-10-16 08:43:32 +00002586template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002587static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002588 int m = pattern.length();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002589 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002590 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002591 // Compute Good Suffix tables.
2592 bmgs_buffers.init(m);
2593
2594 bmgs_buffers.shift(m-1) = 1;
2595 bmgs_buffers.suffix(m) = m + 1;
2596 pchar last_char = pattern[m - 1];
2597 int suffix = m + 1;
2598 for (int i = m; i > start;) {
2599 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2600 if (bmgs_buffers.shift(suffix) == len) {
2601 bmgs_buffers.shift(suffix) = suffix - i;
2602 }
2603 suffix = bmgs_buffers.suffix(suffix);
2604 }
2605 i--;
2606 suffix--;
2607 bmgs_buffers.suffix(i) = suffix;
2608 if (suffix == m) {
2609 // No suffix to extend, so we check against last_char only.
2610 while (i > start && pattern[i - 1] != last_char) {
2611 if (bmgs_buffers.shift(m) == len) {
2612 bmgs_buffers.shift(m) = m - i;
2613 }
2614 i--;
2615 bmgs_buffers.suffix(i) = m;
2616 }
2617 if (i > start) {
2618 i--;
2619 suffix--;
2620 bmgs_buffers.suffix(i) = suffix;
2621 }
2622 }
2623 }
2624 if (suffix < m) {
2625 for (int i = start; i <= m; i++) {
2626 if (bmgs_buffers.shift(i) == len) {
2627 bmgs_buffers.shift(i) = suffix - start;
2628 }
2629 if (i == suffix) {
2630 suffix = bmgs_buffers.suffix(suffix);
2631 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002632 }
2633 }
2634}
2635
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002636
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002637template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002638static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002639 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002640 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002641 }
2642 if (sizeof(pchar) == 1) {
2643 if (char_code > String::kMaxAsciiCharCode) {
2644 return -1;
2645 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002646 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002647 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002648 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002649}
2650
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002651
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002652// Restricted simplified Boyer-Moore string matching.
2653// Uses only the bad-shift table of Boyer-Moore and only uses it
2654// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002655template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002656static int BoyerMooreHorspool(Vector<const schar> subject,
2657 Vector<const pchar> pattern,
2658 int start_index,
2659 bool* complete) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002660 ASSERT(algorithm <= BOYER_MOORE_HORSPOOL);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002661 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002662 int m = pattern.length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002663
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002664 int badness = -m;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002665
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002666 // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002667 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002668 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002669 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002670 // Perform search
2671 for (idx = start_index; idx <= n - m;) {
2672 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002673 int c;
2674 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002675 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002676 int shift = j - bc_occ;
2677 idx += shift;
2678 badness += 1 - shift; // at most zero, so badness cannot increase.
2679 if (idx > n - m) {
2680 *complete = true;
2681 return -1;
2682 }
2683 }
2684 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002685 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002686 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002687 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002688 return idx;
2689 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002690 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002691 // Badness increases by the number of characters we have
2692 // checked, and decreases by the number of characters we
2693 // can skip by shifting. It's a measure of how we are doing
2694 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002695 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002696 if (badness > 0) {
2697 *complete = false;
2698 return idx;
2699 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002700 }
2701 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002702 *complete = true;
2703 return -1;
2704}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002705
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002706
2707template <typename schar, typename pchar>
2708static int BoyerMooreIndexOf(Vector<const schar> subject,
2709 Vector<const pchar> pattern,
2710 int idx) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002711 ASSERT(algorithm <= BOYER_MOORE);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002712 int n = subject.length();
2713 int m = pattern.length();
2714 // Only preprocess at most kBMMaxShift last characters of pattern.
2715 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2716
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002717 pchar last_char = pattern[m - 1];
2718 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002719 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002720 int j = m - 1;
2721 schar c;
2722 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002723 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002724 idx += shift;
2725 if (idx > n - m) {
2726 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002727 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002728 }
2729 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2730 if (j < 0) {
2731 return idx;
2732 } else if (j < start) {
2733 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002734 // Fall back on BMH shift.
2735 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002736 } else {
2737 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002738 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002739 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002740 if (gs_shift > shift) {
2741 shift = gs_shift;
2742 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002743 idx += shift;
2744 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002745 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002746
2747 return -1;
2748}
2749
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002750
2751template <typename schar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002752static inline int SingleCharIndexOf(Vector<const schar> string,
2753 schar pattern_char,
2754 int start_index) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002755 if (sizeof(schar) == 1) {
2756 const schar* pos = reinterpret_cast<const schar*>(
2757 memchr(string.start() + start_index,
2758 pattern_char,
2759 string.length() - start_index));
2760 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002761 return static_cast<int>(pos - string.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002762 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002763 for (int i = start_index, n = string.length(); i < n; i++) {
2764 if (pattern_char == string[i]) {
2765 return i;
2766 }
2767 }
2768 return -1;
2769}
2770
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002771
2772template <typename schar>
2773static int SingleCharLastIndexOf(Vector<const schar> string,
2774 schar pattern_char,
2775 int start_index) {
2776 for (int i = start_index; i >= 0; i--) {
2777 if (pattern_char == string[i]) {
2778 return i;
2779 }
2780 }
2781 return -1;
2782}
2783
2784
ager@chromium.org7c537e22008-10-16 08:43:32 +00002785// Trivial string search for shorter strings.
2786// On return, if "complete" is set to true, the return value is the
2787// final result of searching for the patter in the subject.
2788// If "complete" is set to false, the return value is the index where
2789// further checking should start, i.e., it's guaranteed that the pattern
2790// does not occur at a position prior to the returned index.
2791template <typename pchar, typename schar>
2792static int SimpleIndexOf(Vector<const schar> subject,
2793 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002794 int idx,
2795 bool* complete) {
2796 // Badness is a count of how much work we have done. When we have
2797 // done enough work we decide it's probably worth switching to a better
2798 // algorithm.
2799 int badness = -10 - (pattern.length() << 2);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002800
ager@chromium.org7c537e22008-10-16 08:43:32 +00002801 // We know our pattern is at least 2 characters, we cache the first so
2802 // the common case of the first character not matching is faster.
2803 pchar pattern_first_char = pattern[0];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002804 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2805 badness++;
2806 if (badness > 0) {
2807 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002808 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002809 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002810 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2811 const schar* pos = reinterpret_cast<const schar*>(
2812 memchr(subject.start() + i,
2813 pattern_first_char,
2814 n - i + 1));
2815 if (pos == NULL) {
2816 *complete = true;
2817 return -1;
2818 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002819 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002820 } else {
2821 if (subject[i] != pattern_first_char) continue;
2822 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002823 int j = 1;
2824 do {
2825 if (pattern[j] != subject[i+j]) {
2826 break;
2827 }
2828 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002829 } while (j < pattern.length());
2830 if (j == pattern.length()) {
2831 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002832 return i;
2833 }
2834 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002835 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002836 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002837 return -1;
2838}
2839
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002840// Simple indexOf that never bails out. For short patterns only.
2841template <typename pchar, typename schar>
2842static int SimpleIndexOf(Vector<const schar> subject,
2843 Vector<const pchar> pattern,
2844 int idx) {
2845 pchar pattern_first_char = pattern[0];
2846 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002847 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2848 const schar* pos = reinterpret_cast<const schar*>(
2849 memchr(subject.start() + i,
2850 pattern_first_char,
2851 n - i + 1));
2852 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002853 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002854 } else {
2855 if (subject[i] != pattern_first_char) continue;
2856 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002857 int j = 1;
2858 do {
2859 if (pattern[j] != subject[i+j]) {
2860 break;
2861 }
2862 j++;
2863 } while (j < pattern.length());
2864 if (j == pattern.length()) {
2865 return i;
2866 }
2867 }
2868 return -1;
2869}
2870
2871
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002872// Strategy for searching for a string in another string.
2873enum StringSearchStrategy { SEARCH_FAIL, SEARCH_SHORT, SEARCH_LONG };
ager@chromium.org7c537e22008-10-16 08:43:32 +00002874
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002875
2876template <typename pchar>
2877static inline StringSearchStrategy InitializeStringSearch(
2878 Vector<const pchar> pat, bool ascii_subject) {
2879 ASSERT(pat.length() > 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002880 // We have an ASCII haystack and a non-ASCII needle. Check if there
2881 // really is a non-ASCII character in the needle and bail out if there
2882 // is.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002883 if (ascii_subject && sizeof(pchar) > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002884 for (int i = 0; i < pat.length(); i++) {
2885 uc16 c = pat[i];
2886 if (c > String::kMaxAsciiCharCode) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002887 return SEARCH_FAIL;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002888 }
2889 }
2890 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002891 if (pat.length() < kBMMinPatternLength) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002892 return SEARCH_SHORT;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002893 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002894 algorithm = SIMPLE_SEARCH;
2895 return SEARCH_LONG;
2896}
2897
2898
2899// Dispatch long needle searches to different algorithms.
2900template <typename schar, typename pchar>
2901static int ComplexIndexOf(Vector<const schar> sub,
2902 Vector<const pchar> pat,
2903 int start_index) {
2904 ASSERT(pat.length() >= kBMMinPatternLength);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002905 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002906 bool complete;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002907 int idx = start_index;
2908 switch (algorithm) {
2909 case SIMPLE_SEARCH:
2910 idx = SimpleIndexOf(sub, pat, idx, &complete);
2911 if (complete) return idx;
2912 BoyerMoorePopulateBadCharTable(pat);
2913 algorithm = BOYER_MOORE_HORSPOOL;
2914 // FALLTHROUGH.
2915 case BOYER_MOORE_HORSPOOL:
2916 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
2917 if (complete) return idx;
2918 // Build the Good Suffix table and continue searching.
2919 BoyerMoorePopulateGoodSuffixTable(pat);
2920 algorithm = BOYER_MOORE;
2921 // FALLTHROUGH.
2922 case BOYER_MOORE:
2923 return BoyerMooreIndexOf(sub, pat, idx);
2924 }
2925 UNREACHABLE();
2926 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002927}
2928
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002929
2930// Dispatch to different search strategies for a single search.
2931// If searching multiple times on the same needle, the search
2932// strategy should only be computed once and then dispatch to different
2933// loops.
2934template <typename schar, typename pchar>
2935static int StringSearch(Vector<const schar> sub,
2936 Vector<const pchar> pat,
2937 int start_index) {
2938 bool ascii_subject = (sizeof(schar) == 1);
2939 StringSearchStrategy strategy = InitializeStringSearch(pat, ascii_subject);
2940 switch (strategy) {
2941 case SEARCH_FAIL: return -1;
2942 case SEARCH_SHORT: return SimpleIndexOf(sub, pat, start_index);
2943 case SEARCH_LONG: return ComplexIndexOf(sub, pat, start_index);
2944 }
2945 UNREACHABLE();
2946 return -1;
2947}
2948
2949
ager@chromium.org7c537e22008-10-16 08:43:32 +00002950// Perform string match of pattern on subject, starting at start index.
2951// Caller must ensure that 0 <= start_index <= sub->length(),
2952// and should check that pat->length() + start_index <= sub->length()
2953int Runtime::StringMatch(Handle<String> sub,
2954 Handle<String> pat,
2955 int start_index) {
2956 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002957 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002958
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002959 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002960 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002961
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002962 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002963 if (start_index + pattern_length > subject_length) return -1;
2964
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002965 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002966 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002967 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002968
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002969 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002970 // character patterns linear search is necessary, so any smart
2971 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002972 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002973 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002974 String* seq_sub = *sub;
2975 if (seq_sub->IsConsString()) {
2976 seq_sub = ConsString::cast(seq_sub)->first();
2977 }
2978 if (seq_sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002979 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002980 if (pchar > String::kMaxAsciiCharCode) {
2981 return -1;
2982 }
2983 Vector<const char> ascii_vector =
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002984 seq_sub->ToAsciiVector().SubVector(start_index, subject_length);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002985 const void* pos = memchr(ascii_vector.start(),
2986 static_cast<const char>(pchar),
2987 static_cast<size_t>(ascii_vector.length()));
2988 if (pos == NULL) {
2989 return -1;
2990 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002991 return static_cast<int>(reinterpret_cast<const char*>(pos)
2992 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002993 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002994 return SingleCharIndexOf(seq_sub->ToUC16Vector(),
2995 pat->Get(0),
2996 start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002997 }
2998
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002999 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003000 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003001 }
ager@chromium.org236ad962008-09-25 09:45:57 +00003002
ager@chromium.org7c537e22008-10-16 08:43:32 +00003003 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
lrn@chromium.org32d961d2010-06-30 09:09:34 +00003004 // Extract flattened substrings of cons strings before determining asciiness.
3005 String* seq_sub = *sub;
3006 if (seq_sub->IsConsString()) {
3007 seq_sub = ConsString::cast(seq_sub)->first();
3008 }
3009 String* seq_pat = *pat;
3010 if (seq_pat->IsConsString()) {
3011 seq_pat = ConsString::cast(seq_pat)->first();
3012 }
3013
ager@chromium.org7c537e22008-10-16 08:43:32 +00003014 // dispatch on type of strings
lrn@chromium.org32d961d2010-06-30 09:09:34 +00003015 if (seq_pat->IsAsciiRepresentation()) {
3016 Vector<const char> pat_vector = seq_pat->ToAsciiVector();
3017 if (seq_sub->IsAsciiRepresentation()) {
3018 return StringSearch(seq_sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00003019 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00003020 return StringSearch(seq_sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00003021 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00003022 Vector<const uc16> pat_vector = seq_pat->ToUC16Vector();
3023 if (seq_sub->IsAsciiRepresentation()) {
3024 return StringSearch(seq_sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003025 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00003026 return StringSearch(seq_sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003027}
3028
3029
3030static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003031 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003032 ASSERT(args.length() == 3);
3033
ager@chromium.org7c537e22008-10-16 08:43:32 +00003034 CONVERT_ARG_CHECKED(String, sub, 0);
3035 CONVERT_ARG_CHECKED(String, pat, 1);
3036
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003037 Object* index = args[2];
3038 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003039 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003040
ager@chromium.org870a0b62008-11-04 11:43:05 +00003041 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00003042 int position = Runtime::StringMatch(sub, pat, start_index);
3043 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003044}
3045
3046
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003047template <typename schar, typename pchar>
3048static int StringMatchBackwards(Vector<const schar> sub,
3049 Vector<const pchar> pat,
3050 int idx) {
3051 ASSERT(pat.length() >= 1);
3052 ASSERT(idx + pat.length() <= sub.length());
3053
3054 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
3055 for (int i = 0; i < pat.length(); i++) {
3056 uc16 c = pat[i];
3057 if (c > String::kMaxAsciiCharCode) {
3058 return -1;
3059 }
3060 }
3061 }
3062
3063 pchar pattern_first_char = pat[0];
3064 for (int i = idx; i >= 0; i--) {
3065 if (sub[i] != pattern_first_char) continue;
3066 int j = 1;
3067 while (j < pat.length()) {
3068 if (pat[j] != sub[i+j]) {
3069 break;
3070 }
3071 j++;
3072 }
3073 if (j == pat.length()) {
3074 return i;
3075 }
3076 }
3077 return -1;
3078}
3079
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003080static Object* Runtime_StringLastIndexOf(Arguments args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003081 HandleScope scope; // create a new handle scope
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003082 ASSERT(args.length() == 3);
3083
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003084 CONVERT_ARG_CHECKED(String, sub, 0);
3085 CONVERT_ARG_CHECKED(String, pat, 1);
3086
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003087 Object* index = args[2];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003088 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003089 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003090
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003091 uint32_t pat_length = pat->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003092 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003093
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003094 if (start_index + pat_length > sub_length) {
3095 start_index = sub_length - pat_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00003096 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003097
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003098 if (pat_length == 0) {
3099 return Smi::FromInt(start_index);
3100 }
3101
3102 if (!sub->IsFlat()) {
3103 FlattenString(sub);
3104 }
3105
3106 if (pat_length == 1) {
3107 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
3108 if (sub->IsAsciiRepresentation()) {
3109 uc16 pchar = pat->Get(0);
3110 if (pchar > String::kMaxAsciiCharCode) {
3111 return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003112 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003113 return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
3114 static_cast<char>(pat->Get(0)),
3115 start_index));
3116 } else {
3117 return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
3118 pat->Get(0),
3119 start_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003120 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003121 }
3122
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003123 if (!pat->IsFlat()) {
3124 FlattenString(pat);
3125 }
3126
3127 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
3128
3129 int position = -1;
3130
3131 if (pat->IsAsciiRepresentation()) {
3132 Vector<const char> pat_vector = pat->ToAsciiVector();
3133 if (sub->IsAsciiRepresentation()) {
3134 position = StringMatchBackwards(sub->ToAsciiVector(),
3135 pat_vector,
3136 start_index);
3137 } else {
3138 position = StringMatchBackwards(sub->ToUC16Vector(),
3139 pat_vector,
3140 start_index);
3141 }
3142 } else {
3143 Vector<const uc16> pat_vector = pat->ToUC16Vector();
3144 if (sub->IsAsciiRepresentation()) {
3145 position = StringMatchBackwards(sub->ToAsciiVector(),
3146 pat_vector,
3147 start_index);
3148 } else {
3149 position = StringMatchBackwards(sub->ToUC16Vector(),
3150 pat_vector,
3151 start_index);
3152 }
3153 }
3154
3155 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003156}
3157
3158
3159static Object* Runtime_StringLocaleCompare(Arguments args) {
3160 NoHandleAllocation ha;
3161 ASSERT(args.length() == 2);
3162
3163 CONVERT_CHECKED(String, str1, args[0]);
3164 CONVERT_CHECKED(String, str2, args[1]);
3165
3166 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003167 int str1_length = str1->length();
3168 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003169
3170 // Decide trivial cases without flattening.
3171 if (str1_length == 0) {
3172 if (str2_length == 0) return Smi::FromInt(0); // Equal.
3173 return Smi::FromInt(-str2_length);
3174 } else {
3175 if (str2_length == 0) return Smi::FromInt(str1_length);
3176 }
3177
3178 int end = str1_length < str2_length ? str1_length : str2_length;
3179
3180 // No need to flatten if we are going to find the answer on the first
3181 // character. At this point we know there is at least one character
3182 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003183 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003184 if (d != 0) return Smi::FromInt(d);
3185
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003186 str1->TryFlatten();
3187 str2->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003188
3189 static StringInputBuffer buf1;
3190 static StringInputBuffer buf2;
3191
3192 buf1.Reset(str1);
3193 buf2.Reset(str2);
3194
3195 for (int i = 0; i < end; i++) {
3196 uint16_t char1 = buf1.GetNext();
3197 uint16_t char2 = buf2.GetNext();
3198 if (char1 != char2) return Smi::FromInt(char1 - char2);
3199 }
3200
3201 return Smi::FromInt(str1_length - str2_length);
3202}
3203
3204
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003205static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003206 NoHandleAllocation ha;
3207 ASSERT(args.length() == 3);
3208
3209 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00003210 Object* from = args[1];
3211 Object* to = args[2];
3212 int start, end;
3213 // We have a fast integer-only case here to avoid a conversion to double in
3214 // the common case where from and to are Smis.
3215 if (from->IsSmi() && to->IsSmi()) {
3216 start = Smi::cast(from)->value();
3217 end = Smi::cast(to)->value();
3218 } else {
3219 CONVERT_DOUBLE_CHECKED(from_number, from);
3220 CONVERT_DOUBLE_CHECKED(to_number, to);
3221 start = FastD2I(from_number);
3222 end = FastD2I(to_number);
3223 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003224 RUNTIME_ASSERT(end >= start);
3225 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003226 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003227 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003228 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003229}
3230
3231
ager@chromium.org41826e72009-03-30 13:30:57 +00003232static Object* Runtime_StringMatch(Arguments args) {
3233 ASSERT_EQ(3, args.length());
3234
3235 CONVERT_ARG_CHECKED(String, subject, 0);
3236 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
3237 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
3238 HandleScope handles;
3239
3240 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
3241
3242 if (match.is_null()) {
3243 return Failure::Exception();
3244 }
3245 if (match->IsNull()) {
3246 return Heap::null_value();
3247 }
3248 int length = subject->length();
3249
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00003250 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00003251 ZoneList<int> offsets(8);
3252 do {
3253 int start;
3254 int end;
3255 {
3256 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003257 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00003258 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
3259 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
3260 }
3261 offsets.Add(start);
3262 offsets.Add(end);
3263 int index = start < end ? end : end + 1;
3264 if (index > length) break;
3265 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
3266 if (match.is_null()) {
3267 return Failure::Exception();
3268 }
3269 } while (!match->IsNull());
3270 int matches = offsets.length() / 2;
3271 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
3272 for (int i = 0; i < matches ; i++) {
3273 int from = offsets.at(i * 2);
3274 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003275 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00003276 }
3277 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
3278 result->set_length(Smi::FromInt(matches));
3279 return *result;
3280}
3281
3282
lrn@chromium.org25156de2010-04-06 13:10:27 +00003283// Two smis before and after the match, for very long strings.
3284const int kMaxBuilderEntriesPerRegExpMatch = 5;
3285
3286
3287static void SetLastMatchInfoNoCaptures(Handle<String> subject,
3288 Handle<JSArray> last_match_info,
3289 int match_start,
3290 int match_end) {
3291 // Fill last_match_info with a single capture.
3292 last_match_info->EnsureSize(2 + RegExpImpl::kLastMatchOverhead);
3293 AssertNoAllocation no_gc;
3294 FixedArray* elements = FixedArray::cast(last_match_info->elements());
3295 RegExpImpl::SetLastCaptureCount(elements, 2);
3296 RegExpImpl::SetLastInput(elements, *subject);
3297 RegExpImpl::SetLastSubject(elements, *subject);
3298 RegExpImpl::SetCapture(elements, 0, match_start);
3299 RegExpImpl::SetCapture(elements, 1, match_end);
3300}
3301
3302
3303template <typename schar>
3304static bool SearchCharMultiple(Vector<schar> subject,
3305 String* pattern,
3306 schar pattern_char,
3307 FixedArrayBuilder* builder,
3308 int* match_pos) {
3309 // Position of last match.
3310 int pos = *match_pos;
3311 int subject_length = subject.length();
3312 while (pos < subject_length) {
3313 int match_end = pos + 1;
3314 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3315 *match_pos = pos;
3316 return false;
3317 }
3318 int new_pos = SingleCharIndexOf(subject, pattern_char, match_end);
3319 if (new_pos >= 0) {
3320 // Match has been found.
3321 if (new_pos > match_end) {
3322 ReplacementStringBuilder::AddSubjectSlice(builder, match_end, new_pos);
3323 }
3324 pos = new_pos;
3325 builder->Add(pattern);
3326 } else {
3327 break;
3328 }
3329 }
3330 if (pos + 1 < subject_length) {
3331 ReplacementStringBuilder::AddSubjectSlice(builder, pos + 1, subject_length);
3332 }
3333 *match_pos = pos;
3334 return true;
3335}
3336
3337
3338static bool SearchCharMultiple(Handle<String> subject,
3339 Handle<String> pattern,
3340 Handle<JSArray> last_match_info,
3341 FixedArrayBuilder* builder) {
3342 ASSERT(subject->IsFlat());
3343 ASSERT_EQ(1, pattern->length());
3344 uc16 pattern_char = pattern->Get(0);
3345 // Treating position before first as initial "previous match position".
3346 int match_pos = -1;
3347
3348 for (;;) { // Break when search complete.
3349 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3350 AssertNoAllocation no_gc;
3351 if (subject->IsAsciiRepresentation()) {
3352 if (pattern_char > String::kMaxAsciiCharCode) {
3353 break;
3354 }
3355 Vector<const char> subject_vector = subject->ToAsciiVector();
3356 char pattern_ascii_char = static_cast<char>(pattern_char);
3357 bool complete = SearchCharMultiple<const char>(subject_vector,
3358 *pattern,
3359 pattern_ascii_char,
3360 builder,
3361 &match_pos);
3362 if (complete) break;
3363 } else {
3364 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3365 bool complete = SearchCharMultiple<const uc16>(subject_vector,
3366 *pattern,
3367 pattern_char,
3368 builder,
3369 &match_pos);
3370 if (complete) break;
3371 }
3372 }
3373
3374 if (match_pos >= 0) {
3375 SetLastMatchInfoNoCaptures(subject,
3376 last_match_info,
3377 match_pos,
3378 match_pos + 1);
3379 return true;
3380 }
3381 return false; // No matches at all.
3382}
3383
3384
3385template <typename schar, typename pchar>
3386static bool SearchStringMultiple(Vector<schar> subject,
3387 String* pattern,
3388 Vector<pchar> pattern_string,
3389 FixedArrayBuilder* builder,
3390 int* match_pos) {
3391 int pos = *match_pos;
3392 int subject_length = subject.length();
3393 int pattern_length = pattern_string.length();
3394 int max_search_start = subject_length - pattern_length;
3395 bool is_ascii = (sizeof(schar) == 1);
3396 StringSearchStrategy strategy =
3397 InitializeStringSearch(pattern_string, is_ascii);
3398 switch (strategy) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003399 case SEARCH_FAIL: break;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003400 case SEARCH_SHORT:
3401 while (pos <= max_search_start) {
3402 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3403 *match_pos = pos;
3404 return false;
3405 }
3406 // Position of end of previous match.
3407 int match_end = pos + pattern_length;
3408 int new_pos = SimpleIndexOf(subject, pattern_string, match_end);
3409 if (new_pos >= 0) {
3410 // A match.
3411 if (new_pos > match_end) {
3412 ReplacementStringBuilder::AddSubjectSlice(builder,
3413 match_end,
3414 new_pos);
3415 }
3416 pos = new_pos;
3417 builder->Add(pattern);
3418 } else {
3419 break;
3420 }
3421 }
3422 break;
3423 case SEARCH_LONG:
3424 while (pos <= max_search_start) {
3425 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003426 *match_pos = pos;
3427 return false;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003428 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003429 int match_end = pos + pattern_length;
3430 int new_pos = ComplexIndexOf(subject, pattern_string, match_end);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003431 if (new_pos >= 0) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003432 // A match has been found.
3433 if (new_pos > match_end) {
3434 ReplacementStringBuilder::AddSubjectSlice(builder,
3435 match_end,
3436 new_pos);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003437 }
3438 pos = new_pos;
3439 builder->Add(pattern);
3440 } else {
3441 break;
3442 }
3443 }
3444 break;
3445 }
3446 if (pos < max_search_start) {
3447 ReplacementStringBuilder::AddSubjectSlice(builder,
3448 pos + pattern_length,
3449 subject_length);
3450 }
3451 *match_pos = pos;
3452 return true;
3453}
3454
3455
3456static bool SearchStringMultiple(Handle<String> subject,
3457 Handle<String> pattern,
3458 Handle<JSArray> last_match_info,
3459 FixedArrayBuilder* builder) {
3460 ASSERT(subject->IsFlat());
3461 ASSERT(pattern->IsFlat());
3462 ASSERT(pattern->length() > 1);
3463
3464 // Treating as if a previous match was before first character.
3465 int match_pos = -pattern->length();
3466
3467 for (;;) { // Break when search complete.
3468 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3469 AssertNoAllocation no_gc;
3470 if (subject->IsAsciiRepresentation()) {
3471 Vector<const char> subject_vector = subject->ToAsciiVector();
3472 if (pattern->IsAsciiRepresentation()) {
3473 if (SearchStringMultiple(subject_vector,
3474 *pattern,
3475 pattern->ToAsciiVector(),
3476 builder,
3477 &match_pos)) break;
3478 } else {
3479 if (SearchStringMultiple(subject_vector,
3480 *pattern,
3481 pattern->ToUC16Vector(),
3482 builder,
3483 &match_pos)) break;
3484 }
3485 } else {
3486 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3487 if (pattern->IsAsciiRepresentation()) {
3488 if (SearchStringMultiple(subject_vector,
3489 *pattern,
3490 pattern->ToAsciiVector(),
3491 builder,
3492 &match_pos)) break;
3493 } else {
3494 if (SearchStringMultiple(subject_vector,
3495 *pattern,
3496 pattern->ToUC16Vector(),
3497 builder,
3498 &match_pos)) break;
3499 }
3500 }
3501 }
3502
3503 if (match_pos >= 0) {
3504 SetLastMatchInfoNoCaptures(subject,
3505 last_match_info,
3506 match_pos,
3507 match_pos + pattern->length());
3508 return true;
3509 }
3510 return false; // No matches at all.
3511}
3512
3513
3514static RegExpImpl::IrregexpResult SearchRegExpNoCaptureMultiple(
3515 Handle<String> subject,
3516 Handle<JSRegExp> regexp,
3517 Handle<JSArray> last_match_array,
3518 FixedArrayBuilder* builder) {
3519 ASSERT(subject->IsFlat());
3520 int match_start = -1;
3521 int match_end = 0;
3522 int pos = 0;
3523 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3524 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3525
3526 OffsetsVector registers(required_registers);
3527 Vector<int> register_vector(registers.vector(), registers.length());
3528 int subject_length = subject->length();
3529
3530 for (;;) { // Break on failure, return on exception.
3531 RegExpImpl::IrregexpResult result =
3532 RegExpImpl::IrregexpExecOnce(regexp,
3533 subject,
3534 pos,
3535 register_vector);
3536 if (result == RegExpImpl::RE_SUCCESS) {
3537 match_start = register_vector[0];
3538 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3539 if (match_end < match_start) {
3540 ReplacementStringBuilder::AddSubjectSlice(builder,
3541 match_end,
3542 match_start);
3543 }
3544 match_end = register_vector[1];
3545 HandleScope loop_scope;
3546 builder->Add(*Factory::NewSubString(subject, match_start, match_end));
3547 if (match_start != match_end) {
3548 pos = match_end;
3549 } else {
3550 pos = match_end + 1;
3551 if (pos > subject_length) break;
3552 }
3553 } else if (result == RegExpImpl::RE_FAILURE) {
3554 break;
3555 } else {
3556 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3557 return result;
3558 }
3559 }
3560
3561 if (match_start >= 0) {
3562 if (match_end < subject_length) {
3563 ReplacementStringBuilder::AddSubjectSlice(builder,
3564 match_end,
3565 subject_length);
3566 }
3567 SetLastMatchInfoNoCaptures(subject,
3568 last_match_array,
3569 match_start,
3570 match_end);
3571 return RegExpImpl::RE_SUCCESS;
3572 } else {
3573 return RegExpImpl::RE_FAILURE; // No matches at all.
3574 }
3575}
3576
3577
3578static RegExpImpl::IrregexpResult SearchRegExpMultiple(
3579 Handle<String> subject,
3580 Handle<JSRegExp> regexp,
3581 Handle<JSArray> last_match_array,
3582 FixedArrayBuilder* builder) {
3583
3584 ASSERT(subject->IsFlat());
3585 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3586 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3587
3588 OffsetsVector registers(required_registers);
3589 Vector<int> register_vector(registers.vector(), registers.length());
3590
3591 RegExpImpl::IrregexpResult result =
3592 RegExpImpl::IrregexpExecOnce(regexp,
3593 subject,
3594 0,
3595 register_vector);
3596
3597 int capture_count = regexp->CaptureCount();
3598 int subject_length = subject->length();
3599
3600 // Position to search from.
3601 int pos = 0;
3602 // End of previous match. Differs from pos if match was empty.
3603 int match_end = 0;
3604 if (result == RegExpImpl::RE_SUCCESS) {
3605 // Need to keep a copy of the previous match for creating last_match_info
3606 // at the end, so we have two vectors that we swap between.
3607 OffsetsVector registers2(required_registers);
3608 Vector<int> prev_register_vector(registers2.vector(), registers2.length());
3609
3610 do {
3611 int match_start = register_vector[0];
3612 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3613 if (match_end < match_start) {
3614 ReplacementStringBuilder::AddSubjectSlice(builder,
3615 match_end,
3616 match_start);
3617 }
3618 match_end = register_vector[1];
3619
3620 {
3621 // Avoid accumulating new handles inside loop.
3622 HandleScope temp_scope;
3623 // Arguments array to replace function is match, captures, index and
3624 // subject, i.e., 3 + capture count in total.
3625 Handle<FixedArray> elements = Factory::NewFixedArray(3 + capture_count);
3626 elements->set(0, *Factory::NewSubString(subject,
3627 match_start,
3628 match_end));
3629 for (int i = 1; i <= capture_count; i++) {
3630 int start = register_vector[i * 2];
3631 if (start >= 0) {
3632 int end = register_vector[i * 2 + 1];
3633 ASSERT(start <= end);
3634 Handle<String> substring = Factory::NewSubString(subject,
3635 start,
3636 end);
3637 elements->set(i, *substring);
3638 } else {
3639 ASSERT(register_vector[i * 2 + 1] < 0);
3640 elements->set(i, Heap::undefined_value());
3641 }
3642 }
3643 elements->set(capture_count + 1, Smi::FromInt(match_start));
3644 elements->set(capture_count + 2, *subject);
3645 builder->Add(*Factory::NewJSArrayWithElements(elements));
3646 }
3647 // Swap register vectors, so the last successful match is in
3648 // prev_register_vector.
3649 Vector<int> tmp = prev_register_vector;
3650 prev_register_vector = register_vector;
3651 register_vector = tmp;
3652
3653 if (match_end > match_start) {
3654 pos = match_end;
3655 } else {
3656 pos = match_end + 1;
3657 if (pos > subject_length) {
3658 break;
3659 }
3660 }
3661
3662 result = RegExpImpl::IrregexpExecOnce(regexp,
3663 subject,
3664 pos,
3665 register_vector);
3666 } while (result == RegExpImpl::RE_SUCCESS);
3667
3668 if (result != RegExpImpl::RE_EXCEPTION) {
3669 // Finished matching, with at least one match.
3670 if (match_end < subject_length) {
3671 ReplacementStringBuilder::AddSubjectSlice(builder,
3672 match_end,
3673 subject_length);
3674 }
3675
3676 int last_match_capture_count = (capture_count + 1) * 2;
3677 int last_match_array_size =
3678 last_match_capture_count + RegExpImpl::kLastMatchOverhead;
3679 last_match_array->EnsureSize(last_match_array_size);
3680 AssertNoAllocation no_gc;
3681 FixedArray* elements = FixedArray::cast(last_match_array->elements());
3682 RegExpImpl::SetLastCaptureCount(elements, last_match_capture_count);
3683 RegExpImpl::SetLastSubject(elements, *subject);
3684 RegExpImpl::SetLastInput(elements, *subject);
3685 for (int i = 0; i < last_match_capture_count; i++) {
3686 RegExpImpl::SetCapture(elements, i, prev_register_vector[i]);
3687 }
3688 return RegExpImpl::RE_SUCCESS;
3689 }
3690 }
3691 // No matches at all, return failure or exception result directly.
3692 return result;
3693}
3694
3695
3696static Object* Runtime_RegExpExecMultiple(Arguments args) {
3697 ASSERT(args.length() == 4);
3698 HandleScope handles;
3699
3700 CONVERT_ARG_CHECKED(String, subject, 1);
3701 if (!subject->IsFlat()) { FlattenString(subject); }
3702 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
3703 CONVERT_ARG_CHECKED(JSArray, last_match_info, 2);
3704 CONVERT_ARG_CHECKED(JSArray, result_array, 3);
3705
3706 ASSERT(last_match_info->HasFastElements());
3707 ASSERT(regexp->GetFlags().is_global());
3708 Handle<FixedArray> result_elements;
3709 if (result_array->HasFastElements()) {
3710 result_elements =
3711 Handle<FixedArray>(FixedArray::cast(result_array->elements()));
3712 } else {
3713 result_elements = Factory::NewFixedArrayWithHoles(16);
3714 }
3715 FixedArrayBuilder builder(result_elements);
3716
3717 if (regexp->TypeTag() == JSRegExp::ATOM) {
3718 Handle<String> pattern(
3719 String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex)));
3720 int pattern_length = pattern->length();
3721 if (pattern_length == 1) {
3722 if (SearchCharMultiple(subject, pattern, last_match_info, &builder)) {
3723 return *builder.ToJSArray(result_array);
3724 }
3725 return Heap::null_value();
3726 }
3727
3728 if (!pattern->IsFlat()) FlattenString(pattern);
3729 if (SearchStringMultiple(subject, pattern, last_match_info, &builder)) {
3730 return *builder.ToJSArray(result_array);
3731 }
3732 return Heap::null_value();
3733 }
3734
3735 ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
3736
3737 RegExpImpl::IrregexpResult result;
3738 if (regexp->CaptureCount() == 0) {
3739 result = SearchRegExpNoCaptureMultiple(subject,
3740 regexp,
3741 last_match_info,
3742 &builder);
3743 } else {
3744 result = SearchRegExpMultiple(subject, regexp, last_match_info, &builder);
3745 }
3746 if (result == RegExpImpl::RE_SUCCESS) return *builder.ToJSArray(result_array);
3747 if (result == RegExpImpl::RE_FAILURE) return Heap::null_value();
3748 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3749 return Failure::Exception();
3750}
3751
3752
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003753static Object* Runtime_NumberToRadixString(Arguments args) {
3754 NoHandleAllocation ha;
3755 ASSERT(args.length() == 2);
3756
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003757 // Fast case where the result is a one character string.
3758 if (args[0]->IsSmi() && args[1]->IsSmi()) {
3759 int value = Smi::cast(args[0])->value();
3760 int radix = Smi::cast(args[1])->value();
3761 if (value >= 0 && value < radix) {
3762 RUNTIME_ASSERT(radix <= 36);
3763 // Character array used for conversion.
3764 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
3765 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
3766 }
3767 }
3768
3769 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003770 CONVERT_DOUBLE_CHECKED(value, args[0]);
3771 if (isnan(value)) {
3772 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3773 }
3774 if (isinf(value)) {
3775 if (value < 0) {
3776 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3777 }
3778 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3779 }
3780 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
3781 int radix = FastD2I(radix_number);
3782 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3783 char* str = DoubleToRadixCString(value, radix);
3784 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3785 DeleteArray(str);
3786 return result;
3787}
3788
3789
3790static Object* Runtime_NumberToFixed(Arguments args) {
3791 NoHandleAllocation ha;
3792 ASSERT(args.length() == 2);
3793
3794 CONVERT_DOUBLE_CHECKED(value, args[0]);
3795 if (isnan(value)) {
3796 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3797 }
3798 if (isinf(value)) {
3799 if (value < 0) {
3800 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3801 }
3802 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3803 }
3804 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3805 int f = FastD2I(f_number);
3806 RUNTIME_ASSERT(f >= 0);
3807 char* str = DoubleToFixedCString(value, f);
3808 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3809 DeleteArray(str);
3810 return res;
3811}
3812
3813
3814static Object* Runtime_NumberToExponential(Arguments args) {
3815 NoHandleAllocation ha;
3816 ASSERT(args.length() == 2);
3817
3818 CONVERT_DOUBLE_CHECKED(value, args[0]);
3819 if (isnan(value)) {
3820 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3821 }
3822 if (isinf(value)) {
3823 if (value < 0) {
3824 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3825 }
3826 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3827 }
3828 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3829 int f = FastD2I(f_number);
3830 RUNTIME_ASSERT(f >= -1 && f <= 20);
3831 char* str = DoubleToExponentialCString(value, f);
3832 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3833 DeleteArray(str);
3834 return res;
3835}
3836
3837
3838static Object* Runtime_NumberToPrecision(Arguments args) {
3839 NoHandleAllocation ha;
3840 ASSERT(args.length() == 2);
3841
3842 CONVERT_DOUBLE_CHECKED(value, args[0]);
3843 if (isnan(value)) {
3844 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3845 }
3846 if (isinf(value)) {
3847 if (value < 0) {
3848 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3849 }
3850 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3851 }
3852 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3853 int f = FastD2I(f_number);
3854 RUNTIME_ASSERT(f >= 1 && f <= 21);
3855 char* str = DoubleToPrecisionCString(value, f);
3856 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3857 DeleteArray(str);
3858 return res;
3859}
3860
3861
3862// Returns a single character string where first character equals
3863// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003864static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003865 if (index < static_cast<uint32_t>(string->length())) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003866 string->TryFlatten();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003867 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003868 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003869 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003870 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003871}
3872
3873
3874Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
3875 // Handle [] indexing on Strings
3876 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003877 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
3878 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003879 }
3880
3881 // Handle [] indexing on String objects
3882 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003883 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
3884 Handle<Object> result =
3885 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
3886 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003887 }
3888
3889 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003890 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003891 return prototype->GetElement(index);
3892 }
3893
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003894 return GetElement(object, index);
3895}
3896
3897
3898Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003899 return object->GetElement(index);
3900}
3901
3902
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003903Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
3904 HandleScope scope;
3905
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003906 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003907 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003908 Handle<Object> error =
3909 Factory::NewTypeError("non_object_property_load",
3910 HandleVector(args, 2));
3911 return Top::Throw(*error);
3912 }
3913
3914 // Check if the given key is an array index.
3915 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003916 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003917 return GetElementOrCharAt(object, index);
3918 }
3919
3920 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003921 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003922 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003923 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003924 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003925 bool has_pending_exception = false;
3926 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003927 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003928 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003929 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003930 }
3931
ager@chromium.org32912102009-01-16 10:38:43 +00003932 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003933 // the element if so.
3934 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003935 return GetElementOrCharAt(object, index);
3936 } else {
3937 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003938 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003939 }
3940}
3941
3942
3943static Object* Runtime_GetProperty(Arguments args) {
3944 NoHandleAllocation ha;
3945 ASSERT(args.length() == 2);
3946
3947 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003948 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003949
3950 return Runtime::GetObjectProperty(object, key);
3951}
3952
3953
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003954// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003955static Object* Runtime_KeyedGetProperty(Arguments args) {
3956 NoHandleAllocation ha;
3957 ASSERT(args.length() == 2);
3958
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003959 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00003960 // itself.
3961 //
3962 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00003963 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00003964 // global proxy object never has properties. This is the case
3965 // because the global proxy object forwards everything to its hidden
3966 // prototype including local lookups.
3967 //
3968 // Additionally, we need to make sure that we do not cache results
3969 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003970 if (args[0]->IsJSObject() &&
3971 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00003972 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003973 args[1]->IsString()) {
3974 JSObject* receiver = JSObject::cast(args[0]);
3975 String* key = String::cast(args[1]);
3976 if (receiver->HasFastProperties()) {
3977 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003978 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003979 int offset = KeyedLookupCache::Lookup(receiver_map, key);
3980 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003981 Object* value = receiver->FastPropertyAt(offset);
3982 return value->IsTheHole() ? Heap::undefined_value() : value;
3983 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003984 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003985 LookupResult result;
3986 receiver->LocalLookup(key, &result);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00003987 if (result.IsProperty() && result.type() == FIELD) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003988 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003989 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003990 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003991 }
3992 } else {
3993 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003994 StringDictionary* dictionary = receiver->property_dictionary();
3995 int entry = dictionary->FindEntry(key);
3996 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003997 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003998 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003999 if (!receiver->IsGlobalObject()) return value;
4000 value = JSGlobalPropertyCell::cast(value)->value();
4001 if (!value->IsTheHole()) return value;
4002 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004003 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00004004 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004005 } else if (args[0]->IsString() && args[1]->IsSmi()) {
4006 // Fast case for string indexing using [] with a smi index.
4007 HandleScope scope;
4008 Handle<String> str = args.at<String>(0);
4009 int index = Smi::cast(args[1])->value();
4010 Handle<Object> result = GetCharAt(str, index);
4011 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00004012 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004013
4014 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00004015 return Runtime::GetObjectProperty(args.at<Object>(0),
4016 args.at<Object>(1));
4017}
4018
4019
ager@chromium.org5c838252010-02-19 08:53:10 +00004020static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
4021 ASSERT(args.length() == 5);
4022 HandleScope scope;
4023 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4024 CONVERT_CHECKED(String, name, args[1]);
4025 CONVERT_CHECKED(Smi, flag_setter, args[2]);
4026 CONVERT_CHECKED(JSFunction, fun, args[3]);
4027 CONVERT_CHECKED(Smi, flag_attr, args[4]);
4028 int unchecked = flag_attr->value();
4029 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4030 RUNTIME_ASSERT(!obj->IsNull());
4031 LookupResult result;
4032 obj->LocalLookupRealNamedProperty(name, &result);
4033
4034 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
4035 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
4036 // delete it to avoid running into trouble in DefineAccessor, which
4037 // handles this incorrectly if the property is readonly (does nothing)
4038 if (result.IsProperty() &&
4039 (result.type() == FIELD || result.type() == NORMAL
4040 || result.type() == CONSTANT_FUNCTION)) {
4041 obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
4042 }
4043 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
4044}
4045
4046static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
4047 ASSERT(args.length() == 4);
4048 HandleScope scope;
4049 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
4050 CONVERT_ARG_CHECKED(String, name, 1);
4051 Handle<Object> obj_value = args.at<Object>(2);
4052
4053 CONVERT_CHECKED(Smi, flag, args[3]);
4054 int unchecked = flag->value();
4055 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4056
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004057 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
4058
4059 // Check if this is an element.
4060 uint32_t index;
4061 bool is_element = name->AsArrayIndex(&index);
4062
4063 // Special case for elements if any of the flags are true.
4064 // If elements are in fast case we always implicitly assume that:
4065 // DONT_DELETE: false, DONT_ENUM: false, READ_ONLY: false.
4066 if (((unchecked & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) &&
4067 is_element) {
4068 // Normalize the elements to enable attributes on the property.
4069 js_object->NormalizeElements();
4070 NumberDictionary* dictionary = js_object->element_dictionary();
4071 // Make sure that we never go back to fast case.
4072 dictionary->set_requires_slow_elements();
4073 PropertyDetails details = PropertyDetails(attr, NORMAL);
4074 dictionary->Set(index, *obj_value, details);
4075 }
4076
ager@chromium.org5c838252010-02-19 08:53:10 +00004077 LookupResult result;
4078 js_object->LocalLookupRealNamedProperty(*name, &result);
4079
ager@chromium.org5c838252010-02-19 08:53:10 +00004080 // Take special care when attributes are different and there is already
4081 // a property. For simplicity we normalize the property which enables us
4082 // to not worry about changing the instance_descriptor and creating a new
4083 // map. The current version of SetObjectProperty does not handle attributes
4084 // correctly in the case where a property is a field and is reset with
4085 // new attributes.
4086 if (result.IsProperty() && attr != result.GetAttributes()) {
4087 // New attributes - normalize to avoid writing to instance descriptor
4088 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
4089 // Use IgnoreAttributes version since a readonly property may be
4090 // overridden and SetProperty does not allow this.
4091 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
4092 *obj_value,
4093 attr);
4094 }
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004095
ager@chromium.org5c838252010-02-19 08:53:10 +00004096 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
4097}
4098
4099
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004100Object* Runtime::SetObjectProperty(Handle<Object> object,
4101 Handle<Object> key,
4102 Handle<Object> value,
4103 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004104 HandleScope scope;
4105
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004106 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004107 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004108 Handle<Object> error =
4109 Factory::NewTypeError("non_object_property_store",
4110 HandleVector(args, 2));
4111 return Top::Throw(*error);
4112 }
4113
4114 // If the object isn't a JavaScript object, we ignore the store.
4115 if (!object->IsJSObject()) return *value;
4116
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004117 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
4118
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004119 // Check if the given key is an array index.
4120 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004121 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004122 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
4123 // of a string using [] notation. We need to support this too in
4124 // JavaScript.
4125 // In the case of a String object we just need to redirect the assignment to
4126 // the underlying string if the index is in range. Since the underlying
4127 // string does nothing with the assignment then we can ignore such
4128 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004129 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004130 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004131 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004132
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004133 Handle<Object> result = SetElement(js_object, index, value);
4134 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004135 return *value;
4136 }
4137
4138 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004139 Handle<Object> result;
4140 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004141 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004142 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004143 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004144 key_string->TryFlatten();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004145 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004146 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004147 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004148 return *value;
4149 }
4150
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004151 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004152 bool has_pending_exception = false;
4153 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4154 if (has_pending_exception) return Failure::Exception();
4155 Handle<String> name = Handle<String>::cast(converted);
4156
4157 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004158 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004159 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004160 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004161 }
4162}
4163
4164
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004165Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
4166 Handle<Object> key,
4167 Handle<Object> value,
4168 PropertyAttributes attr) {
4169 HandleScope scope;
4170
4171 // Check if the given key is an array index.
4172 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004173 if (key->ToArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004174 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
4175 // of a string using [] notation. We need to support this too in
4176 // JavaScript.
4177 // In the case of a String object we just need to redirect the assignment to
4178 // the underlying string if the index is in range. Since the underlying
4179 // string does nothing with the assignment then we can ignore such
4180 // assignments.
4181 if (js_object->IsStringObjectWithCharacterAt(index)) {
4182 return *value;
4183 }
4184
4185 return js_object->SetElement(index, *value);
4186 }
4187
4188 if (key->IsString()) {
4189 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004190 return js_object->SetElement(index, *value);
4191 } else {
4192 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004193 key_string->TryFlatten();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004194 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
4195 *value,
4196 attr);
4197 }
4198 }
4199
4200 // Call-back into JavaScript to convert the key to a string.
4201 bool has_pending_exception = false;
4202 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4203 if (has_pending_exception) return Failure::Exception();
4204 Handle<String> name = Handle<String>::cast(converted);
4205
4206 if (name->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004207 return js_object->SetElement(index, *value);
4208 } else {
4209 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
4210 }
4211}
4212
4213
ager@chromium.orge2902be2009-06-08 12:21:35 +00004214Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
4215 Handle<Object> key) {
4216 HandleScope scope;
4217
4218 // Check if the given key is an array index.
4219 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004220 if (key->ToArrayIndex(&index)) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00004221 // In Firefox/SpiderMonkey, Safari and Opera you can access the
4222 // characters of a string using [] notation. In the case of a
4223 // String object we just need to redirect the deletion to the
4224 // underlying string if the index is in range. Since the
4225 // underlying string does nothing with the deletion, we can ignore
4226 // such deletions.
4227 if (js_object->IsStringObjectWithCharacterAt(index)) {
4228 return Heap::true_value();
4229 }
4230
4231 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
4232 }
4233
4234 Handle<String> key_string;
4235 if (key->IsString()) {
4236 key_string = Handle<String>::cast(key);
4237 } else {
4238 // Call-back into JavaScript to convert the key to a string.
4239 bool has_pending_exception = false;
4240 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4241 if (has_pending_exception) return Failure::Exception();
4242 key_string = Handle<String>::cast(converted);
4243 }
4244
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004245 key_string->TryFlatten();
ager@chromium.orge2902be2009-06-08 12:21:35 +00004246 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
4247}
4248
4249
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004250static Object* Runtime_SetProperty(Arguments args) {
4251 NoHandleAllocation ha;
4252 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
4253
4254 Handle<Object> object = args.at<Object>(0);
4255 Handle<Object> key = args.at<Object>(1);
4256 Handle<Object> value = args.at<Object>(2);
4257
4258 // Compute attributes.
4259 PropertyAttributes attributes = NONE;
4260 if (args.length() == 4) {
4261 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004262 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004263 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004264 RUNTIME_ASSERT(
4265 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4266 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004267 }
4268 return Runtime::SetObjectProperty(object, key, value, attributes);
4269}
4270
4271
4272// Set a local property, even if it is READ_ONLY. If the property does not
4273// exist, it will be added with attributes NONE.
4274static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
4275 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004276 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004277 CONVERT_CHECKED(JSObject, object, args[0]);
4278 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004279 // Compute attributes.
4280 PropertyAttributes attributes = NONE;
4281 if (args.length() == 4) {
4282 CONVERT_CHECKED(Smi, value_obj, args[3]);
4283 int unchecked_value = value_obj->value();
4284 // Only attribute bits should be set.
4285 RUNTIME_ASSERT(
4286 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4287 attributes = static_cast<PropertyAttributes>(unchecked_value);
4288 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004289
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004290 return object->
4291 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004292}
4293
4294
4295static Object* Runtime_DeleteProperty(Arguments args) {
4296 NoHandleAllocation ha;
4297 ASSERT(args.length() == 2);
4298
4299 CONVERT_CHECKED(JSObject, object, args[0]);
4300 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00004301 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004302}
4303
4304
ager@chromium.org9085a012009-05-11 19:22:57 +00004305static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
4306 Handle<String> key) {
4307 if (object->HasLocalProperty(*key)) return Heap::true_value();
4308 // Handle hidden prototypes. If there's a hidden prototype above this thing
4309 // then we have to check it for properties, because they are supposed to
4310 // look like they are on this object.
4311 Handle<Object> proto(object->GetPrototype());
4312 if (proto->IsJSObject() &&
4313 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
4314 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
4315 }
4316 return Heap::false_value();
4317}
4318
4319
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004320static Object* Runtime_HasLocalProperty(Arguments args) {
4321 NoHandleAllocation ha;
4322 ASSERT(args.length() == 2);
4323 CONVERT_CHECKED(String, key, args[1]);
4324
ager@chromium.org9085a012009-05-11 19:22:57 +00004325 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004326 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00004327 if (obj->IsJSObject()) {
4328 JSObject* object = JSObject::cast(obj);
4329 // Fast case - no interceptors.
4330 if (object->HasRealNamedProperty(key)) return Heap::true_value();
4331 // Slow case. Either it's not there or we have an interceptor. We should
4332 // have handles for this kind of deal.
4333 HandleScope scope;
4334 return HasLocalPropertyImplementation(Handle<JSObject>(object),
4335 Handle<String>(key));
4336 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004337 // Well, there is one exception: Handle [] on strings.
4338 uint32_t index;
4339 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00004340 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004341 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004342 return Heap::true_value();
4343 }
4344 }
4345 return Heap::false_value();
4346}
4347
4348
4349static Object* Runtime_HasProperty(Arguments args) {
4350 NoHandleAllocation na;
4351 ASSERT(args.length() == 2);
4352
4353 // Only JS objects can have properties.
4354 if (args[0]->IsJSObject()) {
4355 JSObject* object = JSObject::cast(args[0]);
4356 CONVERT_CHECKED(String, key, args[1]);
4357 if (object->HasProperty(key)) return Heap::true_value();
4358 }
4359 return Heap::false_value();
4360}
4361
4362
4363static Object* Runtime_HasElement(Arguments args) {
4364 NoHandleAllocation na;
4365 ASSERT(args.length() == 2);
4366
4367 // Only JS objects can have elements.
4368 if (args[0]->IsJSObject()) {
4369 JSObject* object = JSObject::cast(args[0]);
4370 CONVERT_CHECKED(Smi, index_obj, args[1]);
4371 uint32_t index = index_obj->value();
4372 if (object->HasElement(index)) return Heap::true_value();
4373 }
4374 return Heap::false_value();
4375}
4376
4377
4378static Object* Runtime_IsPropertyEnumerable(Arguments args) {
4379 NoHandleAllocation ha;
4380 ASSERT(args.length() == 2);
4381
4382 CONVERT_CHECKED(JSObject, object, args[0]);
4383 CONVERT_CHECKED(String, key, args[1]);
4384
4385 uint32_t index;
4386 if (key->AsArrayIndex(&index)) {
4387 return Heap::ToBoolean(object->HasElement(index));
4388 }
4389
ager@chromium.org870a0b62008-11-04 11:43:05 +00004390 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
4391 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004392}
4393
4394
4395static Object* Runtime_GetPropertyNames(Arguments args) {
4396 HandleScope scope;
4397 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004398 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004399 return *GetKeysFor(object);
4400}
4401
4402
4403// Returns either a FixedArray as Runtime_GetPropertyNames,
4404// or, if the given object has an enum cache that contains
4405// all enumerable properties of the object and its prototypes
4406// have none, the map of the object. This is used to speed up
4407// the check for deletions during a for-in.
4408static Object* Runtime_GetPropertyNamesFast(Arguments args) {
4409 ASSERT(args.length() == 1);
4410
4411 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4412
4413 if (raw_object->IsSimpleEnum()) return raw_object->map();
4414
4415 HandleScope scope;
4416 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004417 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
4418 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004419
4420 // Test again, since cache may have been built by preceding call.
4421 if (object->IsSimpleEnum()) return object->map();
4422
4423 return *content;
4424}
4425
4426
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004427// Find the length of the prototype chain that is to to handled as one. If a
4428// prototype object is hidden it is to be viewed as part of the the object it
4429// is prototype for.
4430static int LocalPrototypeChainLength(JSObject* obj) {
4431 int count = 1;
4432 Object* proto = obj->GetPrototype();
4433 while (proto->IsJSObject() &&
4434 JSObject::cast(proto)->map()->is_hidden_prototype()) {
4435 count++;
4436 proto = JSObject::cast(proto)->GetPrototype();
4437 }
4438 return count;
4439}
4440
4441
4442// Return the names of the local named properties.
4443// args[0]: object
4444static Object* Runtime_GetLocalPropertyNames(Arguments args) {
4445 HandleScope scope;
4446 ASSERT(args.length() == 1);
4447 if (!args[0]->IsJSObject()) {
4448 return Heap::undefined_value();
4449 }
4450 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4451
4452 // Skip the global proxy as it has no properties and always delegates to the
4453 // real global object.
4454 if (obj->IsJSGlobalProxy()) {
4455 // Only collect names if access is permitted.
4456 if (obj->IsAccessCheckNeeded() &&
4457 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
4458 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
4459 return *Factory::NewJSArray(0);
4460 }
4461 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
4462 }
4463
4464 // Find the number of objects making up this.
4465 int length = LocalPrototypeChainLength(*obj);
4466
4467 // Find the number of local properties for each of the objects.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004468 ScopedVector<int> local_property_count(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004469 int total_property_count = 0;
4470 Handle<JSObject> jsproto = obj;
4471 for (int i = 0; i < length; i++) {
4472 // Only collect names if access is permitted.
4473 if (jsproto->IsAccessCheckNeeded() &&
4474 !Top::MayNamedAccess(*jsproto,
4475 Heap::undefined_value(),
4476 v8::ACCESS_KEYS)) {
4477 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
4478 return *Factory::NewJSArray(0);
4479 }
4480 int n;
4481 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4482 local_property_count[i] = n;
4483 total_property_count += n;
4484 if (i < length - 1) {
4485 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4486 }
4487 }
4488
4489 // Allocate an array with storage for all the property names.
4490 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
4491
4492 // Get the property names.
4493 jsproto = obj;
4494 int proto_with_hidden_properties = 0;
4495 for (int i = 0; i < length; i++) {
4496 jsproto->GetLocalPropertyNames(*names,
4497 i == 0 ? 0 : local_property_count[i - 1]);
4498 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
4499 proto_with_hidden_properties++;
4500 }
4501 if (i < length - 1) {
4502 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4503 }
4504 }
4505
4506 // Filter out name of hidden propeties object.
4507 if (proto_with_hidden_properties > 0) {
4508 Handle<FixedArray> old_names = names;
4509 names = Factory::NewFixedArray(
4510 names->length() - proto_with_hidden_properties);
4511 int dest_pos = 0;
4512 for (int i = 0; i < total_property_count; i++) {
4513 Object* name = old_names->get(i);
4514 if (name == Heap::hidden_symbol()) {
4515 continue;
4516 }
4517 names->set(dest_pos++, name);
4518 }
4519 }
4520
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004521 return *Factory::NewJSArrayWithElements(names);
4522}
4523
4524
4525// Return the names of the local indexed properties.
4526// args[0]: object
4527static Object* Runtime_GetLocalElementNames(Arguments args) {
4528 HandleScope scope;
4529 ASSERT(args.length() == 1);
4530 if (!args[0]->IsJSObject()) {
4531 return Heap::undefined_value();
4532 }
4533 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4534
4535 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4536 Handle<FixedArray> names = Factory::NewFixedArray(n);
4537 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4538 return *Factory::NewJSArrayWithElements(names);
4539}
4540
4541
4542// Return information on whether an object has a named or indexed interceptor.
4543// args[0]: object
4544static Object* Runtime_GetInterceptorInfo(Arguments args) {
4545 HandleScope scope;
4546 ASSERT(args.length() == 1);
4547 if (!args[0]->IsJSObject()) {
4548 return Smi::FromInt(0);
4549 }
4550 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4551
4552 int result = 0;
4553 if (obj->HasNamedInterceptor()) result |= 2;
4554 if (obj->HasIndexedInterceptor()) result |= 1;
4555
4556 return Smi::FromInt(result);
4557}
4558
4559
4560// Return property names from named interceptor.
4561// args[0]: object
4562static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
4563 HandleScope scope;
4564 ASSERT(args.length() == 1);
4565 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4566
4567 if (obj->HasNamedInterceptor()) {
4568 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4569 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4570 }
4571 return Heap::undefined_value();
4572}
4573
4574
4575// Return element names from indexed interceptor.
4576// args[0]: object
4577static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
4578 HandleScope scope;
4579 ASSERT(args.length() == 1);
4580 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4581
4582 if (obj->HasIndexedInterceptor()) {
4583 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4584 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4585 }
4586 return Heap::undefined_value();
4587}
4588
4589
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004590static Object* Runtime_LocalKeys(Arguments args) {
4591 ASSERT_EQ(args.length(), 1);
4592 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4593 HandleScope scope;
4594 Handle<JSObject> object(raw_object);
4595 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
4596 LOCAL_ONLY);
4597 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
4598 // property array and since the result is mutable we have to create
4599 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004600 int length = contents->length();
4601 Handle<FixedArray> copy = Factory::NewFixedArray(length);
4602 for (int i = 0; i < length; i++) {
4603 Object* entry = contents->get(i);
4604 if (entry->IsString()) {
4605 copy->set(i, entry);
4606 } else {
4607 ASSERT(entry->IsNumber());
4608 HandleScope scope;
4609 Handle<Object> entry_handle(entry);
4610 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
4611 copy->set(i, *entry_str);
4612 }
4613 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004614 return *Factory::NewJSArrayWithElements(copy);
4615}
4616
4617
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004618static Object* Runtime_GetArgumentsProperty(Arguments args) {
4619 NoHandleAllocation ha;
4620 ASSERT(args.length() == 1);
4621
4622 // Compute the frame holding the arguments.
4623 JavaScriptFrameIterator it;
4624 it.AdvanceToArgumentsFrame();
4625 JavaScriptFrame* frame = it.frame();
4626
4627 // Get the actual number of provided arguments.
4628 const uint32_t n = frame->GetProvidedParametersCount();
4629
4630 // Try to convert the key to an index. If successful and within
4631 // index return the the argument from the frame.
4632 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004633 if (args[0]->ToArrayIndex(&index) && index < n) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004634 return frame->GetParameter(index);
4635 }
4636
4637 // Convert the key to a string.
4638 HandleScope scope;
4639 bool exception = false;
4640 Handle<Object> converted =
4641 Execution::ToString(args.at<Object>(0), &exception);
4642 if (exception) return Failure::Exception();
4643 Handle<String> key = Handle<String>::cast(converted);
4644
4645 // Try to convert the string key into an array index.
4646 if (key->AsArrayIndex(&index)) {
4647 if (index < n) {
4648 return frame->GetParameter(index);
4649 } else {
4650 return Top::initial_object_prototype()->GetElement(index);
4651 }
4652 }
4653
4654 // Handle special arguments properties.
4655 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
4656 if (key->Equals(Heap::callee_symbol())) return frame->function();
4657
4658 // Lookup in the initial Object.prototype object.
4659 return Top::initial_object_prototype()->GetProperty(*key);
4660}
4661
4662
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004663static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004664 HandleScope scope;
4665
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004666 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004667 Handle<Object> object = args.at<Object>(0);
4668 if (object->IsJSObject()) {
4669 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00004670 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
4671 js_object->TransformToFastProperties(0);
4672 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004673 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004674 return *object;
4675}
4676
4677
4678static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004679 HandleScope scope;
4680
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004681 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004682 Handle<Object> object = args.at<Object>(0);
4683 if (object->IsJSObject()) {
4684 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004685 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004686 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004687 return *object;
4688}
4689
4690
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004691static Object* Runtime_ToBool(Arguments args) {
4692 NoHandleAllocation ha;
4693 ASSERT(args.length() == 1);
4694
4695 return args[0]->ToBoolean();
4696}
4697
4698
4699// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
4700// Possible optimizations: put the type string into the oddballs.
4701static Object* Runtime_Typeof(Arguments args) {
4702 NoHandleAllocation ha;
4703
4704 Object* obj = args[0];
4705 if (obj->IsNumber()) return Heap::number_symbol();
4706 HeapObject* heap_obj = HeapObject::cast(obj);
4707
4708 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004709 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004710
4711 InstanceType instance_type = heap_obj->map()->instance_type();
4712 if (instance_type < FIRST_NONSTRING_TYPE) {
4713 return Heap::string_symbol();
4714 }
4715
4716 switch (instance_type) {
4717 case ODDBALL_TYPE:
4718 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
4719 return Heap::boolean_symbol();
4720 }
4721 if (heap_obj->IsNull()) {
4722 return Heap::object_symbol();
4723 }
4724 ASSERT(heap_obj->IsUndefined());
4725 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004726 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004727 return Heap::function_symbol();
4728 default:
4729 // For any kind of object not handled above, the spec rule for
4730 // host objects gives that it is okay to return "object"
4731 return Heap::object_symbol();
4732 }
4733}
4734
4735
lrn@chromium.org25156de2010-04-06 13:10:27 +00004736static bool AreDigits(const char*s, int from, int to) {
4737 for (int i = from; i < to; i++) {
4738 if (s[i] < '0' || s[i] > '9') return false;
4739 }
4740
4741 return true;
4742}
4743
4744
4745static int ParseDecimalInteger(const char*s, int from, int to) {
4746 ASSERT(to - from < 10); // Overflow is not possible.
4747 ASSERT(from < to);
4748 int d = s[from] - '0';
4749
4750 for (int i = from + 1; i < to; i++) {
4751 d = 10 * d + (s[i] - '0');
4752 }
4753
4754 return d;
4755}
4756
4757
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004758static Object* Runtime_StringToNumber(Arguments args) {
4759 NoHandleAllocation ha;
4760 ASSERT(args.length() == 1);
4761 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004762 subject->TryFlatten();
lrn@chromium.org25156de2010-04-06 13:10:27 +00004763
4764 // Fast case: short integer or some sorts of junk values.
4765 int len = subject->length();
4766 if (subject->IsSeqAsciiString()) {
4767 if (len == 0) return Smi::FromInt(0);
4768
4769 char const* data = SeqAsciiString::cast(subject)->GetChars();
4770 bool minus = (data[0] == '-');
4771 int start_pos = (minus ? 1 : 0);
4772
4773 if (start_pos == len) {
4774 return Heap::nan_value();
4775 } else if (data[start_pos] > '9') {
4776 // Fast check for a junk value. A valid string may start from a
4777 // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit or
4778 // the 'I' character ('Infinity'). All of that have codes not greater than
4779 // '9' except 'I'.
4780 if (data[start_pos] != 'I') {
4781 return Heap::nan_value();
4782 }
4783 } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
4784 // The maximal/minimal smi has 10 digits. If the string has less digits we
4785 // know it will fit into the smi-data type.
4786 int d = ParseDecimalInteger(data, start_pos, len);
4787 if (minus) {
4788 if (d == 0) return Heap::minus_zero_value();
4789 d = -d;
4790 }
4791 return Smi::FromInt(d);
4792 }
4793 }
4794
4795 // Slower case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004796 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
4797}
4798
4799
4800static Object* Runtime_StringFromCharCodeArray(Arguments args) {
4801 NoHandleAllocation ha;
4802 ASSERT(args.length() == 1);
4803
4804 CONVERT_CHECKED(JSArray, codes, args[0]);
4805 int length = Smi::cast(codes->length())->value();
4806
4807 // Check if the string can be ASCII.
4808 int i;
4809 for (i = 0; i < length; i++) {
4810 Object* element = codes->GetElement(i);
4811 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
4812 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
4813 break;
4814 }
4815
4816 Object* object = NULL;
4817 if (i == length) { // The string is ASCII.
4818 object = Heap::AllocateRawAsciiString(length);
4819 } else { // The string is not ASCII.
4820 object = Heap::AllocateRawTwoByteString(length);
4821 }
4822
4823 if (object->IsFailure()) return object;
4824 String* result = String::cast(object);
4825 for (int i = 0; i < length; i++) {
4826 Object* element = codes->GetElement(i);
4827 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004828 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004829 }
4830 return result;
4831}
4832
4833
4834// kNotEscaped is generated by the following:
4835//
4836// #!/bin/perl
4837// for (my $i = 0; $i < 256; $i++) {
4838// print "\n" if $i % 16 == 0;
4839// my $c = chr($i);
4840// my $escaped = 1;
4841// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
4842// print $escaped ? "0, " : "1, ";
4843// }
4844
4845
4846static bool IsNotEscaped(uint16_t character) {
4847 // Only for 8 bit characters, the rest are always escaped (in a different way)
4848 ASSERT(character < 256);
4849 static const char kNotEscaped[256] = {
4850 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4851 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4852 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
4853 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
4854 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4855 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
4856 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4857 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
4858 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4859 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4860 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4861 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4862 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4863 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4864 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4865 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4866 };
4867 return kNotEscaped[character] != 0;
4868}
4869
4870
4871static Object* Runtime_URIEscape(Arguments args) {
4872 const char hex_chars[] = "0123456789ABCDEF";
4873 NoHandleAllocation ha;
4874 ASSERT(args.length() == 1);
4875 CONVERT_CHECKED(String, source, args[0]);
4876
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004877 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004878
4879 int escaped_length = 0;
4880 int length = source->length();
4881 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004882 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004883 buffer->Reset(source);
4884 while (buffer->has_more()) {
4885 uint16_t character = buffer->GetNext();
4886 if (character >= 256) {
4887 escaped_length += 6;
4888 } else if (IsNotEscaped(character)) {
4889 escaped_length++;
4890 } else {
4891 escaped_length += 3;
4892 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004893 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004894 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004895 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004896 Top::context()->mark_out_of_memory();
4897 return Failure::OutOfMemoryException();
4898 }
4899 }
4900 }
4901 // No length change implies no change. Return original string if no change.
4902 if (escaped_length == length) {
4903 return source;
4904 }
4905 Object* o = Heap::AllocateRawAsciiString(escaped_length);
4906 if (o->IsFailure()) return o;
4907 String* destination = String::cast(o);
4908 int dest_position = 0;
4909
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004910 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004911 buffer->Rewind();
4912 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00004913 uint16_t chr = buffer->GetNext();
4914 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004915 destination->Set(dest_position, '%');
4916 destination->Set(dest_position+1, 'u');
4917 destination->Set(dest_position+2, hex_chars[chr >> 12]);
4918 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
4919 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
4920 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004921 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00004922 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004923 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004924 dest_position++;
4925 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004926 destination->Set(dest_position, '%');
4927 destination->Set(dest_position+1, hex_chars[chr >> 4]);
4928 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004929 dest_position += 3;
4930 }
4931 }
4932 return destination;
4933}
4934
4935
4936static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
4937 static const signed char kHexValue['g'] = {
4938 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4939 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4940 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4941 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
4942 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4943 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4944 -1, 10, 11, 12, 13, 14, 15 };
4945
4946 if (character1 > 'f') return -1;
4947 int hi = kHexValue[character1];
4948 if (hi == -1) return -1;
4949 if (character2 > 'f') return -1;
4950 int lo = kHexValue[character2];
4951 if (lo == -1) return -1;
4952 return (hi << 4) + lo;
4953}
4954
4955
ager@chromium.org870a0b62008-11-04 11:43:05 +00004956static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004957 int i,
4958 int length,
4959 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004960 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00004961 int32_t hi = 0;
4962 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004963 if (character == '%' &&
4964 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004965 source->Get(i + 1) == 'u' &&
4966 (hi = TwoDigitHex(source->Get(i + 2),
4967 source->Get(i + 3))) != -1 &&
4968 (lo = TwoDigitHex(source->Get(i + 4),
4969 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004970 *step = 6;
4971 return (hi << 8) + lo;
4972 } else if (character == '%' &&
4973 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004974 (lo = TwoDigitHex(source->Get(i + 1),
4975 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004976 *step = 3;
4977 return lo;
4978 } else {
4979 *step = 1;
4980 return character;
4981 }
4982}
4983
4984
4985static Object* Runtime_URIUnescape(Arguments args) {
4986 NoHandleAllocation ha;
4987 ASSERT(args.length() == 1);
4988 CONVERT_CHECKED(String, source, args[0]);
4989
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004990 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004991
4992 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004993 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004994
4995 int unescaped_length = 0;
4996 for (int i = 0; i < length; unescaped_length++) {
4997 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004998 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004999 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005000 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005001 i += step;
5002 }
5003
5004 // No length change implies no change. Return original string if no change.
5005 if (unescaped_length == length)
5006 return source;
5007
5008 Object* o = ascii ?
5009 Heap::AllocateRawAsciiString(unescaped_length) :
5010 Heap::AllocateRawTwoByteString(unescaped_length);
5011 if (o->IsFailure()) return o;
5012 String* destination = String::cast(o);
5013
5014 int dest_position = 0;
5015 for (int i = 0; i < length; dest_position++) {
5016 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005017 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005018 i += step;
5019 }
5020 return destination;
5021}
5022
5023
5024static Object* Runtime_StringParseInt(Arguments args) {
5025 NoHandleAllocation ha;
5026
5027 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005028 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005029
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005030 s->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005031
lrn@chromium.org25156de2010-04-06 13:10:27 +00005032 RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
5033 double value = StringToInt(s, radix);
5034 return Heap::NumberFromDouble(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005035 return Heap::nan_value();
5036}
5037
5038
5039static Object* Runtime_StringParseFloat(Arguments args) {
5040 NoHandleAllocation ha;
5041 CONVERT_CHECKED(String, str, args[0]);
5042
5043 // ECMA-262 section 15.1.2.3, empty string is NaN
5044 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
5045
5046 // Create a number object from the value.
5047 return Heap::NumberFromDouble(value);
5048}
5049
5050
5051static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
5052static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
5053
5054
5055template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005056static Object* ConvertCaseHelper(String* s,
5057 int length,
5058 int input_string_length,
5059 unibrow::Mapping<Converter, 128>* mapping) {
5060 // We try this twice, once with the assumption that the result is no longer
5061 // than the input and, if that assumption breaks, again with the exact
5062 // length. This may not be pretty, but it is nicer than what was here before
5063 // and I hereby claim my vaffel-is.
5064 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005065 // Allocate the resulting string.
5066 //
5067 // NOTE: This assumes that the upper/lower case of an ascii
5068 // character is also ascii. This is currently the case, but it
5069 // might break in the future if we implement more context and locale
5070 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00005071 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005072 ? Heap::AllocateRawAsciiString(length)
5073 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005074 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005075 String* result = String::cast(o);
5076 bool has_changed_character = false;
5077
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005078 // Convert all characters to upper case, assuming that they will fit
5079 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005080 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005081 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005082 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005083 // We can assume that the string is not empty
5084 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005085 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00005086 bool has_next = buffer->has_more();
5087 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005088 int char_length = mapping->get(current, next, chars);
5089 if (char_length == 0) {
5090 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005091 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005092 i++;
5093 } else if (char_length == 1) {
5094 // Common case: converting the letter resulted in one character.
5095 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005096 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005097 has_changed_character = true;
5098 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005099 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005100 // We've assumed that the result would be as long as the
5101 // input but here is a character that converts to several
5102 // characters. No matter, we calculate the exact length
5103 // of the result and try the whole thing again.
5104 //
5105 // Note that this leaves room for optimization. We could just
5106 // memcpy what we already have to the result string. Also,
5107 // the result string is the last object allocated we could
5108 // "realloc" it and probably, in the vast majority of cases,
5109 // extend the existing string to be able to hold the full
5110 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00005111 int next_length = 0;
5112 if (has_next) {
5113 next_length = mapping->get(next, 0, chars);
5114 if (next_length == 0) next_length = 1;
5115 }
5116 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005117 while (buffer->has_more()) {
5118 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00005119 // NOTE: we use 0 as the next character here because, while
5120 // the next character may affect what a character converts to,
5121 // it does not in any case affect the length of what it convert
5122 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005123 int char_length = mapping->get(current, 0, chars);
5124 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00005125 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005126 if (current_length > Smi::kMaxValue) {
5127 Top::context()->mark_out_of_memory();
5128 return Failure::OutOfMemoryException();
5129 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005130 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005131 // Try again with the real length.
5132 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005133 } else {
5134 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005135 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005136 i++;
5137 }
5138 has_changed_character = true;
5139 }
5140 current = next;
5141 }
5142 if (has_changed_character) {
5143 return result;
5144 } else {
5145 // If we didn't actually change anything in doing the conversion
5146 // we simple return the result and let the converted string
5147 // become garbage; there is no reason to keep two identical strings
5148 // alive.
5149 return s;
5150 }
5151}
5152
5153
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005154namespace {
5155
5156struct ToLowerTraits {
5157 typedef unibrow::ToLowercase UnibrowConverter;
5158
5159 static bool ConvertAscii(char* dst, char* src, int length) {
5160 bool changed = false;
5161 for (int i = 0; i < length; ++i) {
5162 char c = src[i];
5163 if ('A' <= c && c <= 'Z') {
5164 c += ('a' - 'A');
5165 changed = true;
5166 }
5167 dst[i] = c;
5168 }
5169 return changed;
5170 }
5171};
5172
5173
5174struct ToUpperTraits {
5175 typedef unibrow::ToUppercase UnibrowConverter;
5176
5177 static bool ConvertAscii(char* dst, char* src, int length) {
5178 bool changed = false;
5179 for (int i = 0; i < length; ++i) {
5180 char c = src[i];
5181 if ('a' <= c && c <= 'z') {
5182 c -= ('a' - 'A');
5183 changed = true;
5184 }
5185 dst[i] = c;
5186 }
5187 return changed;
5188 }
5189};
5190
5191} // namespace
5192
5193
5194template <typename ConvertTraits>
5195static Object* ConvertCase(
5196 Arguments args,
5197 unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005198 NoHandleAllocation ha;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005199 CONVERT_CHECKED(String, s, args[0]);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005200 s = s->TryFlattenGetString();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005201
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005202 const int length = s->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005203 // Assume that the string is not empty; we need this assumption later
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005204 if (length == 0) return s;
5205
5206 // Simpler handling of ascii strings.
5207 //
5208 // NOTE: This assumes that the upper/lower case of an ascii
5209 // character is also ascii. This is currently the case, but it
5210 // might break in the future if we implement more context and locale
5211 // dependent upper/lower conversions.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005212 if (s->IsSeqAsciiString()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005213 Object* o = Heap::AllocateRawAsciiString(length);
5214 if (o->IsFailure()) return o;
5215 SeqAsciiString* result = SeqAsciiString::cast(o);
5216 bool has_changed_character = ConvertTraits::ConvertAscii(
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005217 result->GetChars(), SeqAsciiString::cast(s)->GetChars(), length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005218 return has_changed_character ? result : s;
5219 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005220
5221 Object* answer = ConvertCaseHelper(s, length, length, mapping);
5222 if (answer->IsSmi()) {
5223 // Retry with correct length.
5224 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
5225 }
5226 return answer; // This may be a failure.
5227}
5228
5229
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005230static Object* Runtime_StringToLowerCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005231 return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005232}
5233
5234
5235static Object* Runtime_StringToUpperCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005236 return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005237}
5238
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005239
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005240static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
5241 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
5242}
5243
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005244
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005245static Object* Runtime_StringTrim(Arguments args) {
5246 NoHandleAllocation ha;
5247 ASSERT(args.length() == 3);
5248
5249 CONVERT_CHECKED(String, s, args[0]);
5250 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
5251 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
5252
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005253 s->TryFlatten();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005254 int length = s->length();
5255
5256 int left = 0;
5257 if (trimLeft) {
5258 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
5259 left++;
5260 }
5261 }
5262
5263 int right = length;
5264 if (trimRight) {
5265 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
5266 right--;
5267 }
5268 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005269 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005270}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005271
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005272
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005273template <typename schar, typename pchar>
5274void FindStringIndices(Vector<const schar> subject,
5275 Vector<const pchar> pattern,
5276 ZoneList<int>* indices,
5277 unsigned int limit) {
5278 ASSERT(limit > 0);
5279 // Collect indices of pattern in subject, and the end-of-string index.
5280 // Stop after finding at most limit values.
5281 StringSearchStrategy strategy =
5282 InitializeStringSearch(pattern, sizeof(schar) == 1);
5283 switch (strategy) {
5284 case SEARCH_FAIL: return;
5285 case SEARCH_SHORT: {
5286 int pattern_length = pattern.length();
5287 int index = 0;
5288 while (limit > 0) {
5289 index = SimpleIndexOf(subject, pattern, index);
5290 if (index < 0) return;
5291 indices->Add(index);
5292 index += pattern_length;
5293 limit--;
5294 }
5295 return;
5296 }
5297 case SEARCH_LONG: {
5298 int pattern_length = pattern.length();
5299 int index = 0;
5300 while (limit > 0) {
5301 index = ComplexIndexOf(subject, pattern, index);
5302 if (index < 0) return;
5303 indices->Add(index);
5304 index += pattern_length;
5305 limit--;
5306 }
5307 return;
5308 }
5309 default:
5310 UNREACHABLE();
5311 return;
5312 }
5313}
5314
5315template <typename schar>
5316inline void FindCharIndices(Vector<const schar> subject,
5317 const schar pattern_char,
5318 ZoneList<int>* indices,
5319 unsigned int limit) {
5320 // Collect indices of pattern_char in subject, and the end-of-string index.
5321 // Stop after finding at most limit values.
5322 int index = 0;
5323 while (limit > 0) {
5324 index = SingleCharIndexOf(subject, pattern_char, index);
5325 if (index < 0) return;
5326 indices->Add(index);
5327 index++;
5328 limit--;
5329 }
5330}
5331
5332
5333static Object* Runtime_StringSplit(Arguments args) {
5334 ASSERT(args.length() == 3);
5335 HandleScope handle_scope;
5336 CONVERT_ARG_CHECKED(String, subject, 0);
5337 CONVERT_ARG_CHECKED(String, pattern, 1);
5338 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
5339
5340 int subject_length = subject->length();
5341 int pattern_length = pattern->length();
5342 RUNTIME_ASSERT(pattern_length > 0);
5343
5344 // The limit can be very large (0xffffffffu), but since the pattern
5345 // isn't empty, we can never create more parts than ~half the length
5346 // of the subject.
5347
5348 if (!subject->IsFlat()) FlattenString(subject);
5349
5350 static const int kMaxInitialListCapacity = 16;
5351
5352 ZoneScope scope(DELETE_ON_EXIT);
5353
5354 // Find (up to limit) indices of separator and end-of-string in subject
5355 int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
5356 ZoneList<int> indices(initial_capacity);
5357 if (pattern_length == 1) {
5358 // Special case, go directly to fast single-character split.
5359 AssertNoAllocation nogc;
5360 uc16 pattern_char = pattern->Get(0);
5361 if (subject->IsTwoByteRepresentation()) {
5362 FindCharIndices(subject->ToUC16Vector(), pattern_char,
5363 &indices,
5364 limit);
5365 } else if (pattern_char <= String::kMaxAsciiCharCode) {
5366 FindCharIndices(subject->ToAsciiVector(),
5367 static_cast<char>(pattern_char),
5368 &indices,
5369 limit);
5370 }
5371 } else {
5372 if (!pattern->IsFlat()) FlattenString(pattern);
5373 AssertNoAllocation nogc;
5374 if (subject->IsAsciiRepresentation()) {
5375 Vector<const char> subject_vector = subject->ToAsciiVector();
5376 if (pattern->IsAsciiRepresentation()) {
5377 FindStringIndices(subject_vector,
5378 pattern->ToAsciiVector(),
5379 &indices,
5380 limit);
5381 } else {
5382 FindStringIndices(subject_vector,
5383 pattern->ToUC16Vector(),
5384 &indices,
5385 limit);
5386 }
5387 } else {
5388 Vector<const uc16> subject_vector = subject->ToUC16Vector();
5389 if (pattern->IsAsciiRepresentation()) {
5390 FindStringIndices(subject_vector,
5391 pattern->ToAsciiVector(),
5392 &indices,
5393 limit);
5394 } else {
5395 FindStringIndices(subject_vector,
5396 pattern->ToUC16Vector(),
5397 &indices,
5398 limit);
5399 }
5400 }
5401 }
5402 if (static_cast<uint32_t>(indices.length()) < limit) {
5403 indices.Add(subject_length);
5404 }
5405 // The list indices now contains the end of each part to create.
5406
5407
5408 // Create JSArray of substrings separated by separator.
5409 int part_count = indices.length();
5410
5411 Handle<JSArray> result = Factory::NewJSArray(part_count);
5412 result->set_length(Smi::FromInt(part_count));
5413
5414 ASSERT(result->HasFastElements());
5415
5416 if (part_count == 1 && indices.at(0) == subject_length) {
5417 FixedArray::cast(result->elements())->set(0, *subject);
5418 return *result;
5419 }
5420
5421 Handle<FixedArray> elements(FixedArray::cast(result->elements()));
5422 int part_start = 0;
5423 for (int i = 0; i < part_count; i++) {
5424 HandleScope local_loop_handle;
5425 int part_end = indices.at(i);
5426 Handle<String> substring =
5427 Factory::NewSubString(subject, part_start, part_end);
5428 elements->set(i, *substring);
5429 part_start = part_end + pattern_length;
5430 }
5431
5432 return *result;
5433}
5434
5435
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005436// Copies ascii characters to the given fixed array looking up
5437// one-char strings in the cache. Gives up on the first char that is
5438// not in the cache and fills the remainder with smi zeros. Returns
5439// the length of the successfully copied prefix.
5440static int CopyCachedAsciiCharsToArray(const char* chars,
5441 FixedArray* elements,
5442 int length) {
5443 AssertNoAllocation nogc;
5444 FixedArray* ascii_cache = Heap::single_character_string_cache();
5445 Object* undefined = Heap::undefined_value();
5446 int i;
5447 for (i = 0; i < length; ++i) {
5448 Object* value = ascii_cache->get(chars[i]);
5449 if (value == undefined) break;
5450 ASSERT(!Heap::InNewSpace(value));
5451 elements->set(i, value, SKIP_WRITE_BARRIER);
5452 }
5453 if (i < length) {
5454 ASSERT(Smi::FromInt(0) == 0);
5455 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
5456 }
5457#ifdef DEBUG
5458 for (int j = 0; j < length; ++j) {
5459 Object* element = elements->get(j);
5460 ASSERT(element == Smi::FromInt(0) ||
5461 (element->IsString() && String::cast(element)->LooksValid()));
5462 }
5463#endif
5464 return i;
5465}
5466
5467
5468// Converts a String to JSArray.
5469// For example, "foo" => ["f", "o", "o"].
5470static Object* Runtime_StringToArray(Arguments args) {
5471 HandleScope scope;
5472 ASSERT(args.length() == 1);
5473 CONVERT_ARG_CHECKED(String, s, 0);
5474
5475 s->TryFlatten();
5476 const int length = s->length();
5477
5478 Handle<FixedArray> elements;
5479 if (s->IsFlat() && s->IsAsciiRepresentation()) {
5480 Object* obj = Heap::AllocateUninitializedFixedArray(length);
5481 if (obj->IsFailure()) return obj;
5482 elements = Handle<FixedArray>(FixedArray::cast(obj));
5483
5484 Vector<const char> chars = s->ToAsciiVector();
5485 // Note, this will initialize all elements (not only the prefix)
5486 // to prevent GC from seeing partially initialized array.
5487 int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
5488 *elements,
5489 length);
5490
5491 for (int i = num_copied_from_cache; i < length; ++i) {
5492 elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
5493 }
5494 } else {
5495 elements = Factory::NewFixedArray(length);
5496 for (int i = 0; i < length; ++i) {
5497 elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
5498 }
5499 }
5500
5501#ifdef DEBUG
5502 for (int i = 0; i < length; ++i) {
5503 ASSERT(String::cast(elements->get(i))->length() == 1);
5504 }
5505#endif
5506
5507 return *Factory::NewJSArrayWithElements(elements);
5508}
5509
5510
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00005511bool Runtime::IsUpperCaseChar(uint16_t ch) {
5512 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
5513 int char_length = to_upper_mapping.get(ch, 0, chars);
5514 return char_length == 0;
5515}
5516
5517
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005518static Object* Runtime_NumberToString(Arguments args) {
5519 NoHandleAllocation ha;
5520 ASSERT(args.length() == 1);
5521
5522 Object* number = args[0];
5523 RUNTIME_ASSERT(number->IsNumber());
5524
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005525 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005526}
5527
5528
ager@chromium.org357bf652010-04-12 11:30:10 +00005529static Object* Runtime_NumberToStringSkipCache(Arguments args) {
5530 NoHandleAllocation ha;
5531 ASSERT(args.length() == 1);
5532
5533 Object* number = args[0];
5534 RUNTIME_ASSERT(number->IsNumber());
5535
5536 return Heap::NumberToString(number, false);
5537}
5538
5539
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005540static Object* Runtime_NumberToInteger(Arguments args) {
5541 NoHandleAllocation ha;
5542 ASSERT(args.length() == 1);
5543
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005544 CONVERT_DOUBLE_CHECKED(number, args[0]);
5545
5546 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5547 if (number > 0 && number <= Smi::kMaxValue) {
5548 return Smi::FromInt(static_cast<int>(number));
5549 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005550 return Heap::NumberFromDouble(DoubleToInteger(number));
5551}
5552
5553
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005554static Object* Runtime_NumberToIntegerMapMinusZero(Arguments args) {
5555 NoHandleAllocation ha;
5556 ASSERT(args.length() == 1);
5557
5558 CONVERT_DOUBLE_CHECKED(number, args[0]);
5559
5560 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5561 if (number > 0 && number <= Smi::kMaxValue) {
5562 return Smi::FromInt(static_cast<int>(number));
5563 }
5564
5565 double double_value = DoubleToInteger(number);
5566 // Map both -0 and +0 to +0.
5567 if (double_value == 0) double_value = 0;
5568
5569 return Heap::NumberFromDouble(double_value);
5570}
5571
5572
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005573static Object* Runtime_NumberToJSUint32(Arguments args) {
5574 NoHandleAllocation ha;
5575 ASSERT(args.length() == 1);
5576
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005577 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005578 return Heap::NumberFromUint32(number);
5579}
5580
5581
5582static Object* Runtime_NumberToJSInt32(Arguments args) {
5583 NoHandleAllocation ha;
5584 ASSERT(args.length() == 1);
5585
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005586 CONVERT_DOUBLE_CHECKED(number, args[0]);
5587
5588 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5589 if (number > 0 && number <= Smi::kMaxValue) {
5590 return Smi::FromInt(static_cast<int>(number));
5591 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005592 return Heap::NumberFromInt32(DoubleToInt32(number));
5593}
5594
5595
ager@chromium.org870a0b62008-11-04 11:43:05 +00005596// Converts a Number to a Smi, if possible. Returns NaN if the number is not
5597// a small integer.
5598static Object* Runtime_NumberToSmi(Arguments args) {
5599 NoHandleAllocation ha;
5600 ASSERT(args.length() == 1);
5601
5602 Object* obj = args[0];
5603 if (obj->IsSmi()) {
5604 return obj;
5605 }
5606 if (obj->IsHeapNumber()) {
5607 double value = HeapNumber::cast(obj)->value();
5608 int int_value = FastD2I(value);
5609 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
5610 return Smi::FromInt(int_value);
5611 }
5612 }
5613 return Heap::nan_value();
5614}
5615
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005616
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005617static Object* Runtime_NumberAdd(Arguments args) {
5618 NoHandleAllocation ha;
5619 ASSERT(args.length() == 2);
5620
5621 CONVERT_DOUBLE_CHECKED(x, args[0]);
5622 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005623 return Heap::NumberFromDouble(x + y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005624}
5625
5626
5627static Object* Runtime_NumberSub(Arguments args) {
5628 NoHandleAllocation ha;
5629 ASSERT(args.length() == 2);
5630
5631 CONVERT_DOUBLE_CHECKED(x, args[0]);
5632 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005633 return Heap::NumberFromDouble(x - y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005634}
5635
5636
5637static Object* Runtime_NumberMul(Arguments args) {
5638 NoHandleAllocation ha;
5639 ASSERT(args.length() == 2);
5640
5641 CONVERT_DOUBLE_CHECKED(x, args[0]);
5642 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005643 return Heap::NumberFromDouble(x * y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005644}
5645
5646
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005647static Object* Runtime_NumberUnaryMinus(Arguments args) {
5648 NoHandleAllocation ha;
5649 ASSERT(args.length() == 1);
5650
5651 CONVERT_DOUBLE_CHECKED(x, args[0]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005652 return Heap::NumberFromDouble(-x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005653}
5654
5655
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00005656static Object* Runtime_NumberAlloc(Arguments args) {
5657 NoHandleAllocation ha;
5658 ASSERT(args.length() == 0);
5659
5660 return Heap::NumberFromDouble(9876543210.0);
5661}
5662
5663
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005664static Object* Runtime_NumberDiv(Arguments args) {
5665 NoHandleAllocation ha;
5666 ASSERT(args.length() == 2);
5667
5668 CONVERT_DOUBLE_CHECKED(x, args[0]);
5669 CONVERT_DOUBLE_CHECKED(y, args[1]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005670 return Heap::NumberFromDouble(x / y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005671}
5672
5673
5674static Object* Runtime_NumberMod(Arguments args) {
5675 NoHandleAllocation ha;
5676 ASSERT(args.length() == 2);
5677
5678 CONVERT_DOUBLE_CHECKED(x, args[0]);
5679 CONVERT_DOUBLE_CHECKED(y, args[1]);
5680
ager@chromium.org3811b432009-10-28 14:53:37 +00005681 x = modulo(x, y);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005682 // NumberFromDouble may return a Smi instead of a Number object
5683 return Heap::NumberFromDouble(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005684}
5685
5686
5687static Object* Runtime_StringAdd(Arguments args) {
5688 NoHandleAllocation ha;
5689 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005690 CONVERT_CHECKED(String, str1, args[0]);
5691 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005692 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00005693 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005694}
5695
5696
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005697template <typename sinkchar>
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005698static inline void StringBuilderConcatHelper(String* special,
5699 sinkchar* sink,
5700 FixedArray* fixed_array,
5701 int array_length) {
5702 int position = 0;
5703 for (int i = 0; i < array_length; i++) {
5704 Object* element = fixed_array->get(i);
5705 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005706 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005707 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005708 int pos;
5709 int len;
5710 if (encoded_slice > 0) {
5711 // Position and length encoded in one smi.
5712 pos = StringBuilderSubstringPosition::decode(encoded_slice);
5713 len = StringBuilderSubstringLength::decode(encoded_slice);
5714 } else {
5715 // Position and length encoded in two smis.
5716 Object* obj = fixed_array->get(++i);
5717 ASSERT(obj->IsSmi());
5718 pos = Smi::cast(obj)->value();
5719 len = -encoded_slice;
5720 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00005721 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00005722 sink + position,
5723 pos,
5724 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005725 position += len;
5726 } else {
5727 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005728 int element_length = string->length();
5729 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005730 position += element_length;
5731 }
5732 }
5733}
5734
5735
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005736static Object* Runtime_StringBuilderConcat(Arguments args) {
5737 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005738 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005739 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005740 if (!args[1]->IsSmi()) {
5741 Top::context()->mark_out_of_memory();
5742 return Failure::OutOfMemoryException();
5743 }
5744 int array_length = Smi::cast(args[1])->value();
5745 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005746
5747 // This assumption is used by the slice encoding in one or two smis.
5748 ASSERT(Smi::kMaxValue >= String::kMaxLength);
5749
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005750 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005751 if (!array->HasFastElements()) {
5752 return Top::Throw(Heap::illegal_argument_symbol());
5753 }
5754 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005755 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005756 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005757 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005758
5759 if (array_length == 0) {
5760 return Heap::empty_string();
5761 } else if (array_length == 1) {
5762 Object* first = fixed_array->get(0);
5763 if (first->IsString()) return first;
5764 }
5765
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005766 bool ascii = special->HasOnlyAsciiChars();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005767 int position = 0;
5768 for (int i = 0; i < array_length; i++) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005769 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005770 Object* elt = fixed_array->get(i);
5771 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005772 // Smi encoding of position and length.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005773 int smi_value = Smi::cast(elt)->value();
5774 int pos;
5775 int len;
5776 if (smi_value > 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005777 // Position and length encoded in one smi.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005778 pos = StringBuilderSubstringPosition::decode(smi_value);
5779 len = StringBuilderSubstringLength::decode(smi_value);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005780 } else {
5781 // Position and length encoded in two smis.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005782 len = -smi_value;
5783 // Get the position and check that it is a positive smi.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005784 i++;
5785 if (i >= array_length) {
5786 return Top::Throw(Heap::illegal_argument_symbol());
5787 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005788 Object* next_smi = fixed_array->get(i);
5789 if (!next_smi->IsSmi()) {
5790 return Top::Throw(Heap::illegal_argument_symbol());
5791 }
5792 pos = Smi::cast(next_smi)->value();
5793 if (pos < 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005794 return Top::Throw(Heap::illegal_argument_symbol());
5795 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005796 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005797 ASSERT(pos >= 0);
5798 ASSERT(len >= 0);
5799 if (pos > special_length || len > special_length - pos) {
5800 return Top::Throw(Heap::illegal_argument_symbol());
5801 }
5802 increment = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005803 } else if (elt->IsString()) {
5804 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005805 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005806 increment = element_length;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005807 if (ascii && !element->HasOnlyAsciiChars()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005808 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005809 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005810 } else {
5811 return Top::Throw(Heap::illegal_argument_symbol());
5812 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005813 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005814 Top::context()->mark_out_of_memory();
5815 return Failure::OutOfMemoryException();
5816 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005817 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005818 }
5819
5820 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005821 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005822
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005823 if (ascii) {
5824 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005825 if (object->IsFailure()) return object;
5826 SeqAsciiString* answer = SeqAsciiString::cast(object);
5827 StringBuilderConcatHelper(special,
5828 answer->GetChars(),
5829 fixed_array,
5830 array_length);
5831 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005832 } else {
5833 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005834 if (object->IsFailure()) return object;
5835 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
5836 StringBuilderConcatHelper(special,
5837 answer->GetChars(),
5838 fixed_array,
5839 array_length);
5840 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005841 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005842}
5843
5844
5845static Object* Runtime_NumberOr(Arguments args) {
5846 NoHandleAllocation ha;
5847 ASSERT(args.length() == 2);
5848
5849 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5850 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5851 return Heap::NumberFromInt32(x | y);
5852}
5853
5854
5855static Object* Runtime_NumberAnd(Arguments args) {
5856 NoHandleAllocation ha;
5857 ASSERT(args.length() == 2);
5858
5859 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5860 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5861 return Heap::NumberFromInt32(x & y);
5862}
5863
5864
5865static Object* Runtime_NumberXor(Arguments args) {
5866 NoHandleAllocation ha;
5867 ASSERT(args.length() == 2);
5868
5869 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5870 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5871 return Heap::NumberFromInt32(x ^ y);
5872}
5873
5874
5875static Object* Runtime_NumberNot(Arguments args) {
5876 NoHandleAllocation ha;
5877 ASSERT(args.length() == 1);
5878
5879 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5880 return Heap::NumberFromInt32(~x);
5881}
5882
5883
5884static Object* Runtime_NumberShl(Arguments args) {
5885 NoHandleAllocation ha;
5886 ASSERT(args.length() == 2);
5887
5888 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5889 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5890 return Heap::NumberFromInt32(x << (y & 0x1f));
5891}
5892
5893
5894static Object* Runtime_NumberShr(Arguments args) {
5895 NoHandleAllocation ha;
5896 ASSERT(args.length() == 2);
5897
5898 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
5899 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5900 return Heap::NumberFromUint32(x >> (y & 0x1f));
5901}
5902
5903
5904static Object* Runtime_NumberSar(Arguments args) {
5905 NoHandleAllocation ha;
5906 ASSERT(args.length() == 2);
5907
5908 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5909 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5910 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
5911}
5912
5913
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005914static Object* Runtime_NumberEquals(Arguments args) {
5915 NoHandleAllocation ha;
5916 ASSERT(args.length() == 2);
5917
5918 CONVERT_DOUBLE_CHECKED(x, args[0]);
5919 CONVERT_DOUBLE_CHECKED(y, args[1]);
5920 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
5921 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
5922 if (x == y) return Smi::FromInt(EQUAL);
5923 Object* result;
5924 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
5925 result = Smi::FromInt(EQUAL);
5926 } else {
5927 result = Smi::FromInt(NOT_EQUAL);
5928 }
5929 return result;
5930}
5931
5932
5933static Object* Runtime_StringEquals(Arguments args) {
5934 NoHandleAllocation ha;
5935 ASSERT(args.length() == 2);
5936
5937 CONVERT_CHECKED(String, x, args[0]);
5938 CONVERT_CHECKED(String, y, args[1]);
5939
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005940 bool not_equal = !x->Equals(y);
5941 // This is slightly convoluted because the value that signifies
5942 // equality is 0 and inequality is 1 so we have to negate the result
5943 // from String::Equals.
5944 ASSERT(not_equal == 0 || not_equal == 1);
5945 STATIC_CHECK(EQUAL == 0);
5946 STATIC_CHECK(NOT_EQUAL == 1);
5947 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005948}
5949
5950
5951static Object* Runtime_NumberCompare(Arguments args) {
5952 NoHandleAllocation ha;
5953 ASSERT(args.length() == 3);
5954
5955 CONVERT_DOUBLE_CHECKED(x, args[0]);
5956 CONVERT_DOUBLE_CHECKED(y, args[1]);
5957 if (isnan(x) || isnan(y)) return args[2];
5958 if (x == y) return Smi::FromInt(EQUAL);
5959 if (isless(x, y)) return Smi::FromInt(LESS);
5960 return Smi::FromInt(GREATER);
5961}
5962
5963
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005964// Compare two Smis as if they were converted to strings and then
5965// compared lexicographically.
5966static Object* Runtime_SmiLexicographicCompare(Arguments args) {
5967 NoHandleAllocation ha;
5968 ASSERT(args.length() == 2);
5969
5970 // Arrays for the individual characters of the two Smis. Smis are
5971 // 31 bit integers and 10 decimal digits are therefore enough.
5972 static int x_elms[10];
5973 static int y_elms[10];
5974
5975 // Extract the integer values from the Smis.
5976 CONVERT_CHECKED(Smi, x, args[0]);
5977 CONVERT_CHECKED(Smi, y, args[1]);
5978 int x_value = x->value();
5979 int y_value = y->value();
5980
5981 // If the integers are equal so are the string representations.
5982 if (x_value == y_value) return Smi::FromInt(EQUAL);
5983
5984 // If one of the integers are zero the normal integer order is the
5985 // same as the lexicographic order of the string representations.
5986 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
5987
ager@chromium.org32912102009-01-16 10:38:43 +00005988 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005989 // smallest because the char code of '-' is less than the char code
5990 // of any digit. Otherwise, we make both values positive.
5991 if (x_value < 0 || y_value < 0) {
5992 if (y_value >= 0) return Smi::FromInt(LESS);
5993 if (x_value >= 0) return Smi::FromInt(GREATER);
5994 x_value = -x_value;
5995 y_value = -y_value;
5996 }
5997
5998 // Convert the integers to arrays of their decimal digits.
5999 int x_index = 0;
6000 int y_index = 0;
6001 while (x_value > 0) {
6002 x_elms[x_index++] = x_value % 10;
6003 x_value /= 10;
6004 }
6005 while (y_value > 0) {
6006 y_elms[y_index++] = y_value % 10;
6007 y_value /= 10;
6008 }
6009
6010 // Loop through the arrays of decimal digits finding the first place
6011 // where they differ.
6012 while (--x_index >= 0 && --y_index >= 0) {
6013 int diff = x_elms[x_index] - y_elms[y_index];
6014 if (diff != 0) return Smi::FromInt(diff);
6015 }
6016
6017 // If one array is a suffix of the other array, the longest array is
6018 // the representation of the largest of the Smis in the
6019 // lexicographic ordering.
6020 return Smi::FromInt(x_index - y_index);
6021}
6022
6023
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006024static Object* StringInputBufferCompare(String* x, String* y) {
6025 static StringInputBuffer bufx;
6026 static StringInputBuffer bufy;
6027 bufx.Reset(x);
6028 bufy.Reset(y);
6029 while (bufx.has_more() && bufy.has_more()) {
6030 int d = bufx.GetNext() - bufy.GetNext();
6031 if (d < 0) return Smi::FromInt(LESS);
6032 else if (d > 0) return Smi::FromInt(GREATER);
6033 }
6034
6035 // x is (non-trivial) prefix of y:
6036 if (bufy.has_more()) return Smi::FromInt(LESS);
6037 // y is prefix of x:
6038 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
6039}
6040
6041
6042static Object* FlatStringCompare(String* x, String* y) {
6043 ASSERT(x->IsFlat());
6044 ASSERT(y->IsFlat());
6045 Object* equal_prefix_result = Smi::FromInt(EQUAL);
6046 int prefix_length = x->length();
6047 if (y->length() < prefix_length) {
6048 prefix_length = y->length();
6049 equal_prefix_result = Smi::FromInt(GREATER);
6050 } else if (y->length() > prefix_length) {
6051 equal_prefix_result = Smi::FromInt(LESS);
6052 }
6053 int r;
6054 if (x->IsAsciiRepresentation()) {
6055 Vector<const char> x_chars = x->ToAsciiVector();
6056 if (y->IsAsciiRepresentation()) {
6057 Vector<const char> y_chars = y->ToAsciiVector();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006058 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006059 } else {
6060 Vector<const uc16> y_chars = y->ToUC16Vector();
6061 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
6062 }
6063 } else {
6064 Vector<const uc16> x_chars = x->ToUC16Vector();
6065 if (y->IsAsciiRepresentation()) {
6066 Vector<const char> y_chars = y->ToAsciiVector();
6067 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
6068 } else {
6069 Vector<const uc16> y_chars = y->ToUC16Vector();
6070 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
6071 }
6072 }
6073 Object* result;
6074 if (r == 0) {
6075 result = equal_prefix_result;
6076 } else {
6077 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
6078 }
6079 ASSERT(result == StringInputBufferCompare(x, y));
6080 return result;
6081}
6082
6083
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006084static Object* Runtime_StringCompare(Arguments args) {
6085 NoHandleAllocation ha;
6086 ASSERT(args.length() == 2);
6087
6088 CONVERT_CHECKED(String, x, args[0]);
6089 CONVERT_CHECKED(String, y, args[1]);
6090
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006091 Counters::string_compare_runtime.Increment();
6092
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006093 // A few fast case tests before we flatten.
6094 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006095 if (y->length() == 0) {
6096 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006097 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006098 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006099 return Smi::FromInt(LESS);
6100 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006101
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006102 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006103 if (d < 0) return Smi::FromInt(LESS);
6104 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006105
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006106 Object* obj = Heap::PrepareForCompare(x);
6107 if (obj->IsFailure()) return obj;
6108 obj = Heap::PrepareForCompare(y);
6109 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006110
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006111 return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
6112 : StringInputBufferCompare(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006113}
6114
6115
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006116static Object* Runtime_Math_acos(Arguments args) {
6117 NoHandleAllocation ha;
6118 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006119 Counters::math_acos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006120
6121 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006122 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006123}
6124
6125
6126static Object* Runtime_Math_asin(Arguments args) {
6127 NoHandleAllocation ha;
6128 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006129 Counters::math_asin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006130
6131 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006132 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006133}
6134
6135
6136static Object* Runtime_Math_atan(Arguments args) {
6137 NoHandleAllocation ha;
6138 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006139 Counters::math_atan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006140
6141 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006142 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006143}
6144
6145
6146static Object* Runtime_Math_atan2(Arguments args) {
6147 NoHandleAllocation ha;
6148 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006149 Counters::math_atan2.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006150
6151 CONVERT_DOUBLE_CHECKED(x, args[0]);
6152 CONVERT_DOUBLE_CHECKED(y, args[1]);
6153 double result;
6154 if (isinf(x) && isinf(y)) {
6155 // Make sure that the result in case of two infinite arguments
6156 // is a multiple of Pi / 4. The sign of the result is determined
6157 // by the first argument (x) and the sign of the second argument
6158 // determines the multiplier: one or three.
6159 static double kPiDividedBy4 = 0.78539816339744830962;
6160 int multiplier = (x < 0) ? -1 : 1;
6161 if (y < 0) multiplier *= 3;
6162 result = multiplier * kPiDividedBy4;
6163 } else {
6164 result = atan2(x, y);
6165 }
6166 return Heap::AllocateHeapNumber(result);
6167}
6168
6169
6170static Object* Runtime_Math_ceil(Arguments args) {
6171 NoHandleAllocation ha;
6172 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006173 Counters::math_ceil.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006174
6175 CONVERT_DOUBLE_CHECKED(x, args[0]);
6176 return Heap::NumberFromDouble(ceiling(x));
6177}
6178
6179
6180static Object* Runtime_Math_cos(Arguments args) {
6181 NoHandleAllocation ha;
6182 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006183 Counters::math_cos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006184
6185 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006186 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006187}
6188
6189
6190static Object* Runtime_Math_exp(Arguments args) {
6191 NoHandleAllocation ha;
6192 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006193 Counters::math_exp.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006194
6195 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006196 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006197}
6198
6199
6200static Object* Runtime_Math_floor(Arguments args) {
6201 NoHandleAllocation ha;
6202 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006203 Counters::math_floor.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006204
6205 CONVERT_DOUBLE_CHECKED(x, args[0]);
6206 return Heap::NumberFromDouble(floor(x));
6207}
6208
6209
6210static Object* Runtime_Math_log(Arguments args) {
6211 NoHandleAllocation ha;
6212 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006213 Counters::math_log.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006214
6215 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006216 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006217}
6218
6219
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006220// Helper function to compute x^y, where y is known to be an
6221// integer. Uses binary decomposition to limit the number of
6222// multiplications; see the discussion in "Hacker's Delight" by Henry
6223// S. Warren, Jr., figure 11-6, page 213.
6224static double powi(double x, int y) {
6225 ASSERT(y != kMinInt);
6226 unsigned n = (y < 0) ? -y : y;
6227 double m = x;
6228 double p = 1;
6229 while (true) {
6230 if ((n & 1) != 0) p *= m;
6231 n >>= 1;
6232 if (n == 0) {
6233 if (y < 0) {
6234 // Unfortunately, we have to be careful when p has reached
6235 // infinity in the computation, because sometimes the higher
6236 // internal precision in the pow() implementation would have
6237 // given us a finite p. This happens very rarely.
6238 double result = 1.0 / p;
6239 return (result == 0 && isinf(p))
6240 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
6241 : result;
6242 } else {
6243 return p;
6244 }
6245 }
6246 m *= m;
6247 }
6248}
6249
6250
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006251static Object* Runtime_Math_pow(Arguments args) {
6252 NoHandleAllocation ha;
6253 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006254 Counters::math_pow.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006255
6256 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006257
6258 // If the second argument is a smi, it is much faster to call the
6259 // custom powi() function than the generic pow().
6260 if (args[1]->IsSmi()) {
6261 int y = Smi::cast(args[1])->value();
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00006262 return Heap::NumberFromDouble(powi(x, y));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006263 }
6264
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006265 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00006266
6267 if (!isinf(x)) {
6268 if (y == 0.5) {
6269 // It's not uncommon to use Math.pow(x, 0.5) to compute the
6270 // square root of a number. To speed up such computations, we
6271 // explictly check for this case and use the sqrt() function
6272 // which is faster than pow().
6273 return Heap::AllocateHeapNumber(sqrt(x));
6274 } else if (y == -0.5) {
6275 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
6276 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
6277 }
6278 }
6279
6280 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006281 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006282 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6283 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006284 } else {
6285 return Heap::AllocateHeapNumber(pow(x, y));
6286 }
6287}
6288
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006289// Fast version of Math.pow if we know that y is not an integer and
6290// y is not -0.5 or 0.5. Used as slowcase from codegen.
6291static Object* Runtime_Math_pow_cfunction(Arguments args) {
6292 NoHandleAllocation ha;
6293 ASSERT(args.length() == 2);
6294 CONVERT_DOUBLE_CHECKED(x, args[0]);
6295 CONVERT_DOUBLE_CHECKED(y, args[1]);
6296 if (y == 0) {
6297 return Smi::FromInt(1);
6298 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6299 return Heap::nan_value();
6300 } else {
6301 return Heap::AllocateHeapNumber(pow(x, y));
6302 }
6303}
6304
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006305
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006306static Object* Runtime_RoundNumber(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006307 NoHandleAllocation ha;
6308 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006309 Counters::math_round.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006310
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006311 if (!args[0]->IsHeapNumber()) {
6312 // Must be smi. Return the argument unchanged for all the other types
6313 // to make fuzz-natives test happy.
6314 return args[0];
6315 }
6316
6317 HeapNumber* number = reinterpret_cast<HeapNumber*>(args[0]);
6318
6319 double value = number->value();
6320 int exponent = number->get_exponent();
6321 int sign = number->get_sign();
6322
6323 // We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
6324 // should be rounded to 2^30, which is not smi.
6325 if (!sign && exponent <= kSmiValueSize - 3) {
6326 return Smi::FromInt(static_cast<int>(value + 0.5));
6327 }
6328
6329 // If the magnitude is big enough, there's no place for fraction part. If we
6330 // try to add 0.5 to this number, 1.0 will be added instead.
6331 if (exponent >= 52) {
6332 return number;
6333 }
6334
6335 if (sign && value >= -0.5) return Heap::minus_zero_value();
6336
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006337 // Do not call NumberFromDouble() to avoid extra checks.
6338 return Heap::AllocateHeapNumber(floor(value + 0.5));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006339}
6340
6341
6342static Object* Runtime_Math_sin(Arguments args) {
6343 NoHandleAllocation ha;
6344 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006345 Counters::math_sin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006346
6347 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006348 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006349}
6350
6351
6352static Object* Runtime_Math_sqrt(Arguments args) {
6353 NoHandleAllocation ha;
6354 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006355 Counters::math_sqrt.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006356
6357 CONVERT_DOUBLE_CHECKED(x, args[0]);
6358 return Heap::AllocateHeapNumber(sqrt(x));
6359}
6360
6361
6362static Object* Runtime_Math_tan(Arguments args) {
6363 NoHandleAllocation ha;
6364 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006365 Counters::math_tan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006366
6367 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006368 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006369}
6370
6371
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006372static int MakeDay(int year, int month, int day) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006373 static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
6374 181, 212, 243, 273, 304, 334};
6375 static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
6376 182, 213, 244, 274, 305, 335};
6377
6378 year += month / 12;
6379 month %= 12;
6380 if (month < 0) {
6381 year--;
6382 month += 12;
6383 }
6384
6385 ASSERT(month >= 0);
6386 ASSERT(month < 12);
6387
6388 // year_delta is an arbitrary number such that:
6389 // a) year_delta = -1 (mod 400)
6390 // b) year + year_delta > 0 for years in the range defined by
6391 // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
6392 // Jan 1 1970. This is required so that we don't run into integer
6393 // division of negative numbers.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006394 // c) there shouldn't be an overflow for 32-bit integers in the following
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006395 // operations.
6396 static const int year_delta = 399999;
6397 static const int base_day = 365 * (1970 + year_delta) +
6398 (1970 + year_delta) / 4 -
6399 (1970 + year_delta) / 100 +
6400 (1970 + year_delta) / 400;
6401
6402 int year1 = year + year_delta;
6403 int day_from_year = 365 * year1 +
6404 year1 / 4 -
6405 year1 / 100 +
6406 year1 / 400 -
6407 base_day;
6408
6409 if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006410 return day_from_year + day_from_month[month] + day - 1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006411 }
6412
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006413 return day_from_year + day_from_month_leap[month] + day - 1;
6414}
6415
6416
6417static Object* Runtime_DateMakeDay(Arguments args) {
6418 NoHandleAllocation ha;
6419 ASSERT(args.length() == 3);
6420
6421 CONVERT_SMI_CHECKED(year, args[0]);
6422 CONVERT_SMI_CHECKED(month, args[1]);
6423 CONVERT_SMI_CHECKED(date, args[2]);
6424
6425 return Smi::FromInt(MakeDay(year, month, date));
6426}
6427
6428
6429static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
6430static const int kDaysIn4Years = 4 * 365 + 1;
6431static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
6432static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
6433static const int kDays1970to2000 = 30 * 365 + 7;
6434static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
6435 kDays1970to2000;
6436static const int kYearsOffset = 400000;
6437
6438static const char kDayInYear[] = {
6439 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6440 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6441 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6442 22, 23, 24, 25, 26, 27, 28,
6443 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6444 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6445 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6446 22, 23, 24, 25, 26, 27, 28, 29, 30,
6447 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6448 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6449 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6450 22, 23, 24, 25, 26, 27, 28, 29, 30,
6451 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6452 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6453 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6454 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6455 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6456 22, 23, 24, 25, 26, 27, 28, 29, 30,
6457 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6458 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6459 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6460 22, 23, 24, 25, 26, 27, 28, 29, 30,
6461 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6462 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6463
6464 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6465 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6466 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6467 22, 23, 24, 25, 26, 27, 28,
6468 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6469 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6470 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6471 22, 23, 24, 25, 26, 27, 28, 29, 30,
6472 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6473 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6474 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6475 22, 23, 24, 25, 26, 27, 28, 29, 30,
6476 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6477 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6478 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6479 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6480 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6481 22, 23, 24, 25, 26, 27, 28, 29, 30,
6482 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6483 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6484 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6485 22, 23, 24, 25, 26, 27, 28, 29, 30,
6486 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6487 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6488
6489 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6490 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6491 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6492 22, 23, 24, 25, 26, 27, 28, 29,
6493 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6494 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6495 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6496 22, 23, 24, 25, 26, 27, 28, 29, 30,
6497 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6498 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6499 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6500 22, 23, 24, 25, 26, 27, 28, 29, 30,
6501 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6502 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6503 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6504 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6505 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6506 22, 23, 24, 25, 26, 27, 28, 29, 30,
6507 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6508 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6509 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6510 22, 23, 24, 25, 26, 27, 28, 29, 30,
6511 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6512 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6513
6514 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6515 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6516 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6517 22, 23, 24, 25, 26, 27, 28,
6518 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6519 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6520 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6521 22, 23, 24, 25, 26, 27, 28, 29, 30,
6522 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6523 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6524 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6525 22, 23, 24, 25, 26, 27, 28, 29, 30,
6526 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6527 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6528 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6529 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6530 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6531 22, 23, 24, 25, 26, 27, 28, 29, 30,
6532 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6533 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6534 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6535 22, 23, 24, 25, 26, 27, 28, 29, 30,
6536 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6537 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
6538
6539static const char kMonthInYear[] = {
6540 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,
6541 0, 0, 0, 0, 0, 0,
6542 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,
6543 1, 1, 1,
6544 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,
6545 2, 2, 2, 2, 2, 2,
6546 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,
6547 3, 3, 3, 3, 3,
6548 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,
6549 4, 4, 4, 4, 4, 4,
6550 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,
6551 5, 5, 5, 5, 5,
6552 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,
6553 6, 6, 6, 6, 6, 6,
6554 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,
6555 7, 7, 7, 7, 7, 7,
6556 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,
6557 8, 8, 8, 8, 8,
6558 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,
6559 9, 9, 9, 9, 9, 9,
6560 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6561 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6562 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6563 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6564
6565 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,
6566 0, 0, 0, 0, 0, 0,
6567 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,
6568 1, 1, 1,
6569 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,
6570 2, 2, 2, 2, 2, 2,
6571 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,
6572 3, 3, 3, 3, 3,
6573 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,
6574 4, 4, 4, 4, 4, 4,
6575 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,
6576 5, 5, 5, 5, 5,
6577 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,
6578 6, 6, 6, 6, 6, 6,
6579 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,
6580 7, 7, 7, 7, 7, 7,
6581 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,
6582 8, 8, 8, 8, 8,
6583 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,
6584 9, 9, 9, 9, 9, 9,
6585 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6586 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6587 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6588 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6589
6590 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,
6591 0, 0, 0, 0, 0, 0,
6592 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,
6593 1, 1, 1, 1,
6594 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,
6595 2, 2, 2, 2, 2, 2,
6596 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,
6597 3, 3, 3, 3, 3,
6598 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,
6599 4, 4, 4, 4, 4, 4,
6600 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,
6601 5, 5, 5, 5, 5,
6602 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,
6603 6, 6, 6, 6, 6, 6,
6604 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,
6605 7, 7, 7, 7, 7, 7,
6606 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,
6607 8, 8, 8, 8, 8,
6608 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,
6609 9, 9, 9, 9, 9, 9,
6610 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6611 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6612 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6613 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6614
6615 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,
6616 0, 0, 0, 0, 0, 0,
6617 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,
6618 1, 1, 1,
6619 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,
6620 2, 2, 2, 2, 2, 2,
6621 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,
6622 3, 3, 3, 3, 3,
6623 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,
6624 4, 4, 4, 4, 4, 4,
6625 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,
6626 5, 5, 5, 5, 5,
6627 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,
6628 6, 6, 6, 6, 6, 6,
6629 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,
6630 7, 7, 7, 7, 7, 7,
6631 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,
6632 8, 8, 8, 8, 8,
6633 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,
6634 9, 9, 9, 9, 9, 9,
6635 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6636 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6637 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6638 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
6639
6640
6641// This function works for dates from 1970 to 2099.
6642static inline void DateYMDFromTimeAfter1970(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006643 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006644#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006645 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006646#endif
6647
6648 year = 1970 + (4 * date + 2) / kDaysIn4Years;
6649 date %= kDaysIn4Years;
6650
6651 month = kMonthInYear[date];
6652 day = kDayInYear[date];
6653
6654 ASSERT(MakeDay(year, month, day) == save_date);
6655}
6656
6657
6658static inline void DateYMDFromTimeSlow(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006659 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006660#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006661 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006662#endif
6663
6664 date += kDaysOffset;
6665 year = 400 * (date / kDaysIn400Years) - kYearsOffset;
6666 date %= kDaysIn400Years;
6667
6668 ASSERT(MakeDay(year, 0, 1) + date == save_date);
6669
6670 date--;
6671 int yd1 = date / kDaysIn100Years;
6672 date %= kDaysIn100Years;
6673 year += 100 * yd1;
6674
6675 date++;
6676 int yd2 = date / kDaysIn4Years;
6677 date %= kDaysIn4Years;
6678 year += 4 * yd2;
6679
6680 date--;
6681 int yd3 = date / 365;
6682 date %= 365;
6683 year += yd3;
6684
6685 bool is_leap = (!yd1 || yd2) && !yd3;
6686
6687 ASSERT(date >= -1);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006688 ASSERT(is_leap || (date >= 0));
6689 ASSERT((date < 365) || (is_leap && (date < 366)));
6690 ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0))));
6691 ASSERT(is_leap || ((MakeDay(year, 0, 1) + date) == save_date));
6692 ASSERT(!is_leap || ((MakeDay(year, 0, 1) + date + 1) == save_date));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006693
6694 if (is_leap) {
6695 day = kDayInYear[2*365 + 1 + date];
6696 month = kMonthInYear[2*365 + 1 + date];
6697 } else {
6698 day = kDayInYear[date];
6699 month = kMonthInYear[date];
6700 }
6701
6702 ASSERT(MakeDay(year, month, day) == save_date);
6703}
6704
6705
6706static inline void DateYMDFromTime(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006707 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006708 if (date >= 0 && date < 32 * kDaysIn4Years) {
6709 DateYMDFromTimeAfter1970(date, year, month, day);
6710 } else {
6711 DateYMDFromTimeSlow(date, year, month, day);
6712 }
6713}
6714
6715
6716static Object* Runtime_DateYMDFromTime(Arguments args) {
6717 NoHandleAllocation ha;
6718 ASSERT(args.length() == 2);
6719
6720 CONVERT_DOUBLE_CHECKED(t, args[0]);
6721 CONVERT_CHECKED(JSArray, res_array, args[1]);
6722
6723 int year, month, day;
6724 DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
6725
ricow@chromium.org0b9f8502010-08-18 07:45:01 +00006726 RUNTIME_ASSERT(res_array->elements()->map() == Heap::fixed_array_map());
6727 FixedArray* elms = FixedArray::cast(res_array->elements());
6728 RUNTIME_ASSERT(elms->length() == 3);
6729
6730 elms->set(0, Smi::FromInt(year));
6731 elms->set(1, Smi::FromInt(month));
6732 elms->set(2, Smi::FromInt(day));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006733
6734 return Heap::undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006735}
6736
6737
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006738static Object* Runtime_NewArgumentsFast(Arguments args) {
6739 NoHandleAllocation ha;
6740 ASSERT(args.length() == 3);
6741
6742 JSFunction* callee = JSFunction::cast(args[0]);
6743 Object** parameters = reinterpret_cast<Object**>(args[1]);
6744 const int length = Smi::cast(args[2])->value();
6745
6746 Object* result = Heap::AllocateArgumentsObject(callee, length);
6747 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006748 // Allocate the elements if needed.
6749 if (length > 0) {
6750 // Allocate the fixed array.
6751 Object* obj = Heap::AllocateRawFixedArray(length);
6752 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006753
6754 AssertNoAllocation no_gc;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006755 FixedArray* array = reinterpret_cast<FixedArray*>(obj);
6756 array->set_map(Heap::fixed_array_map());
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006757 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006758
6759 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006760 for (int i = 0; i < length; i++) {
6761 array->set(i, *--parameters, mode);
6762 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006763 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006764 }
6765 return result;
6766}
6767
6768
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006769static Object* Runtime_NewClosure(Arguments args) {
6770 HandleScope scope;
6771 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00006772 CONVERT_ARG_CHECKED(Context, context, 0);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006773 CONVERT_ARG_CHECKED(SharedFunctionInfo, shared, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006774
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00006775 PretenureFlag pretenure = (context->global_context() == *context)
6776 ? TENURED // Allocate global closures in old space.
6777 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006778 Handle<JSFunction> result =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006779 Factory::NewFunctionFromSharedFunctionInfo(shared, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006780 return *result;
6781}
6782
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006783static Object* Runtime_NewObjectFromBound(Arguments args) {
6784 HandleScope scope;
6785 ASSERT(args.length() == 2);
6786 CONVERT_ARG_CHECKED(JSFunction, function, 0);
6787 CONVERT_ARG_CHECKED(JSArray, params, 1);
6788
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006789 RUNTIME_ASSERT(params->HasFastElements());
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006790 FixedArray* fixed = FixedArray::cast(params->elements());
6791
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006792 int fixed_length = Smi::cast(params->length())->value();
6793 SmartPointer<Object**> param_data(NewArray<Object**>(fixed_length));
6794 for (int i = 0; i < fixed_length; i++) {
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006795 Handle<Object> val = Handle<Object>(fixed->get(i));
6796 param_data[i] = val.location();
6797 }
6798
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006799 bool exception = false;
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006800 Handle<Object> result = Execution::New(
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006801 function, fixed_length, *param_data, &exception);
6802 if (exception) {
6803 return Failure::Exception();
6804 }
6805 ASSERT(!result.is_null());
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006806 return *result;
6807}
6808
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006809
ager@chromium.org5c838252010-02-19 08:53:10 +00006810static Code* ComputeConstructStub(Handle<JSFunction> function) {
6811 Handle<Object> prototype = Factory::null_value();
6812 if (function->has_instance_prototype()) {
6813 prototype = Handle<Object>(function->instance_prototype());
6814 }
6815 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006816 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00006817 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006818 if (code->IsFailure()) {
6819 return Builtins::builtin(Builtins::JSConstructStubGeneric);
6820 }
6821 return Code::cast(code);
6822 }
6823
ager@chromium.org5c838252010-02-19 08:53:10 +00006824 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006825}
6826
6827
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006828static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006829 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006830 ASSERT(args.length() == 1);
6831
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006832 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006833
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006834 // If the constructor isn't a proper function we throw a type error.
6835 if (!constructor->IsJSFunction()) {
6836 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6837 Handle<Object> type_error =
6838 Factory::NewTypeError("not_constructor", arguments);
6839 return Top::Throw(*type_error);
6840 }
6841
6842 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006843
6844 // If function should not have prototype, construction is not allowed. In this
6845 // case generated code bailouts here, since function has no initial_map.
6846 if (!function->should_have_prototype()) {
6847 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6848 Handle<Object> type_error =
6849 Factory::NewTypeError("not_constructor", arguments);
6850 return Top::Throw(*type_error);
6851 }
6852
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006853#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006854 // Handle stepping into constructors if step into is active.
6855 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006856 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006857 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006858#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006859
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006860 if (function->has_initial_map()) {
6861 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006862 // The 'Function' function ignores the receiver object when
6863 // called using 'new' and creates a new JSFunction object that
6864 // is returned. The receiver object is only used for error
6865 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006866 // JSFunction. Factory::NewJSObject() should not be used to
6867 // allocate JSFunctions since it does not properly initialize
6868 // the shared part of the function. Since the receiver is
6869 // ignored anyway, we use the global object as the receiver
6870 // instead of a new JSFunction object. This way, errors are
6871 // reported the same way whether or not 'Function' is called
6872 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006873 return Top::context()->global();
6874 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006875 }
6876
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006877 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006878 Handle<SharedFunctionInfo> shared(function->shared());
6879 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006880
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006881 bool first_allocation = !function->has_initial_map();
6882 Handle<JSObject> result = Factory::NewJSObject(function);
6883 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006884 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00006885 ComputeConstructStub(Handle<JSFunction>(function)));
6886 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006887 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006888
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006889 Counters::constructed_objects.Increment();
6890 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006891
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006892 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006893}
6894
6895
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006896static Object* Runtime_LazyCompile(Arguments args) {
6897 HandleScope scope;
6898 ASSERT(args.length() == 1);
6899
6900 Handle<JSFunction> function = args.at<JSFunction>(0);
6901#ifdef DEBUG
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00006902 if (FLAG_trace_lazy && !function->shared()->is_compiled()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006903 PrintF("[lazy: ");
6904 function->shared()->name()->Print();
6905 PrintF("]\n");
6906 }
6907#endif
6908
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006909 // Compile the target function. Here we compile using CompileLazyInLoop in
6910 // order to get the optimized version. This helps code like delta-blue
6911 // that calls performance-critical routines through constructors. A
6912 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
6913 // direct call. Since the in-loop tracking takes place through CallICs
6914 // this means that things called through constructors are never known to
6915 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006916 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006917 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006918 return Failure::Exception();
6919 }
6920
6921 return function->code();
6922}
6923
6924
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006925static Object* Runtime_GetFunctionDelegate(Arguments args) {
6926 HandleScope scope;
6927 ASSERT(args.length() == 1);
6928 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6929 return *Execution::GetFunctionDelegate(args.at<Object>(0));
6930}
6931
6932
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00006933static Object* Runtime_GetConstructorDelegate(Arguments args) {
6934 HandleScope scope;
6935 ASSERT(args.length() == 1);
6936 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6937 return *Execution::GetConstructorDelegate(args.at<Object>(0));
6938}
6939
6940
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006941static Object* Runtime_NewContext(Arguments args) {
6942 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00006943 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006944
kasper.lund7276f142008-07-30 08:49:36 +00006945 CONVERT_CHECKED(JSFunction, function, args[0]);
ager@chromium.orgb5737492010-07-15 09:29:43 +00006946 int length = function->shared()->scope_info()->NumberOfContextSlots();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006947 Object* result = Heap::AllocateFunctionContext(length, function);
6948 if (result->IsFailure()) return result;
6949
6950 Top::set_context(Context::cast(result));
6951
kasper.lund7276f142008-07-30 08:49:36 +00006952 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006953}
6954
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006955static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006956 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006957 Object* js_object = object;
6958 if (!js_object->IsJSObject()) {
6959 js_object = js_object->ToObject();
6960 if (js_object->IsFailure()) {
6961 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006962 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006963 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006964 Handle<Object> result =
6965 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
6966 return Top::Throw(*result);
6967 }
6968 }
6969
6970 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006971 Heap::AllocateWithContext(Top::context(),
6972 JSObject::cast(js_object),
6973 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006974 if (result->IsFailure()) return result;
6975
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006976 Context* context = Context::cast(result);
6977 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006978
kasper.lund7276f142008-07-30 08:49:36 +00006979 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006980}
6981
6982
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006983static Object* Runtime_PushContext(Arguments args) {
6984 NoHandleAllocation ha;
6985 ASSERT(args.length() == 1);
6986 return PushContextHelper(args[0], false);
6987}
6988
6989
6990static Object* Runtime_PushCatchContext(Arguments args) {
6991 NoHandleAllocation ha;
6992 ASSERT(args.length() == 1);
6993 return PushContextHelper(args[0], true);
6994}
6995
6996
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006997static Object* Runtime_LookupContext(Arguments args) {
6998 HandleScope scope;
6999 ASSERT(args.length() == 2);
7000
7001 CONVERT_ARG_CHECKED(Context, context, 0);
7002 CONVERT_ARG_CHECKED(String, name, 1);
7003
7004 int index;
7005 PropertyAttributes attributes;
7006 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007007 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007008 context->Lookup(name, flags, &index, &attributes);
7009
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007010 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007011 ASSERT(holder->IsJSObject());
7012 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007013 }
7014
7015 // No intermediate context found. Use global object by default.
7016 return Top::context()->global();
7017}
7018
7019
ager@chromium.orga1645e22009-09-09 19:27:10 +00007020// A mechanism to return a pair of Object pointers in registers (if possible).
7021// How this is achieved is calling convention-dependent.
7022// All currently supported x86 compiles uses calling conventions that are cdecl
7023// variants where a 64-bit value is returned in two 32-bit registers
7024// (edx:eax on ia32, r1:r0 on ARM).
7025// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
7026// In Win64 calling convention, a struct of two pointers is returned in memory,
7027// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007028#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007029struct ObjectPair {
7030 Object* x;
7031 Object* y;
7032};
ager@chromium.orga1645e22009-09-09 19:27:10 +00007033
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007034static inline ObjectPair MakePair(Object* x, Object* y) {
7035 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00007036 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
7037 // In Win64 they are assigned to a hidden first argument.
7038 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007039}
7040#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007041typedef uint64_t ObjectPair;
7042static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007043 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007044 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007045}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007046#endif
7047
7048
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007049static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007050 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
7051 USE(attributes);
7052 return x->IsTheHole() ? Heap::undefined_value() : x;
7053}
7054
7055
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007056static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
7057 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007058 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007059 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007060 JSFunction* context_extension_function =
7061 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007062 // If the holder isn't a context extension object, we just return it
7063 // as the receiver. This allows arguments objects to be used as
7064 // receivers, but only if they are put in the context scope chain
7065 // explicitly via a with-statement.
7066 Object* constructor = holder->map()->constructor();
7067 if (constructor != context_extension_function) return holder;
7068 // Fall back to using the global object as the receiver if the
7069 // property turns out to be a local variable allocated in a context
7070 // extension object - introduced via eval.
7071 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007072}
7073
7074
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007075static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007076 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00007077 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007078
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007079 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00007080 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007081 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007082 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007083 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007084
7085 int index;
7086 PropertyAttributes attributes;
7087 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007088 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007089 context->Lookup(name, flags, &index, &attributes);
7090
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007091 // If the index is non-negative, the slot has been found in a local
7092 // variable or a parameter. Read it from the context object or the
7093 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007094 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007095 // If the "property" we were looking for is a local variable or an
7096 // argument in a context, the receiver is the global object; see
7097 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
7098 JSObject* receiver = Top::context()->global()->global_receiver();
7099 Object* value = (holder->IsContext())
7100 ? Context::cast(*holder)->get(index)
7101 : JSObject::cast(*holder)->GetElement(index);
7102 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007103 }
7104
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007105 // If the holder is found, we read the property from it.
7106 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007107 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007108 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007109 JSObject* receiver;
7110 if (object->IsGlobalObject()) {
7111 receiver = GlobalObject::cast(object)->global_receiver();
7112 } else if (context->is_exception_holder(*holder)) {
7113 receiver = Top::context()->global()->global_receiver();
7114 } else {
7115 receiver = ComputeReceiverForNonGlobal(object);
7116 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007117 // No need to unhole the value here. This is taken care of by the
7118 // GetProperty function.
7119 Object* value = object->GetProperty(*name);
7120 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007121 }
7122
7123 if (throw_error) {
7124 // The property doesn't exist - throw exception.
7125 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007126 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007127 return MakePair(Top::Throw(*reference_error), NULL);
7128 } else {
7129 // The property doesn't exist - return undefined
7130 return MakePair(Heap::undefined_value(), Heap::undefined_value());
7131 }
7132}
7133
7134
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007135static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007136 return LoadContextSlotHelper(args, true);
7137}
7138
7139
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007140static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007141 return LoadContextSlotHelper(args, false);
7142}
7143
7144
7145static Object* Runtime_StoreContextSlot(Arguments args) {
7146 HandleScope scope;
7147 ASSERT(args.length() == 3);
7148
7149 Handle<Object> value(args[0]);
7150 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007151 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007152
7153 int index;
7154 PropertyAttributes attributes;
7155 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007156 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007157 context->Lookup(name, flags, &index, &attributes);
7158
7159 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007160 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007161 // Ignore if read_only variable.
7162 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007163 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007164 }
7165 } else {
7166 ASSERT((attributes & READ_ONLY) == 0);
7167 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007168 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007169 USE(result);
7170 ASSERT(!result->IsFailure());
7171 }
7172 return *value;
7173 }
7174
7175 // Slow case: The property is not in a FixedArray context.
7176 // It is either in an JSObject extension context or it was not found.
7177 Handle<JSObject> context_ext;
7178
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007179 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007180 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007181 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007182 } else {
7183 // The property was not found. It needs to be stored in the global context.
7184 ASSERT(attributes == ABSENT);
7185 attributes = NONE;
7186 context_ext = Handle<JSObject>(Top::context()->global());
7187 }
7188
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007189 // Set the property, but ignore if read_only variable on the context
7190 // extension object itself.
7191 if ((attributes & READ_ONLY) == 0 ||
7192 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007193 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
7194 if (set.is_null()) {
7195 // Failure::Exception is converted to a null handle in the
7196 // handle-based methods such as SetProperty. We therefore need
7197 // to convert null handles back to exceptions.
7198 ASSERT(Top::has_pending_exception());
7199 return Failure::Exception();
7200 }
7201 }
7202 return *value;
7203}
7204
7205
7206static Object* Runtime_Throw(Arguments args) {
7207 HandleScope scope;
7208 ASSERT(args.length() == 1);
7209
7210 return Top::Throw(args[0]);
7211}
7212
7213
7214static Object* Runtime_ReThrow(Arguments args) {
7215 HandleScope scope;
7216 ASSERT(args.length() == 1);
7217
7218 return Top::ReThrow(args[0]);
7219}
7220
7221
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007222static Object* Runtime_PromoteScheduledException(Arguments args) {
7223 ASSERT_EQ(0, args.length());
7224 return Top::PromoteScheduledException();
7225}
7226
7227
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007228static Object* Runtime_ThrowReferenceError(Arguments args) {
7229 HandleScope scope;
7230 ASSERT(args.length() == 1);
7231
7232 Handle<Object> name(args[0]);
7233 Handle<Object> reference_error =
7234 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
7235 return Top::Throw(*reference_error);
7236}
7237
7238
7239static Object* Runtime_StackOverflow(Arguments args) {
7240 NoHandleAllocation na;
7241 return Top::StackOverflow();
7242}
7243
7244
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007245static Object* Runtime_StackGuard(Arguments args) {
7246 ASSERT(args.length() == 1);
7247
7248 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007249 if (StackGuard::IsStackOverflow()) {
7250 return Runtime_StackOverflow(args);
7251 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007252
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007253 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007254}
7255
7256
7257// NOTE: These PrintXXX functions are defined for all builds (not just
7258// DEBUG builds) because we may want to be able to trace function
7259// calls in all modes.
7260static void PrintString(String* str) {
7261 // not uncommon to have empty strings
7262 if (str->length() > 0) {
7263 SmartPointer<char> s =
7264 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
7265 PrintF("%s", *s);
7266 }
7267}
7268
7269
7270static void PrintObject(Object* obj) {
7271 if (obj->IsSmi()) {
7272 PrintF("%d", Smi::cast(obj)->value());
7273 } else if (obj->IsString() || obj->IsSymbol()) {
7274 PrintString(String::cast(obj));
7275 } else if (obj->IsNumber()) {
7276 PrintF("%g", obj->Number());
7277 } else if (obj->IsFailure()) {
7278 PrintF("<failure>");
7279 } else if (obj->IsUndefined()) {
7280 PrintF("<undefined>");
7281 } else if (obj->IsNull()) {
7282 PrintF("<null>");
7283 } else if (obj->IsTrue()) {
7284 PrintF("<true>");
7285 } else if (obj->IsFalse()) {
7286 PrintF("<false>");
7287 } else {
7288 PrintF("%p", obj);
7289 }
7290}
7291
7292
7293static int StackSize() {
7294 int n = 0;
7295 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
7296 return n;
7297}
7298
7299
7300static void PrintTransition(Object* result) {
7301 // indentation
7302 { const int nmax = 80;
7303 int n = StackSize();
7304 if (n <= nmax)
7305 PrintF("%4d:%*s", n, n, "");
7306 else
7307 PrintF("%4d:%*s", n, nmax, "...");
7308 }
7309
7310 if (result == NULL) {
7311 // constructor calls
7312 JavaScriptFrameIterator it;
7313 JavaScriptFrame* frame = it.frame();
7314 if (frame->IsConstructor()) PrintF("new ");
7315 // function name
7316 Object* fun = frame->function();
7317 if (fun->IsJSFunction()) {
7318 PrintObject(JSFunction::cast(fun)->shared()->name());
7319 } else {
7320 PrintObject(fun);
7321 }
7322 // function arguments
7323 // (we are intentionally only printing the actually
7324 // supplied parameters, not all parameters required)
7325 PrintF("(this=");
7326 PrintObject(frame->receiver());
7327 const int length = frame->GetProvidedParametersCount();
7328 for (int i = 0; i < length; i++) {
7329 PrintF(", ");
7330 PrintObject(frame->GetParameter(i));
7331 }
7332 PrintF(") {\n");
7333
7334 } else {
7335 // function result
7336 PrintF("} -> ");
7337 PrintObject(result);
7338 PrintF("\n");
7339 }
7340}
7341
7342
7343static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007344 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007345 NoHandleAllocation ha;
7346 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007347 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007348}
7349
7350
7351static Object* Runtime_TraceExit(Arguments args) {
7352 NoHandleAllocation ha;
7353 PrintTransition(args[0]);
7354 return args[0]; // return TOS
7355}
7356
7357
7358static Object* Runtime_DebugPrint(Arguments args) {
7359 NoHandleAllocation ha;
7360 ASSERT(args.length() == 1);
7361
7362#ifdef DEBUG
7363 if (args[0]->IsString()) {
7364 // If we have a string, assume it's a code "marker"
7365 // and print some interesting cpu debugging info.
7366 JavaScriptFrameIterator it;
7367 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007368 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
7369 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007370 } else {
7371 PrintF("DebugPrint: ");
7372 }
7373 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007374 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007375 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007376 HeapObject::cast(args[0])->map()->Print();
7377 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007378#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007379 // ShortPrint is available in release mode. Print is not.
7380 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007381#endif
7382 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00007383 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007384
7385 return args[0]; // return TOS
7386}
7387
7388
7389static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007390 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007391 NoHandleAllocation ha;
7392 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007393 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007394}
7395
7396
mads.s.ager31e71382008-08-13 09:32:07 +00007397static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007398 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007399 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007400
7401 // According to ECMA-262, section 15.9.1, page 117, the precision of
7402 // the number in a Date object representing a particular instant in
7403 // time is milliseconds. Therefore, we floor the result of getting
7404 // the OS time.
7405 double millis = floor(OS::TimeCurrentMillis());
7406 return Heap::NumberFromDouble(millis);
7407}
7408
7409
7410static Object* Runtime_DateParseString(Arguments args) {
7411 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007412 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007413
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007414 CONVERT_ARG_CHECKED(String, str, 0);
7415 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007416
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007417 CONVERT_ARG_CHECKED(JSArray, output, 1);
7418 RUNTIME_ASSERT(output->HasFastElements());
7419
7420 AssertNoAllocation no_allocation;
7421
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007422 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007423 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
7424 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007425 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007426 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007427 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007428 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007429 result = DateParser::Parse(str->ToUC16Vector(), output_array);
7430 }
7431
7432 if (result) {
7433 return *output;
7434 } else {
7435 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007436 }
7437}
7438
7439
7440static Object* Runtime_DateLocalTimezone(Arguments args) {
7441 NoHandleAllocation ha;
7442 ASSERT(args.length() == 1);
7443
7444 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007445 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007446 return Heap::AllocateStringFromUtf8(CStrVector(zone));
7447}
7448
7449
7450static Object* Runtime_DateLocalTimeOffset(Arguments args) {
7451 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007452 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007453
7454 return Heap::NumberFromDouble(OS::LocalTimeOffset());
7455}
7456
7457
7458static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
7459 NoHandleAllocation ha;
7460 ASSERT(args.length() == 1);
7461
7462 CONVERT_DOUBLE_CHECKED(x, args[0]);
7463 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
7464}
7465
7466
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007467static Object* Runtime_GlobalReceiver(Arguments args) {
7468 ASSERT(args.length() == 1);
7469 Object* global = args[0];
7470 if (!global->IsJSGlobalObject()) return Heap::null_value();
7471 return JSGlobalObject::cast(global)->global_receiver();
7472}
7473
7474
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007475static Object* Runtime_CompileString(Arguments args) {
7476 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007477 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007478 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007479 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007480
ager@chromium.org381abbb2009-02-25 13:23:22 +00007481 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007482 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007483 Compiler::ValidationState validate = (is_json->IsTrue())
7484 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007485 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
7486 context,
7487 true,
7488 validate);
7489 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007490 Handle<JSFunction> fun =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007491 Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007492 return *fun;
7493}
7494
7495
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007496static ObjectPair CompileGlobalEval(Handle<String> source,
7497 Handle<Object> receiver) {
7498 // Deal with a normal eval call with a string argument. Compile it
7499 // and return the compiled function bound in the local context.
7500 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
7501 source,
7502 Handle<Context>(Top::context()),
7503 Top::context()->IsGlobalContext(),
7504 Compiler::DONT_VALIDATE_JSON);
7505 if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
7506 Handle<JSFunction> compiled = Factory::NewFunctionFromSharedFunctionInfo(
7507 shared,
7508 Handle<Context>(Top::context()),
7509 NOT_TENURED);
7510 return MakePair(*compiled, *receiver);
7511}
7512
7513
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007514static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
7515 ASSERT(args.length() == 3);
7516 if (!args[0]->IsJSFunction()) {
7517 return MakePair(Top::ThrowIllegalOperation(), NULL);
7518 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007519
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007520 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007521 Handle<JSFunction> callee = args.at<JSFunction>(0);
7522 Handle<Object> receiver; // Will be overwritten.
7523
7524 // Compute the calling context.
7525 Handle<Context> context = Handle<Context>(Top::context());
7526#ifdef DEBUG
7527 // Make sure Top::context() agrees with the old code that traversed
7528 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007529 StackFrameLocator locator;
7530 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007531 ASSERT(Context::cast(frame->context()) == *context);
7532#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007533
7534 // Find where the 'eval' symbol is bound. It is unaliased only if
7535 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007536 int index = -1;
7537 PropertyAttributes attributes = ABSENT;
7538 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007539 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
7540 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007541 // Stop search when eval is found or when the global context is
7542 // reached.
7543 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007544 if (context->is_function_context()) {
7545 context = Handle<Context>(Context::cast(context->closure()->context()));
7546 } else {
7547 context = Handle<Context>(context->previous());
7548 }
7549 }
7550
iposva@chromium.org245aa852009-02-10 00:49:54 +00007551 // If eval could not be resolved, it has been deleted and we need to
7552 // throw a reference error.
7553 if (attributes == ABSENT) {
7554 Handle<Object> name = Factory::eval_symbol();
7555 Handle<Object> reference_error =
7556 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007557 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007558 }
7559
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007560 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007561 // 'eval' is not bound in the global context. Just call the function
7562 // with the given arguments. This is not necessarily the global eval.
7563 if (receiver->IsContext()) {
7564 context = Handle<Context>::cast(receiver);
7565 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007566 } else if (receiver->IsJSContextExtensionObject()) {
7567 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007568 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007569 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007570 }
7571
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007572 // 'eval' is bound in the global context, but it may have been overwritten.
7573 // Compare it to the builtin 'GlobalEval' function to make sure.
7574 if (*callee != Top::global_context()->global_eval_fun() ||
7575 !args[1]->IsString()) {
7576 return MakePair(*callee, Top::context()->global()->global_receiver());
7577 }
7578
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007579 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
7580}
7581
7582
7583static ObjectPair Runtime_ResolvePossiblyDirectEvalNoLookup(Arguments args) {
7584 ASSERT(args.length() == 3);
7585 if (!args[0]->IsJSFunction()) {
7586 return MakePair(Top::ThrowIllegalOperation(), NULL);
7587 }
7588
7589 HandleScope scope;
7590 Handle<JSFunction> callee = args.at<JSFunction>(0);
7591
7592 // 'eval' is bound in the global context, but it may have been overwritten.
7593 // Compare it to the builtin 'GlobalEval' function to make sure.
7594 if (*callee != Top::global_context()->global_eval_fun() ||
7595 !args[1]->IsString()) {
7596 return MakePair(*callee, Top::context()->global()->global_receiver());
7597 }
7598
7599 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007600}
7601
7602
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007603static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
7604 // This utility adjusts the property attributes for newly created Function
7605 // object ("new Function(...)") by changing the map.
7606 // All it does is changing the prototype property to enumerable
7607 // as specified in ECMA262, 15.3.5.2.
7608 HandleScope scope;
7609 ASSERT(args.length() == 1);
7610 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7611 ASSERT(func->map()->instance_type() ==
7612 Top::function_instance_map()->instance_type());
7613 ASSERT(func->map()->instance_size() ==
7614 Top::function_instance_map()->instance_size());
7615 func->set_map(*Top::function_instance_map());
7616 return *func;
7617}
7618
7619
lrn@chromium.orgc4e51ac2010-08-09 09:47:21 +00007620static Object* Runtime_AllocateInNewSpace(Arguments args) {
7621 // Allocate a block of memory in NewSpace (filled with a filler).
7622 // Use as fallback for allocation in generated code when NewSpace
7623 // is full.
7624 ASSERT(args.length() == 1);
7625 CONVERT_ARG_CHECKED(Smi, size_smi, 0);
7626 int size = size_smi->value();
7627 RUNTIME_ASSERT(IsAligned(size, kPointerSize));
7628 RUNTIME_ASSERT(size > 0);
7629 static const int kMinFreeNewSpaceAfterGC =
7630 Heap::InitialSemiSpaceSize() * 3/4;
7631 RUNTIME_ASSERT(size <= kMinFreeNewSpaceAfterGC);
7632 Object* allocation = Heap::new_space()->AllocateRaw(size);
7633 if (!allocation->IsFailure()) {
7634 Heap::CreateFillerObjectAt(HeapObject::cast(allocation)->address(), size);
7635 }
7636 return allocation;
7637}
7638
7639
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007640// Push an array unto an array of arrays if it is not already in the
7641// array. Returns true if the element was pushed on the stack and
7642// false otherwise.
7643static Object* Runtime_PushIfAbsent(Arguments args) {
7644 ASSERT(args.length() == 2);
7645 CONVERT_CHECKED(JSArray, array, args[0]);
7646 CONVERT_CHECKED(JSArray, element, args[1]);
7647 RUNTIME_ASSERT(array->HasFastElements());
7648 int length = Smi::cast(array->length())->value();
7649 FixedArray* elements = FixedArray::cast(array->elements());
7650 for (int i = 0; i < length; i++) {
7651 if (elements->get(i) == element) return Heap::false_value();
7652 }
7653 Object* obj = array->SetFastElement(length, element);
7654 if (obj->IsFailure()) return obj;
7655 return Heap::true_value();
7656}
7657
7658
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007659/**
7660 * A simple visitor visits every element of Array's.
7661 * The backend storage can be a fixed array for fast elements case,
7662 * or a dictionary for sparse array. Since Dictionary is a subtype
7663 * of FixedArray, the class can be used by both fast and slow cases.
7664 * The second parameter of the constructor, fast_elements, specifies
7665 * whether the storage is a FixedArray or Dictionary.
7666 *
7667 * An index limit is used to deal with the situation that a result array
7668 * length overflows 32-bit non-negative integer.
7669 */
7670class ArrayConcatVisitor {
7671 public:
7672 ArrayConcatVisitor(Handle<FixedArray> storage,
7673 uint32_t index_limit,
7674 bool fast_elements) :
7675 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007676 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007677
7678 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007679 if (i >= index_limit_ - index_offset_) return;
7680 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007681
7682 if (fast_elements_) {
7683 ASSERT(index < static_cast<uint32_t>(storage_->length()));
7684 storage_->set(index, *elm);
7685
7686 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007687 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
7688 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007689 Factory::DictionaryAtNumberPut(dict, index, elm);
7690 if (!result.is_identical_to(dict))
7691 storage_ = result;
7692 }
7693 }
7694
7695 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007696 if (index_limit_ - index_offset_ < delta) {
7697 index_offset_ = index_limit_;
7698 } else {
7699 index_offset_ += delta;
7700 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007701 }
7702
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007703 Handle<FixedArray> storage() { return storage_; }
7704
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007705 private:
7706 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007707 // Limit on the accepted indices. Elements with indices larger than the
7708 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007709 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007710 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007711 uint32_t index_offset_;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007712 const bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007713};
7714
7715
ager@chromium.org3811b432009-10-28 14:53:37 +00007716template<class ExternalArrayClass, class ElementType>
7717static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
7718 bool elements_are_ints,
7719 bool elements_are_guaranteed_smis,
7720 uint32_t range,
7721 ArrayConcatVisitor* visitor) {
7722 Handle<ExternalArrayClass> array(
7723 ExternalArrayClass::cast(receiver->elements()));
7724 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
7725
7726 if (visitor != NULL) {
7727 if (elements_are_ints) {
7728 if (elements_are_guaranteed_smis) {
7729 for (uint32_t j = 0; j < len; j++) {
7730 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
7731 visitor->visit(j, e);
7732 }
7733 } else {
7734 for (uint32_t j = 0; j < len; j++) {
7735 int64_t val = static_cast<int64_t>(array->get(j));
7736 if (Smi::IsValid(static_cast<intptr_t>(val))) {
7737 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
7738 visitor->visit(j, e);
7739 } else {
7740 Handle<Object> e(
7741 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
7742 visitor->visit(j, e);
7743 }
7744 }
7745 }
7746 } else {
7747 for (uint32_t j = 0; j < len; j++) {
7748 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
7749 visitor->visit(j, e);
7750 }
7751 }
7752 }
7753
7754 return len;
7755}
7756
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007757/**
7758 * A helper function that visits elements of a JSObject. Only elements
7759 * whose index between 0 and range (exclusive) are visited.
7760 *
7761 * If the third parameter, visitor, is not NULL, the visitor is called
7762 * with parameters, 'visitor_index_offset + element index' and the element.
7763 *
7764 * It returns the number of visisted elements.
7765 */
7766static uint32_t IterateElements(Handle<JSObject> receiver,
7767 uint32_t range,
7768 ArrayConcatVisitor* visitor) {
7769 uint32_t num_of_elements = 0;
7770
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007771 switch (receiver->GetElementsKind()) {
7772 case JSObject::FAST_ELEMENTS: {
7773 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
7774 uint32_t len = elements->length();
7775 if (range < len) {
7776 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007777 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007778
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007779 for (uint32_t j = 0; j < len; j++) {
7780 Handle<Object> e(elements->get(j));
7781 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007782 num_of_elements++;
7783 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007784 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007785 }
7786 }
7787 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007788 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007789 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007790 case JSObject::PIXEL_ELEMENTS: {
7791 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
7792 uint32_t len = pixels->length();
7793 if (range < len) {
7794 len = range;
7795 }
7796
7797 for (uint32_t j = 0; j < len; j++) {
7798 num_of_elements++;
7799 if (visitor != NULL) {
7800 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
7801 visitor->visit(j, e);
7802 }
7803 }
7804 break;
7805 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007806 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
7807 num_of_elements =
7808 IterateExternalArrayElements<ExternalByteArray, int8_t>(
7809 receiver, true, true, range, visitor);
7810 break;
7811 }
7812 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7813 num_of_elements =
7814 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
7815 receiver, true, true, range, visitor);
7816 break;
7817 }
7818 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
7819 num_of_elements =
7820 IterateExternalArrayElements<ExternalShortArray, int16_t>(
7821 receiver, true, true, range, visitor);
7822 break;
7823 }
7824 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7825 num_of_elements =
7826 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
7827 receiver, true, true, range, visitor);
7828 break;
7829 }
7830 case JSObject::EXTERNAL_INT_ELEMENTS: {
7831 num_of_elements =
7832 IterateExternalArrayElements<ExternalIntArray, int32_t>(
7833 receiver, true, false, range, visitor);
7834 break;
7835 }
7836 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7837 num_of_elements =
7838 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
7839 receiver, true, false, range, visitor);
7840 break;
7841 }
7842 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
7843 num_of_elements =
7844 IterateExternalArrayElements<ExternalFloatArray, float>(
7845 receiver, false, false, range, visitor);
7846 break;
7847 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007848 case JSObject::DICTIONARY_ELEMENTS: {
7849 Handle<NumberDictionary> dict(receiver->element_dictionary());
7850 uint32_t capacity = dict->Capacity();
7851 for (uint32_t j = 0; j < capacity; j++) {
7852 Handle<Object> k(dict->KeyAt(j));
7853 if (dict->IsKey(*k)) {
7854 ASSERT(k->IsNumber());
7855 uint32_t index = static_cast<uint32_t>(k->Number());
7856 if (index < range) {
7857 num_of_elements++;
7858 if (visitor) {
7859 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
7860 }
7861 }
7862 }
7863 }
7864 break;
7865 }
7866 default:
7867 UNREACHABLE();
7868 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007869 }
7870
7871 return num_of_elements;
7872}
7873
7874
7875/**
7876 * A helper function that visits elements of an Array object, and elements
7877 * on its prototypes.
7878 *
7879 * Elements on prototypes are visited first, and only elements whose indices
7880 * less than Array length are visited.
7881 *
7882 * If a ArrayConcatVisitor object is given, the visitor is called with
7883 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007884 *
7885 * The returned number of elements is an upper bound on the actual number
7886 * of elements added. If the same element occurs in more than one object
7887 * in the array's prototype chain, it will be counted more than once, but
7888 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007889 */
7890static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
7891 ArrayConcatVisitor* visitor) {
7892 uint32_t range = static_cast<uint32_t>(array->length()->Number());
7893 Handle<Object> obj = array;
7894
7895 static const int kEstimatedPrototypes = 3;
7896 List< Handle<JSObject> > objects(kEstimatedPrototypes);
7897
7898 // Visit prototype first. If an element on the prototype is shadowed by
7899 // the inheritor using the same index, the ArrayConcatVisitor visits
7900 // the prototype element before the shadowing element.
7901 // The visitor can simply overwrite the old value by new value using
7902 // the same index. This follows Array::concat semantics.
7903 while (!obj->IsNull()) {
7904 objects.Add(Handle<JSObject>::cast(obj));
7905 obj = Handle<Object>(obj->GetPrototype());
7906 }
7907
7908 uint32_t nof_elements = 0;
7909 for (int i = objects.length() - 1; i >= 0; i--) {
7910 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007911 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007912 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007913
7914 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
7915 nof_elements = JSObject::kMaxElementCount;
7916 } else {
7917 nof_elements += encountered_elements;
7918 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007919 }
7920
7921 return nof_elements;
7922}
7923
7924
7925/**
7926 * A helper function of Runtime_ArrayConcat.
7927 *
7928 * The first argument is an Array of arrays and objects. It is the
7929 * same as the arguments array of Array::concat JS function.
7930 *
7931 * If an argument is an Array object, the function visits array
7932 * elements. If an argument is not an Array object, the function
7933 * visits the object as if it is an one-element array.
7934 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007935 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007936 * non-negative number is used as new length. For example, if one
7937 * array length is 2^32 - 1, second array length is 1, the
7938 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007939 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
7940 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007941 */
7942static uint32_t IterateArguments(Handle<JSArray> arguments,
7943 ArrayConcatVisitor* visitor) {
7944 uint32_t visited_elements = 0;
7945 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7946
7947 for (uint32_t i = 0; i < num_of_args; i++) {
7948 Handle<Object> obj(arguments->GetElement(i));
7949 if (obj->IsJSArray()) {
7950 Handle<JSArray> array = Handle<JSArray>::cast(obj);
7951 uint32_t len = static_cast<uint32_t>(array->length()->Number());
7952 uint32_t nof_elements =
7953 IterateArrayAndPrototypeElements(array, visitor);
7954 // Total elements of array and its prototype chain can be more than
7955 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007956 // the array length number of elements. We use the length as an estimate
7957 // for the actual number of elements added.
7958 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
7959 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
7960 visited_elements = JSArray::kMaxElementCount;
7961 } else {
7962 visited_elements += added_elements;
7963 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007964 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007965 } else {
7966 if (visitor) {
7967 visitor->visit(0, obj);
7968 visitor->increase_index_offset(1);
7969 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007970 if (visited_elements < JSArray::kMaxElementCount) {
7971 visited_elements++;
7972 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007973 }
7974 }
7975 return visited_elements;
7976}
7977
7978
7979/**
7980 * Array::concat implementation.
7981 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007982 * TODO(lrn): Fix non-compliance for very large concatenations and update to
7983 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007984 */
7985static Object* Runtime_ArrayConcat(Arguments args) {
7986 ASSERT(args.length() == 1);
7987 HandleScope handle_scope;
7988
7989 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
7990 Handle<JSArray> arguments(arg_arrays);
7991
7992 // Pass 1: estimate the number of elements of the result
7993 // (it could be more than real numbers if prototype has elements).
7994 uint32_t result_length = 0;
7995 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7996
7997 { AssertNoAllocation nogc;
7998 for (uint32_t i = 0; i < num_of_args; i++) {
7999 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008000 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008001 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008002 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008003 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
8004 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008005 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008006 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008007 if (JSObject::kMaxElementCount - result_length < length_estimate) {
8008 result_length = JSObject::kMaxElementCount;
8009 break;
8010 }
8011 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008012 }
8013 }
8014
8015 // Allocate an empty array, will set length and content later.
8016 Handle<JSArray> result = Factory::NewJSArray(0);
8017
8018 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
8019 // If estimated number of elements is more than half of length, a
8020 // fixed array (fast case) is more time and space-efficient than a
8021 // dictionary.
8022 bool fast_case = (estimate_nof_elements * 2) >= result_length;
8023
8024 Handle<FixedArray> storage;
8025 if (fast_case) {
8026 // The backing storage array must have non-existing elements to
8027 // preserve holes across concat operations.
8028 storage = Factory::NewFixedArrayWithHoles(result_length);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00008029 result->set_map(*Factory::GetFastElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008030 } else {
8031 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
8032 uint32_t at_least_space_for = estimate_nof_elements +
8033 (estimate_nof_elements >> 2);
8034 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008035 Factory::NewNumberDictionary(at_least_space_for));
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00008036 result->set_map(*Factory::GetSlowElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008037 }
8038
8039 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
8040
8041 ArrayConcatVisitor visitor(storage, result_length, fast_case);
8042
8043 IterateArguments(arguments, &visitor);
8044
8045 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00008046 // Please note the storage might have changed in the visitor.
8047 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008048
8049 return *result;
8050}
8051
8052
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008053// This will not allocate (flatten the string), but it may run
8054// very slowly for very deeply nested ConsStrings. For debugging use only.
8055static Object* Runtime_GlobalPrint(Arguments args) {
8056 NoHandleAllocation ha;
8057 ASSERT(args.length() == 1);
8058
8059 CONVERT_CHECKED(String, string, args[0]);
8060 StringInputBuffer buffer(string);
8061 while (buffer.has_more()) {
8062 uint16_t character = buffer.GetNext();
8063 PrintF("%c", character);
8064 }
8065 return string;
8066}
8067
ager@chromium.org5ec48922009-05-05 07:25:34 +00008068// Moves all own elements of an object, that are below a limit, to positions
8069// starting at zero. All undefined values are placed after non-undefined values,
8070// and are followed by non-existing element. Does not change the length
8071// property.
8072// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008073static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00008074 ASSERT(args.length() == 2);
8075 CONVERT_CHECKED(JSObject, object, args[0]);
8076 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
8077 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008078}
8079
8080
8081// Move contents of argument 0 (an array) to argument 1 (an array)
8082static Object* Runtime_MoveArrayContents(Arguments args) {
8083 ASSERT(args.length() == 2);
8084 CONVERT_CHECKED(JSArray, from, args[0]);
8085 CONVERT_CHECKED(JSArray, to, args[1]);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00008086 HeapObject* new_elements = from->elements();
8087 Object* new_map;
ricow@chromium.org0b9f8502010-08-18 07:45:01 +00008088 if (new_elements->map() == Heap::fixed_array_map() ||
8089 new_elements->map() == Heap::fixed_cow_array_map()) {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00008090 new_map = to->map()->GetFastElementsMap();
8091 } else {
8092 new_map = to->map()->GetSlowElementsMap();
8093 }
8094 if (new_map->IsFailure()) return new_map;
8095 to->set_map(Map::cast(new_map));
8096 to->set_elements(new_elements);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008097 to->set_length(from->length());
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00008098 Object* obj = from->ResetElements();
8099 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008100 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008101 return to;
8102}
8103
8104
8105// How many elements does this array have?
8106static Object* Runtime_EstimateNumberOfElements(Arguments args) {
8107 ASSERT(args.length() == 1);
8108 CONVERT_CHECKED(JSArray, array, args[0]);
8109 HeapObject* elements = array->elements();
8110 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008111 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008112 } else {
8113 return array->length();
8114 }
8115}
8116
8117
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008118static Object* Runtime_SwapElements(Arguments args) {
8119 HandleScope handle_scope;
8120
8121 ASSERT_EQ(3, args.length());
8122
ager@chromium.orgac091b72010-05-05 07:34:42 +00008123 CONVERT_ARG_CHECKED(JSObject, object, 0);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008124 Handle<Object> key1 = args.at<Object>(1);
8125 Handle<Object> key2 = args.at<Object>(2);
8126
8127 uint32_t index1, index2;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008128 if (!key1->ToArrayIndex(&index1)
8129 || !key2->ToArrayIndex(&index2)) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00008130 return Top::ThrowIllegalOperation();
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008131 }
8132
ager@chromium.orgac091b72010-05-05 07:34:42 +00008133 Handle<JSObject> jsobject = Handle<JSObject>::cast(object);
8134 Handle<Object> tmp1 = GetElement(jsobject, index1);
8135 Handle<Object> tmp2 = GetElement(jsobject, index2);
8136
8137 SetElement(jsobject, index1, tmp2);
8138 SetElement(jsobject, index2, tmp1);
8139
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008140 return Heap::undefined_value();
8141}
8142
8143
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008144// Returns an array that tells you where in the [0, length) interval an array
8145// might have elements. Can either return keys or intervals. Keys can have
8146// gaps in (undefined). Intervals can also span over some undefined keys.
8147static Object* Runtime_GetArrayKeys(Arguments args) {
8148 ASSERT(args.length() == 2);
8149 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00008150 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008151 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008152 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008153 // Create an array and get all the keys into it, then remove all the
8154 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008155 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008156 int keys_length = keys->length();
8157 for (int i = 0; i < keys_length; i++) {
8158 Object* key = keys->get(i);
8159 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008160 if (!key->ToArrayIndex(&index) || index >= length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008161 // Zap invalid keys.
8162 keys->set_undefined(i);
8163 }
8164 }
8165 return *Factory::NewJSArrayWithElements(keys);
8166 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008167 ASSERT(array->HasFastElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008168 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
8169 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008170 single_interval->set(0, Smi::FromInt(-1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008171 uint32_t actual_length =
8172 static_cast<uint32_t>(FixedArray::cast(array->elements())->length());
ager@chromium.org5ec48922009-05-05 07:25:34 +00008173 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008174 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00008175 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008176 single_interval->set(1, *length_object);
8177 return *Factory::NewJSArrayWithElements(single_interval);
8178 }
8179}
8180
8181
8182// DefineAccessor takes an optional final argument which is the
8183// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
8184// to the way accessors are implemented, it is set for both the getter
8185// and setter on the first call to DefineAccessor and ignored on
8186// subsequent calls.
8187static Object* Runtime_DefineAccessor(Arguments args) {
8188 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
8189 // Compute attributes.
8190 PropertyAttributes attributes = NONE;
8191 if (args.length() == 5) {
8192 CONVERT_CHECKED(Smi, attrs, args[4]);
8193 int value = attrs->value();
8194 // Only attribute bits should be set.
8195 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
8196 attributes = static_cast<PropertyAttributes>(value);
8197 }
8198
8199 CONVERT_CHECKED(JSObject, obj, args[0]);
8200 CONVERT_CHECKED(String, name, args[1]);
8201 CONVERT_CHECKED(Smi, flag, args[2]);
8202 CONVERT_CHECKED(JSFunction, fun, args[3]);
8203 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
8204}
8205
8206
8207static Object* Runtime_LookupAccessor(Arguments args) {
8208 ASSERT(args.length() == 3);
8209 CONVERT_CHECKED(JSObject, obj, args[0]);
8210 CONVERT_CHECKED(String, name, args[1]);
8211 CONVERT_CHECKED(Smi, flag, args[2]);
8212 return obj->LookupAccessor(name, flag->value() == 0);
8213}
8214
8215
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008216#ifdef ENABLE_DEBUGGER_SUPPORT
8217static Object* Runtime_DebugBreak(Arguments args) {
8218 ASSERT(args.length() == 0);
8219 return Execution::DebugBreakHelper();
8220}
8221
8222
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008223// Helper functions for wrapping and unwrapping stack frame ids.
8224static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008225 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008226 return Smi::FromInt(id >> 2);
8227}
8228
8229
8230static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
8231 return static_cast<StackFrame::Id>(wrapped->value() << 2);
8232}
8233
8234
8235// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00008236// args[0]: debug event listener function to set or null or undefined for
8237// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008238// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00008239static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008240 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00008241 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
8242 args[0]->IsUndefined() ||
8243 args[0]->IsNull());
8244 Handle<Object> callback = args.at<Object>(0);
8245 Handle<Object> data = args.at<Object>(1);
8246 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008247
8248 return Heap::undefined_value();
8249}
8250
8251
8252static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00008253 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008254 StackGuard::DebugBreak();
8255 return Heap::undefined_value();
8256}
8257
8258
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008259static Object* DebugLookupResultValue(Object* receiver, String* name,
8260 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00008261 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008262 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008263 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008264 case NORMAL:
8265 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008266 if (value->IsTheHole()) {
8267 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008268 }
8269 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008270 case FIELD:
8271 value =
8272 JSObject::cast(
8273 result->holder())->FastPropertyAt(result->GetFieldIndex());
8274 if (value->IsTheHole()) {
8275 return Heap::undefined_value();
8276 }
8277 return value;
8278 case CONSTANT_FUNCTION:
8279 return result->GetConstantFunction();
8280 case CALLBACKS: {
8281 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008282 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008283 value = receiver->GetPropertyWithCallback(
8284 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00008285 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008286 value = Top::pending_exception();
8287 Top::clear_pending_exception();
8288 if (caught_exception != NULL) {
8289 *caught_exception = true;
8290 }
8291 }
8292 return value;
8293 } else {
8294 return Heap::undefined_value();
8295 }
8296 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008297 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008298 case MAP_TRANSITION:
8299 case CONSTANT_TRANSITION:
8300 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008301 return Heap::undefined_value();
8302 default:
8303 UNREACHABLE();
8304 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008305 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008306 return Heap::undefined_value();
8307}
8308
8309
ager@chromium.org32912102009-01-16 10:38:43 +00008310// Get debugger related details for an object property.
8311// args[0]: object holding property
8312// args[1]: name of the property
8313//
8314// The array returned contains the following information:
8315// 0: Property value
8316// 1: Property details
8317// 2: Property value is exception
8318// 3: Getter function if defined
8319// 4: Setter function if defined
8320// Items 2-4 are only filled if the property has either a getter or a setter
8321// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00008322static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008323 HandleScope scope;
8324
8325 ASSERT(args.length() == 2);
8326
8327 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8328 CONVERT_ARG_CHECKED(String, name, 1);
8329
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008330 // Make sure to set the current context to the context before the debugger was
8331 // entered (if the debugger is entered). The reason for switching context here
8332 // is that for some property lookups (accessors and interceptors) callbacks
8333 // into the embedding application can occour, and the embedding application
8334 // could have the assumption that its own global context is the current
8335 // context and not some internal debugger context.
8336 SaveContext save;
8337 if (Debug::InDebugger()) {
8338 Top::set_context(*Debug::debugger_entry()->GetContext());
8339 }
8340
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008341 // Skip the global proxy as it has no properties and always delegates to the
8342 // real global object.
8343 if (obj->IsJSGlobalProxy()) {
8344 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
8345 }
8346
8347
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008348 // Check if the name is trivially convertible to an index and get the element
8349 // if so.
8350 uint32_t index;
8351 if (name->AsArrayIndex(&index)) {
8352 Handle<FixedArray> details = Factory::NewFixedArray(2);
8353 details->set(0, Runtime::GetElementOrCharAt(obj, index));
8354 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
8355 return *Factory::NewJSArrayWithElements(details);
8356 }
8357
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008358 // Find the number of objects making up this.
8359 int length = LocalPrototypeChainLength(*obj);
8360
8361 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008362 Handle<JSObject> jsproto = obj;
8363 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008364 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008365 jsproto->LocalLookup(*name, &result);
8366 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008367 // LookupResult is not GC safe as it holds raw object pointers.
8368 // GC can happen later in this code so put the required fields into
8369 // local variables using handles when required for later use.
8370 PropertyType result_type = result.type();
8371 Handle<Object> result_callback_obj;
8372 if (result_type == CALLBACKS) {
8373 result_callback_obj = Handle<Object>(result.GetCallbackObject());
8374 }
8375 Smi* property_details = result.GetPropertyDetails().AsSmi();
8376 // DebugLookupResultValue can cause GC so details from LookupResult needs
8377 // to be copied to handles before this.
8378 bool caught_exception = false;
8379 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
8380 &caught_exception);
8381 if (raw_value->IsFailure()) return raw_value;
8382 Handle<Object> value(raw_value);
8383
8384 // If the callback object is a fixed array then it contains JavaScript
8385 // getter and/or setter.
8386 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
8387 result_callback_obj->IsFixedArray();
8388 Handle<FixedArray> details =
8389 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
8390 details->set(0, *value);
8391 details->set(1, property_details);
8392 if (hasJavaScriptAccessors) {
8393 details->set(2,
8394 caught_exception ? Heap::true_value()
8395 : Heap::false_value());
8396 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
8397 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
8398 }
8399
8400 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008401 }
8402 if (i < length - 1) {
8403 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
8404 }
8405 }
8406
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008407 return Heap::undefined_value();
8408}
8409
8410
8411static Object* Runtime_DebugGetProperty(Arguments args) {
8412 HandleScope scope;
8413
8414 ASSERT(args.length() == 2);
8415
8416 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8417 CONVERT_ARG_CHECKED(String, name, 1);
8418
8419 LookupResult result;
8420 obj->Lookup(*name, &result);
8421 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008422 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008423 }
8424 return Heap::undefined_value();
8425}
8426
8427
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008428// Return the property type calculated from the property details.
8429// args[0]: smi with property details.
8430static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
8431 ASSERT(args.length() == 1);
8432 CONVERT_CHECKED(Smi, details, args[0]);
8433 PropertyType type = PropertyDetails(details).type();
8434 return Smi::FromInt(static_cast<int>(type));
8435}
8436
8437
8438// Return the property attribute calculated from the property details.
8439// args[0]: smi with property details.
8440static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
8441 ASSERT(args.length() == 1);
8442 CONVERT_CHECKED(Smi, details, args[0]);
8443 PropertyAttributes attributes = PropertyDetails(details).attributes();
8444 return Smi::FromInt(static_cast<int>(attributes));
8445}
8446
8447
8448// Return the property insertion index calculated from the property details.
8449// args[0]: smi with property details.
8450static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
8451 ASSERT(args.length() == 1);
8452 CONVERT_CHECKED(Smi, details, args[0]);
8453 int index = PropertyDetails(details).index();
8454 return Smi::FromInt(index);
8455}
8456
8457
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008458// Return property value from named interceptor.
8459// args[0]: object
8460// args[1]: property name
8461static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
8462 HandleScope scope;
8463 ASSERT(args.length() == 2);
8464 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8465 RUNTIME_ASSERT(obj->HasNamedInterceptor());
8466 CONVERT_ARG_CHECKED(String, name, 1);
8467
8468 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008469 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008470}
8471
8472
8473// Return element value from indexed interceptor.
8474// args[0]: object
8475// args[1]: index
8476static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
8477 HandleScope scope;
8478 ASSERT(args.length() == 2);
8479 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8480 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
8481 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
8482
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008483 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008484}
8485
8486
8487static Object* Runtime_CheckExecutionState(Arguments args) {
8488 ASSERT(args.length() >= 1);
8489 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00008490 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008491 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008492 return Top::Throw(Heap::illegal_execution_state_symbol());
8493 }
8494
8495 return Heap::true_value();
8496}
8497
8498
8499static Object* Runtime_GetFrameCount(Arguments args) {
8500 HandleScope scope;
8501 ASSERT(args.length() == 1);
8502
8503 // Check arguments.
8504 Object* result = Runtime_CheckExecutionState(args);
8505 if (result->IsFailure()) return result;
8506
8507 // Count all frames which are relevant to debugging stack trace.
8508 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008509 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008510 if (id == StackFrame::NO_ID) {
8511 // If there is no JavaScript stack frame count is 0.
8512 return Smi::FromInt(0);
8513 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008514 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
8515 return Smi::FromInt(n);
8516}
8517
8518
8519static const int kFrameDetailsFrameIdIndex = 0;
8520static const int kFrameDetailsReceiverIndex = 1;
8521static const int kFrameDetailsFunctionIndex = 2;
8522static const int kFrameDetailsArgumentCountIndex = 3;
8523static const int kFrameDetailsLocalCountIndex = 4;
8524static const int kFrameDetailsSourcePositionIndex = 5;
8525static const int kFrameDetailsConstructCallIndex = 6;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008526static const int kFrameDetailsAtReturnIndex = 7;
8527static const int kFrameDetailsDebuggerFrameIndex = 8;
8528static const int kFrameDetailsFirstDynamicIndex = 9;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008529
8530// Return an array with frame details
8531// args[0]: number: break id
8532// args[1]: number: frame index
8533//
8534// The array returned contains the following information:
8535// 0: Frame id
8536// 1: Receiver
8537// 2: Function
8538// 3: Argument count
8539// 4: Local count
8540// 5: Source position
8541// 6: Constructor call
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008542// 7: Is at return
8543// 8: Debugger frame
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008544// Arguments name, value
8545// Locals name, value
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008546// Return value if any
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008547static Object* Runtime_GetFrameDetails(Arguments args) {
8548 HandleScope scope;
8549 ASSERT(args.length() == 2);
8550
8551 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008552 Object* check = Runtime_CheckExecutionState(args);
8553 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008554 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8555
8556 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008557 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008558 if (id == StackFrame::NO_ID) {
8559 // If there are no JavaScript stack frames return undefined.
8560 return Heap::undefined_value();
8561 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008562 int count = 0;
8563 JavaScriptFrameIterator it(id);
8564 for (; !it.done(); it.Advance()) {
8565 if (count == index) break;
8566 count++;
8567 }
8568 if (it.done()) return Heap::undefined_value();
8569
8570 // Traverse the saved contexts chain to find the active context for the
8571 // selected frame.
8572 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008573 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008574 save = save->prev();
8575 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008576 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008577
8578 // Get the frame id.
8579 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
8580
8581 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008582 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008583
8584 // Check for constructor frame.
8585 bool constructor = it.frame()->IsConstructor();
8586
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008587 // Get scope info and read from it for local variable information.
8588 Handle<JSFunction> function(JSFunction::cast(it.frame()->function()));
ager@chromium.orgb5737492010-07-15 09:29:43 +00008589 Handle<SerializedScopeInfo> scope_info(function->shared()->scope_info());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008590 ScopeInfo<> info(*scope_info);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008591
8592 // Get the context.
8593 Handle<Context> context(Context::cast(it.frame()->context()));
8594
8595 // Get the locals names and values into a temporary array.
8596 //
8597 // TODO(1240907): Hide compiler-introduced stack variables
8598 // (e.g. .result)? For users of the debugger, they will probably be
8599 // confusing.
8600 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
8601 for (int i = 0; i < info.NumberOfLocals(); i++) {
8602 // Name of the local.
8603 locals->set(i * 2, *info.LocalName(i));
8604
8605 // Fetch the value of the local - either from the stack or from a
8606 // heap-allocated context.
8607 if (i < info.number_of_stack_slots()) {
8608 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
8609 } else {
8610 Handle<String> name = info.LocalName(i);
8611 // Traverse the context chain to the function context as all local
8612 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008613 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008614 context = Handle<Context>(context->previous());
8615 }
8616 ASSERT(context->is_function_context());
8617 locals->set(i * 2 + 1,
ager@chromium.orgb5737492010-07-15 09:29:43 +00008618 context->get(scope_info->ContextSlotIndex(*name, NULL)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008619 }
8620 }
8621
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008622 // Check whether this frame is positioned at return.
8623 int at_return = (index == 0) ? Debug::IsBreakAtReturn(it.frame()) : false;
8624
8625 // If positioned just before return find the value to be returned and add it
8626 // to the frame information.
8627 Handle<Object> return_value = Factory::undefined_value();
8628 if (at_return) {
8629 StackFrameIterator it2;
8630 Address internal_frame_sp = NULL;
8631 while (!it2.done()) {
8632 if (it2.frame()->is_internal()) {
8633 internal_frame_sp = it2.frame()->sp();
8634 } else {
8635 if (it2.frame()->is_java_script()) {
8636 if (it2.frame()->id() == it.frame()->id()) {
8637 // The internal frame just before the JavaScript frame contains the
8638 // value to return on top. A debug break at return will create an
8639 // internal frame to store the return value (eax/rax/r0) before
8640 // entering the debug break exit frame.
8641 if (internal_frame_sp != NULL) {
8642 return_value =
8643 Handle<Object>(Memory::Object_at(internal_frame_sp));
8644 break;
8645 }
8646 }
8647 }
8648
8649 // Indicate that the previous frame was not an internal frame.
8650 internal_frame_sp = NULL;
8651 }
8652 it2.Advance();
8653 }
8654 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008655
8656 // Now advance to the arguments adapter frame (if any). It contains all
8657 // the provided parameters whereas the function frame always have the number
8658 // of arguments matching the functions parameters. The rest of the
8659 // information (except for what is collected above) is the same.
8660 it.AdvanceToArgumentsFrame();
8661
8662 // Find the number of arguments to fill. At least fill the number of
8663 // parameters for the function and fill more if more parameters are provided.
8664 int argument_count = info.number_of_parameters();
8665 if (argument_count < it.frame()->GetProvidedParametersCount()) {
8666 argument_count = it.frame()->GetProvidedParametersCount();
8667 }
8668
8669 // Calculate the size of the result.
8670 int details_size = kFrameDetailsFirstDynamicIndex +
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008671 2 * (argument_count + info.NumberOfLocals()) +
8672 (at_return ? 1 : 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008673 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8674
8675 // Add the frame id.
8676 details->set(kFrameDetailsFrameIdIndex, *frame_id);
8677
8678 // Add the function (same as in function frame).
8679 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
8680
8681 // Add the arguments count.
8682 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
8683
8684 // Add the locals count
8685 details->set(kFrameDetailsLocalCountIndex,
8686 Smi::FromInt(info.NumberOfLocals()));
8687
8688 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00008689 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008690 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
8691 } else {
8692 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
8693 }
8694
8695 // Add the constructor information.
8696 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
8697
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008698 // Add the at return information.
8699 details->set(kFrameDetailsAtReturnIndex, Heap::ToBoolean(at_return));
8700
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008701 // Add information on whether this frame is invoked in the debugger context.
8702 details->set(kFrameDetailsDebuggerFrameIndex,
8703 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
8704
8705 // Fill the dynamic part.
8706 int details_index = kFrameDetailsFirstDynamicIndex;
8707
8708 // Add arguments name and value.
8709 for (int i = 0; i < argument_count; i++) {
8710 // Name of the argument.
8711 if (i < info.number_of_parameters()) {
8712 details->set(details_index++, *info.parameter_name(i));
8713 } else {
8714 details->set(details_index++, Heap::undefined_value());
8715 }
8716
8717 // Parameter value.
8718 if (i < it.frame()->GetProvidedParametersCount()) {
8719 details->set(details_index++, it.frame()->GetParameter(i));
8720 } else {
8721 details->set(details_index++, Heap::undefined_value());
8722 }
8723 }
8724
8725 // Add locals name and value from the temporary copy from the function frame.
8726 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
8727 details->set(details_index++, locals->get(i));
8728 }
8729
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008730 // Add the value being returned.
8731 if (at_return) {
8732 details->set(details_index++, *return_value);
8733 }
8734
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008735 // Add the receiver (same as in function frame).
8736 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
8737 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
8738 Handle<Object> receiver(it.frame()->receiver());
8739 if (!receiver->IsJSObject()) {
8740 // If the receiver is NOT a JSObject we have hit an optimization
8741 // where a value object is not converted into a wrapped JS objects.
8742 // To hide this optimization from the debugger, we wrap the receiver
8743 // by creating correct wrapper object based on the calling frame's
8744 // global context.
8745 it.Advance();
8746 Handle<Context> calling_frames_global_context(
8747 Context::cast(Context::cast(it.frame()->context())->global_context()));
8748 receiver = Factory::ToObject(receiver, calling_frames_global_context);
8749 }
8750 details->set(kFrameDetailsReceiverIndex, *receiver);
8751
8752 ASSERT_EQ(details_size, details_index);
8753 return *Factory::NewJSArrayWithElements(details);
8754}
8755
8756
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008757// Copy all the context locals into an object used to materialize a scope.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008758static void CopyContextLocalsToScopeObject(
8759 Handle<SerializedScopeInfo> serialized_scope_info,
8760 ScopeInfo<>& scope_info,
8761 Handle<Context> context,
8762 Handle<JSObject> scope_object) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008763 // Fill all context locals to the context extension.
8764 for (int i = Context::MIN_CONTEXT_SLOTS;
8765 i < scope_info.number_of_context_slots();
8766 i++) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00008767 int context_index = serialized_scope_info->ContextSlotIndex(
8768 *scope_info.context_slot_name(i), NULL);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008769
8770 // Don't include the arguments shadow (.arguments) context variable.
8771 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
8772 SetProperty(scope_object,
8773 scope_info.context_slot_name(i),
8774 Handle<Object>(context->get(context_index)), NONE);
8775 }
8776 }
8777}
8778
8779
8780// Create a plain JSObject which materializes the local scope for the specified
8781// frame.
8782static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
8783 Handle<JSFunction> function(JSFunction::cast(frame->function()));
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008784 Handle<SharedFunctionInfo> shared(function->shared());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008785 Handle<SerializedScopeInfo> serialized_scope_info(shared->scope_info());
8786 ScopeInfo<> scope_info(*serialized_scope_info);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008787
8788 // Allocate and initialize a JSObject with all the arguments, stack locals
8789 // heap locals and extension properties of the debugged function.
8790 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
8791
8792 // First fill all parameters.
8793 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8794 SetProperty(local_scope,
8795 scope_info.parameter_name(i),
8796 Handle<Object>(frame->GetParameter(i)), NONE);
8797 }
8798
8799 // Second fill all stack locals.
8800 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
8801 SetProperty(local_scope,
8802 scope_info.stack_slot_name(i),
8803 Handle<Object>(frame->GetExpression(i)), NONE);
8804 }
8805
8806 // Third fill all context locals.
8807 Handle<Context> frame_context(Context::cast(frame->context()));
8808 Handle<Context> function_context(frame_context->fcontext());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008809 CopyContextLocalsToScopeObject(serialized_scope_info, scope_info,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008810 function_context, local_scope);
8811
8812 // Finally copy any properties from the function context extension. This will
8813 // be variables introduced by eval.
8814 if (function_context->closure() == *function) {
8815 if (function_context->has_extension() &&
8816 !function_context->IsGlobalContext()) {
8817 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008818 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008819 for (int i = 0; i < keys->length(); i++) {
8820 // Names of variables introduced by eval are strings.
8821 ASSERT(keys->get(i)->IsString());
8822 Handle<String> key(String::cast(keys->get(i)));
8823 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
8824 }
8825 }
8826 }
8827 return local_scope;
8828}
8829
8830
8831// Create a plain JSObject which materializes the closure content for the
8832// context.
8833static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
8834 ASSERT(context->is_function_context());
8835
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008836 Handle<SharedFunctionInfo> shared(context->closure()->shared());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008837 Handle<SerializedScopeInfo> serialized_scope_info(shared->scope_info());
8838 ScopeInfo<> scope_info(*serialized_scope_info);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008839
8840 // Allocate and initialize a JSObject with all the content of theis function
8841 // closure.
8842 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
8843
8844 // Check whether the arguments shadow object exists.
8845 int arguments_shadow_index =
ager@chromium.orgb5737492010-07-15 09:29:43 +00008846 shared->scope_info()->ContextSlotIndex(Heap::arguments_shadow_symbol(),
8847 NULL);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008848 if (arguments_shadow_index >= 0) {
8849 // In this case all the arguments are available in the arguments shadow
8850 // object.
8851 Handle<JSObject> arguments_shadow(
8852 JSObject::cast(context->get(arguments_shadow_index)));
8853 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8854 SetProperty(closure_scope,
8855 scope_info.parameter_name(i),
8856 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
8857 }
8858 }
8859
8860 // Fill all context locals to the context extension.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008861 CopyContextLocalsToScopeObject(serialized_scope_info, scope_info,
8862 context, closure_scope);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008863
8864 // Finally copy any properties from the function context extension. This will
8865 // be variables introduced by eval.
8866 if (context->has_extension()) {
8867 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008868 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008869 for (int i = 0; i < keys->length(); i++) {
8870 // Names of variables introduced by eval are strings.
8871 ASSERT(keys->get(i)->IsString());
8872 Handle<String> key(String::cast(keys->get(i)));
8873 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
8874 }
8875 }
8876
8877 return closure_scope;
8878}
8879
8880
8881// Iterate over the actual scopes visible from a stack frame. All scopes are
8882// backed by an actual context except the local scope, which is inserted
8883// "artifically" in the context chain.
8884class ScopeIterator {
8885 public:
8886 enum ScopeType {
8887 ScopeTypeGlobal = 0,
8888 ScopeTypeLocal,
8889 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00008890 ScopeTypeClosure,
8891 // Every catch block contains an implicit with block (its parameter is
8892 // a JSContextExtensionObject) that extends current scope with a variable
8893 // holding exception object. Such with blocks are treated as scopes of their
8894 // own type.
8895 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008896 };
8897
8898 explicit ScopeIterator(JavaScriptFrame* frame)
8899 : frame_(frame),
8900 function_(JSFunction::cast(frame->function())),
8901 context_(Context::cast(frame->context())),
8902 local_done_(false),
8903 at_local_(false) {
8904
8905 // Check whether the first scope is actually a local scope.
8906 if (context_->IsGlobalContext()) {
8907 // If there is a stack slot for .result then this local scope has been
8908 // created for evaluating top level code and it is not a real local scope.
8909 // Checking for the existence of .result seems fragile, but the scope info
8910 // saved with the code object does not otherwise have that information.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008911 int index = function_->shared()->scope_info()->
8912 StackSlotIndex(Heap::result_symbol());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008913 at_local_ = index < 0;
8914 } else if (context_->is_function_context()) {
8915 at_local_ = true;
8916 }
8917 }
8918
8919 // More scopes?
8920 bool Done() { return context_.is_null(); }
8921
8922 // Move to the next scope.
8923 void Next() {
8924 // If at a local scope mark the local scope as passed.
8925 if (at_local_) {
8926 at_local_ = false;
8927 local_done_ = true;
8928
8929 // If the current context is not associated with the local scope the
8930 // current context is the next real scope, so don't move to the next
8931 // context in this case.
8932 if (context_->closure() != *function_) {
8933 return;
8934 }
8935 }
8936
8937 // The global scope is always the last in the chain.
8938 if (context_->IsGlobalContext()) {
8939 context_ = Handle<Context>();
8940 return;
8941 }
8942
8943 // Move to the next context.
8944 if (context_->is_function_context()) {
8945 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
8946 } else {
8947 context_ = Handle<Context>(context_->previous());
8948 }
8949
8950 // If passing the local scope indicate that the current scope is now the
8951 // local scope.
8952 if (!local_done_ &&
8953 (context_->IsGlobalContext() || (context_->is_function_context()))) {
8954 at_local_ = true;
8955 }
8956 }
8957
8958 // Return the type of the current scope.
8959 int Type() {
8960 if (at_local_) {
8961 return ScopeTypeLocal;
8962 }
8963 if (context_->IsGlobalContext()) {
8964 ASSERT(context_->global()->IsGlobalObject());
8965 return ScopeTypeGlobal;
8966 }
8967 if (context_->is_function_context()) {
8968 return ScopeTypeClosure;
8969 }
8970 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00008971 // Current scope is either an explicit with statement or a with statement
8972 // implicitely generated for a catch block.
8973 // If the extension object here is a JSContextExtensionObject then
8974 // current with statement is one frome a catch block otherwise it's a
8975 // regular with statement.
8976 if (context_->extension()->IsJSContextExtensionObject()) {
8977 return ScopeTypeCatch;
8978 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008979 return ScopeTypeWith;
8980 }
8981
8982 // Return the JavaScript object with the content of the current scope.
8983 Handle<JSObject> ScopeObject() {
8984 switch (Type()) {
8985 case ScopeIterator::ScopeTypeGlobal:
8986 return Handle<JSObject>(CurrentContext()->global());
8987 break;
8988 case ScopeIterator::ScopeTypeLocal:
8989 // Materialize the content of the local scope into a JSObject.
8990 return MaterializeLocalScope(frame_);
8991 break;
8992 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00008993 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008994 // Return the with object.
8995 return Handle<JSObject>(CurrentContext()->extension());
8996 break;
8997 case ScopeIterator::ScopeTypeClosure:
8998 // Materialize the content of the closure scope into a JSObject.
8999 return MaterializeClosure(CurrentContext());
9000 break;
9001 }
9002 UNREACHABLE();
9003 return Handle<JSObject>();
9004 }
9005
9006 // Return the context for this scope. For the local context there might not
9007 // be an actual context.
9008 Handle<Context> CurrentContext() {
9009 if (at_local_ && context_->closure() != *function_) {
9010 return Handle<Context>();
9011 }
9012 return context_;
9013 }
9014
9015#ifdef DEBUG
9016 // Debug print of the content of the current scope.
9017 void DebugPrint() {
9018 switch (Type()) {
9019 case ScopeIterator::ScopeTypeGlobal:
9020 PrintF("Global:\n");
9021 CurrentContext()->Print();
9022 break;
9023
9024 case ScopeIterator::ScopeTypeLocal: {
9025 PrintF("Local:\n");
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009026 ScopeInfo<> scope_info(function_->shared()->scope_info());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009027 scope_info.Print();
9028 if (!CurrentContext().is_null()) {
9029 CurrentContext()->Print();
9030 if (CurrentContext()->has_extension()) {
9031 Handle<JSObject> extension =
9032 Handle<JSObject>(CurrentContext()->extension());
9033 if (extension->IsJSContextExtensionObject()) {
9034 extension->Print();
9035 }
9036 }
9037 }
9038 break;
9039 }
9040
9041 case ScopeIterator::ScopeTypeWith: {
9042 PrintF("With:\n");
9043 Handle<JSObject> extension =
9044 Handle<JSObject>(CurrentContext()->extension());
9045 extension->Print();
9046 break;
9047 }
9048
ager@chromium.orga1645e22009-09-09 19:27:10 +00009049 case ScopeIterator::ScopeTypeCatch: {
9050 PrintF("Catch:\n");
9051 Handle<JSObject> extension =
9052 Handle<JSObject>(CurrentContext()->extension());
9053 extension->Print();
9054 break;
9055 }
9056
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009057 case ScopeIterator::ScopeTypeClosure: {
9058 PrintF("Closure:\n");
9059 CurrentContext()->Print();
9060 if (CurrentContext()->has_extension()) {
9061 Handle<JSObject> extension =
9062 Handle<JSObject>(CurrentContext()->extension());
9063 if (extension->IsJSContextExtensionObject()) {
9064 extension->Print();
9065 }
9066 }
9067 break;
9068 }
9069
9070 default:
9071 UNREACHABLE();
9072 }
9073 PrintF("\n");
9074 }
9075#endif
9076
9077 private:
9078 JavaScriptFrame* frame_;
9079 Handle<JSFunction> function_;
9080 Handle<Context> context_;
9081 bool local_done_;
9082 bool at_local_;
9083
9084 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
9085};
9086
9087
9088static Object* Runtime_GetScopeCount(Arguments args) {
9089 HandleScope scope;
9090 ASSERT(args.length() == 2);
9091
9092 // Check arguments.
9093 Object* check = Runtime_CheckExecutionState(args);
9094 if (check->IsFailure()) return check;
9095 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9096
9097 // Get the frame where the debugging is performed.
9098 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9099 JavaScriptFrameIterator it(id);
9100 JavaScriptFrame* frame = it.frame();
9101
9102 // Count the visible scopes.
9103 int n = 0;
9104 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
9105 n++;
9106 }
9107
9108 return Smi::FromInt(n);
9109}
9110
9111
9112static const int kScopeDetailsTypeIndex = 0;
9113static const int kScopeDetailsObjectIndex = 1;
9114static const int kScopeDetailsSize = 2;
9115
9116// Return an array with scope details
9117// args[0]: number: break id
9118// args[1]: number: frame index
9119// args[2]: number: scope index
9120//
9121// The array returned contains the following information:
9122// 0: Scope type
9123// 1: Scope object
9124static Object* Runtime_GetScopeDetails(Arguments args) {
9125 HandleScope scope;
9126 ASSERT(args.length() == 3);
9127
9128 // Check arguments.
9129 Object* check = Runtime_CheckExecutionState(args);
9130 if (check->IsFailure()) return check;
9131 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9132 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
9133
9134 // Get the frame where the debugging is performed.
9135 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9136 JavaScriptFrameIterator frame_it(id);
9137 JavaScriptFrame* frame = frame_it.frame();
9138
9139 // Find the requested scope.
9140 int n = 0;
9141 ScopeIterator it(frame);
9142 for (; !it.Done() && n < index; it.Next()) {
9143 n++;
9144 }
9145 if (it.Done()) {
9146 return Heap::undefined_value();
9147 }
9148
9149 // Calculate the size of the result.
9150 int details_size = kScopeDetailsSize;
9151 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
9152
9153 // Fill in scope details.
9154 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
9155 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
9156
9157 return *Factory::NewJSArrayWithElements(details);
9158}
9159
9160
9161static Object* Runtime_DebugPrintScopes(Arguments args) {
9162 HandleScope scope;
9163 ASSERT(args.length() == 0);
9164
9165#ifdef DEBUG
9166 // Print the scopes for the top frame.
9167 StackFrameLocator locator;
9168 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
9169 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
9170 it.DebugPrint();
9171 }
9172#endif
9173 return Heap::undefined_value();
9174}
9175
9176
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009177static Object* Runtime_GetCFrames(Arguments args) {
9178 HandleScope scope;
9179 ASSERT(args.length() == 1);
9180 Object* result = Runtime_CheckExecutionState(args);
9181 if (result->IsFailure()) return result;
9182
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00009183#if V8_HOST_ARCH_64_BIT
9184 UNIMPLEMENTED();
9185 return Heap::undefined_value();
9186#else
9187
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009188 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009189 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
9190 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009191 if (frames_count == OS::kStackWalkError) {
9192 return Heap::undefined_value();
9193 }
9194
9195 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
9196 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
9197 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
9198 for (int i = 0; i < frames_count; i++) {
9199 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
9200 frame_value->SetProperty(
9201 *address_str,
9202 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
9203 NONE);
9204
9205 // Get the stack walk text for this frame.
9206 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009207 int frame_text_length = StrLength(frames[i].text);
9208 if (frame_text_length > 0) {
9209 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009210 frame_text = Factory::NewStringFromAscii(str);
9211 }
9212
9213 if (!frame_text.is_null()) {
9214 frame_value->SetProperty(*text_str, *frame_text, NONE);
9215 }
9216
9217 frames_array->set(i, *frame_value);
9218 }
9219 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00009220#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009221}
9222
9223
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00009224static Object* Runtime_GetThreadCount(Arguments args) {
9225 HandleScope scope;
9226 ASSERT(args.length() == 1);
9227
9228 // Check arguments.
9229 Object* result = Runtime_CheckExecutionState(args);
9230 if (result->IsFailure()) return result;
9231
9232 // Count all archived V8 threads.
9233 int n = 0;
9234 for (ThreadState* thread = ThreadState::FirstInUse();
9235 thread != NULL;
9236 thread = thread->Next()) {
9237 n++;
9238 }
9239
9240 // Total number of threads is current thread and archived threads.
9241 return Smi::FromInt(n + 1);
9242}
9243
9244
9245static const int kThreadDetailsCurrentThreadIndex = 0;
9246static const int kThreadDetailsThreadIdIndex = 1;
9247static const int kThreadDetailsSize = 2;
9248
9249// Return an array with thread details
9250// args[0]: number: break id
9251// args[1]: number: thread index
9252//
9253// The array returned contains the following information:
9254// 0: Is current thread?
9255// 1: Thread id
9256static Object* Runtime_GetThreadDetails(Arguments args) {
9257 HandleScope scope;
9258 ASSERT(args.length() == 2);
9259
9260 // Check arguments.
9261 Object* check = Runtime_CheckExecutionState(args);
9262 if (check->IsFailure()) return check;
9263 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
9264
9265 // Allocate array for result.
9266 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
9267
9268 // Thread index 0 is current thread.
9269 if (index == 0) {
9270 // Fill the details.
9271 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
9272 details->set(kThreadDetailsThreadIdIndex,
9273 Smi::FromInt(ThreadManager::CurrentId()));
9274 } else {
9275 // Find the thread with the requested index.
9276 int n = 1;
9277 ThreadState* thread = ThreadState::FirstInUse();
9278 while (index != n && thread != NULL) {
9279 thread = thread->Next();
9280 n++;
9281 }
9282 if (thread == NULL) {
9283 return Heap::undefined_value();
9284 }
9285
9286 // Fill the details.
9287 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
9288 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
9289 }
9290
9291 // Convert to JS array and return.
9292 return *Factory::NewJSArrayWithElements(details);
9293}
9294
9295
whesse@chromium.orge90029b2010-08-02 11:52:17 +00009296// Sets the disable break state
9297// args[0]: disable break state
9298static Object* Runtime_SetDisableBreak(Arguments args) {
9299 HandleScope scope;
9300 ASSERT(args.length() == 1);
9301 CONVERT_BOOLEAN_CHECKED(disable_break, args[0]);
9302 Debug::set_disable_break(disable_break);
9303 return Heap::undefined_value();
9304}
9305
9306
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009307static Object* Runtime_GetBreakLocations(Arguments args) {
9308 HandleScope scope;
9309 ASSERT(args.length() == 1);
9310
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009311 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9312 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009313 // Find the number of break points
9314 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
9315 if (break_locations->IsUndefined()) return Heap::undefined_value();
9316 // Return array as JS array
9317 return *Factory::NewJSArrayWithElements(
9318 Handle<FixedArray>::cast(break_locations));
9319}
9320
9321
9322// Set a break point in a function
9323// args[0]: function
9324// args[1]: number: break source position (within the function source)
9325// args[2]: number: break point object
9326static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
9327 HandleScope scope;
9328 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009329 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9330 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009331 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9332 RUNTIME_ASSERT(source_position >= 0);
9333 Handle<Object> break_point_object_arg = args.at<Object>(2);
9334
9335 // Set break point.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009336 Debug::SetBreakPoint(shared, break_point_object_arg, &source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009337
lrn@chromium.org32d961d2010-06-30 09:09:34 +00009338 return Smi::FromInt(source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009339}
9340
9341
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009342Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
9343 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009344 // Iterate the heap looking for SharedFunctionInfo generated from the
9345 // script. The inner most SharedFunctionInfo containing the source position
9346 // for the requested break point is found.
9347 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
9348 // which is found is not compiled it is compiled and the heap is iterated
9349 // again as the compilation might create inner functions from the newly
9350 // compiled function and the actual requested break point might be in one of
9351 // these functions.
9352 bool done = false;
9353 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00009354 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009355 Handle<SharedFunctionInfo> target;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009356 while (!done) {
9357 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009358 for (HeapObject* obj = iterator.next();
9359 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009360 if (obj->IsSharedFunctionInfo()) {
9361 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
9362 if (shared->script() == *script) {
9363 // If the SharedFunctionInfo found has the requested script data and
9364 // contains the source position it is a candidate.
9365 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00009366 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009367 start_position = shared->start_position();
9368 }
9369 if (start_position <= position &&
9370 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00009371 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009372 // candidate this is the new candidate.
9373 if (target.is_null()) {
9374 target_start_position = start_position;
9375 target = shared;
9376 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00009377 if (target_start_position == start_position &&
9378 shared->end_position() == target->end_position()) {
9379 // If a top-level function contain only one function
9380 // declartion the source for the top-level and the function is
9381 // the same. In that case prefer the non top-level function.
9382 if (!shared->is_toplevel()) {
9383 target_start_position = start_position;
9384 target = shared;
9385 }
9386 } else if (target_start_position <= start_position &&
9387 shared->end_position() <= target->end_position()) {
9388 // This containment check includes equality as a function inside
9389 // a top-level function can share either start or end position
9390 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009391 target_start_position = start_position;
9392 target = shared;
9393 }
9394 }
9395 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009396 }
9397 }
9398 }
9399
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009400 if (target.is_null()) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009401 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009402 }
9403
9404 // If the candidate found is compiled we are done. NOTE: when lazy
9405 // compilation of inner functions is introduced some additional checking
9406 // needs to be done here to compile inner functions.
9407 done = target->is_compiled();
9408 if (!done) {
9409 // If the candidate is not compiled compile it to reveal any inner
9410 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009411 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009412 }
9413 }
9414
9415 return *target;
9416}
9417
9418
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009419// Changes the state of a break point in a script and returns source position
9420// where break point was set. NOTE: Regarding performance see the NOTE for
9421// GetScriptFromScriptData.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009422// args[0]: script to set break point in
9423// args[1]: number: break source position (within the script source)
9424// args[2]: number: break point object
9425static Object* Runtime_SetScriptBreakPoint(Arguments args) {
9426 HandleScope scope;
9427 ASSERT(args.length() == 3);
9428 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
9429 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9430 RUNTIME_ASSERT(source_position >= 0);
9431 Handle<Object> break_point_object_arg = args.at<Object>(2);
9432
9433 // Get the script from the script wrapper.
9434 RUNTIME_ASSERT(wrapper->value()->IsScript());
9435 Handle<Script> script(Script::cast(wrapper->value()));
9436
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009437 Object* result = Runtime::FindSharedFunctionInfoInScript(
9438 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009439 if (!result->IsUndefined()) {
9440 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
9441 // Find position within function. The script position might be before the
9442 // source position of the first function.
9443 int position;
9444 if (shared->start_position() > source_position) {
9445 position = 0;
9446 } else {
9447 position = source_position - shared->start_position();
9448 }
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009449 Debug::SetBreakPoint(shared, break_point_object_arg, &position);
9450 position += shared->start_position();
9451 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009452 }
9453 return Heap::undefined_value();
9454}
9455
9456
9457// Clear a break point
9458// args[0]: number: break point object
9459static Object* Runtime_ClearBreakPoint(Arguments args) {
9460 HandleScope scope;
9461 ASSERT(args.length() == 1);
9462 Handle<Object> break_point_object_arg = args.at<Object>(0);
9463
9464 // Clear break point.
9465 Debug::ClearBreakPoint(break_point_object_arg);
9466
9467 return Heap::undefined_value();
9468}
9469
9470
9471// Change the state of break on exceptions
9472// args[0]: boolean indicating uncaught exceptions
9473// args[1]: boolean indicating on/off
9474static Object* Runtime_ChangeBreakOnException(Arguments args) {
9475 HandleScope scope;
9476 ASSERT(args.length() == 2);
9477 ASSERT(args[0]->IsNumber());
9478 ASSERT(args[1]->IsBoolean());
9479
9480 // Update break point state
9481 ExceptionBreakType type =
9482 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
9483 bool enable = args[1]->ToBoolean()->IsTrue();
9484 Debug::ChangeBreakOnException(type, enable);
9485 return Heap::undefined_value();
9486}
9487
9488
9489// Prepare for stepping
9490// args[0]: break id for checking execution state
9491// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00009492// args[2]: number of times to perform the step, for step out it is the number
9493// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009494static Object* Runtime_PrepareStep(Arguments args) {
9495 HandleScope scope;
9496 ASSERT(args.length() == 3);
9497 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009498 Object* check = Runtime_CheckExecutionState(args);
9499 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009500 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
9501 return Top::Throw(Heap::illegal_argument_symbol());
9502 }
9503
9504 // Get the step action and check validity.
9505 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
9506 if (step_action != StepIn &&
9507 step_action != StepNext &&
9508 step_action != StepOut &&
9509 step_action != StepInMin &&
9510 step_action != StepMin) {
9511 return Top::Throw(Heap::illegal_argument_symbol());
9512 }
9513
9514 // Get the number of steps.
9515 int step_count = NumberToInt32(args[2]);
9516 if (step_count < 1) {
9517 return Top::Throw(Heap::illegal_argument_symbol());
9518 }
9519
ager@chromium.orga1645e22009-09-09 19:27:10 +00009520 // Clear all current stepping setup.
9521 Debug::ClearStepping();
9522
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009523 // Prepare step.
9524 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
9525 return Heap::undefined_value();
9526}
9527
9528
9529// Clear all stepping set by PrepareStep.
9530static Object* Runtime_ClearStepping(Arguments args) {
9531 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009532 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009533 Debug::ClearStepping();
9534 return Heap::undefined_value();
9535}
9536
9537
9538// Creates a copy of the with context chain. The copy of the context chain is
9539// is linked to the function context supplied.
9540static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
9541 Handle<Context> function_context) {
9542 // At the bottom of the chain. Return the function context to link to.
9543 if (context_chain->is_function_context()) {
9544 return function_context;
9545 }
9546
9547 // Recursively copy the with contexts.
9548 Handle<Context> previous(context_chain->previous());
9549 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
9550 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009551 CopyWithContextChain(function_context, previous),
9552 extension,
9553 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009554}
9555
9556
9557// Helper function to find or create the arguments object for
9558// Runtime_DebugEvaluate.
9559static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
9560 Handle<JSFunction> function,
ager@chromium.orgb5737492010-07-15 09:29:43 +00009561 Handle<SerializedScopeInfo> scope_info,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009562 const ScopeInfo<>* sinfo,
9563 Handle<Context> function_context) {
9564 // Try to find the value of 'arguments' to pass as parameter. If it is not
9565 // found (that is the debugged function does not reference 'arguments' and
9566 // does not support eval) then create an 'arguments' object.
9567 int index;
9568 if (sinfo->number_of_stack_slots() > 0) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00009569 index = scope_info->StackSlotIndex(Heap::arguments_symbol());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009570 if (index != -1) {
9571 return Handle<Object>(frame->GetExpression(index));
9572 }
9573 }
9574
9575 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00009576 index = scope_info->ContextSlotIndex(Heap::arguments_symbol(), NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009577 if (index != -1) {
9578 return Handle<Object>(function_context->get(index));
9579 }
9580 }
9581
9582 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009583 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
9584 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009585
9586 AssertNoAllocation no_gc;
9587 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009588 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009589 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009590 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009591 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009592 return arguments;
9593}
9594
9595
9596// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00009597// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009598// extension part has all the parameters and locals of the function on the
9599// stack frame. A function which calls eval with the code to evaluate is then
9600// compiled in this context and called in this context. As this context
9601// replaces the context of the function on the stack frame a new (empty)
9602// function is created as well to be used as the closure for the context.
9603// This function and the context acts as replacements for the function on the
9604// stack frame presenting the same view of the values of parameters and
9605// local variables as if the piece of JavaScript was evaluated at the point
9606// where the function on the stack frame is currently stopped.
9607static Object* Runtime_DebugEvaluate(Arguments args) {
9608 HandleScope scope;
9609
9610 // Check the execution state and decode arguments frame and source to be
9611 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009612 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009613 Object* check_result = Runtime_CheckExecutionState(args);
9614 if (check_result->IsFailure()) return check_result;
9615 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9616 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009617 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
9618
9619 // Handle the processing of break.
9620 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009621
9622 // Get the frame where the debugging is performed.
9623 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9624 JavaScriptFrameIterator it(id);
9625 JavaScriptFrame* frame = it.frame();
9626 Handle<JSFunction> function(JSFunction::cast(frame->function()));
ager@chromium.orgb5737492010-07-15 09:29:43 +00009627 Handle<SerializedScopeInfo> scope_info(function->shared()->scope_info());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009628 ScopeInfo<> sinfo(*scope_info);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009629
9630 // Traverse the saved contexts chain to find the active context for the
9631 // selected frame.
9632 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009633 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009634 save = save->prev();
9635 }
9636 ASSERT(save != NULL);
9637 SaveContext savex;
9638 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009639
9640 // Create the (empty) function replacing the function on the stack frame for
9641 // the purpose of evaluating in the context created below. It is important
9642 // that this function does not describe any parameters and local variables
9643 // in the context. If it does then this will cause problems with the lookup
9644 // in Context::Lookup, where context slots for parameters and local variables
9645 // are looked at before the extension object.
9646 Handle<JSFunction> go_between =
9647 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
9648 go_between->set_context(function->context());
9649#ifdef DEBUG
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009650 ScopeInfo<> go_between_sinfo(go_between->shared()->scope_info());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009651 ASSERT(go_between_sinfo.number_of_parameters() == 0);
9652 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
9653#endif
9654
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009655 // Materialize the content of the local scope into a JSObject.
9656 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009657
9658 // Allocate a new context for the debug evaluation and set the extension
9659 // object build.
9660 Handle<Context> context =
9661 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009662 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009663 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009664 Handle<Context> frame_context(Context::cast(frame->context()));
9665 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009666 context = CopyWithContextChain(frame_context, context);
9667
9668 // Wrap the evaluation statement in a new function compiled in the newly
9669 // created context. The function has one parameter which has to be called
9670 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00009671 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009672 // function(arguments,__source__) {return eval(__source__);}
9673 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00009674 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009675 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009676 Handle<String> function_source =
9677 Factory::NewStringFromAscii(Vector<const char>(source_str,
9678 source_str_length));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009679 Handle<SharedFunctionInfo> shared =
ager@chromium.org381abbb2009-02-25 13:23:22 +00009680 Compiler::CompileEval(function_source,
9681 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00009682 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00009683 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009684 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009685 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009686 Factory::NewFunctionFromSharedFunctionInfo(shared, context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009687
9688 // Invoke the result of the compilation to get the evaluation function.
9689 bool has_pending_exception;
9690 Handle<Object> receiver(frame->receiver());
9691 Handle<Object> evaluation_function =
9692 Execution::Call(compiled_function, receiver, 0, NULL,
9693 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009694 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009695
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009696 Handle<Object> arguments = GetArgumentsObject(frame, function, scope_info,
9697 &sinfo, function_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009698
9699 // Invoke the evaluation function and return the result.
9700 const int argc = 2;
9701 Object** argv[argc] = { arguments.location(),
9702 Handle<Object>::cast(source).location() };
9703 Handle<Object> result =
9704 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
9705 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009706 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009707
9708 // Skip the global proxy as it has no properties and always delegates to the
9709 // real global object.
9710 if (result->IsJSGlobalProxy()) {
9711 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
9712 }
9713
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009714 return *result;
9715}
9716
9717
9718static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
9719 HandleScope scope;
9720
9721 // Check the execution state and decode arguments frame and source to be
9722 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009723 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009724 Object* check_result = Runtime_CheckExecutionState(args);
9725 if (check_result->IsFailure()) return check_result;
9726 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009727 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
9728
9729 // Handle the processing of break.
9730 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009731
9732 // Enter the top context from before the debugger was invoked.
9733 SaveContext save;
9734 SaveContext* top = &save;
9735 while (top != NULL && *top->context() == *Debug::debug_context()) {
9736 top = top->prev();
9737 }
9738 if (top != NULL) {
9739 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009740 }
9741
9742 // Get the global context now set to the top context from before the
9743 // debugger was invoked.
9744 Handle<Context> context = Top::global_context();
9745
9746 // Compile the source to be evaluated.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009747 Handle<SharedFunctionInfo> shared =
9748 Compiler::CompileEval(source,
9749 context,
9750 true,
9751 Compiler::DONT_VALIDATE_JSON);
9752 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009753 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009754 Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
9755 context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009756
9757 // Invoke the result of the compilation to get the evaluation function.
9758 bool has_pending_exception;
9759 Handle<Object> receiver = Top::global();
9760 Handle<Object> result =
9761 Execution::Call(compiled_function, receiver, 0, NULL,
9762 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009763 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009764 return *result;
9765}
9766
9767
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009768static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
9769 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009770 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009771
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009772 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009773 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009774
9775 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009776 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00009777 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
9778 // Get the script wrapper in a local handle before calling GetScriptWrapper,
9779 // because using
9780 // instances->set(i, *GetScriptWrapper(script))
9781 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
9782 // already have deferenced the instances handle.
9783 Handle<JSValue> wrapper = GetScriptWrapper(script);
9784 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009785 }
9786
9787 // Return result as a JS array.
9788 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
9789 Handle<JSArray>::cast(result)->SetContent(*instances);
9790 return *result;
9791}
9792
9793
9794// Helper function used by Runtime_DebugReferencedBy below.
9795static int DebugReferencedBy(JSObject* target,
9796 Object* instance_filter, int max_references,
9797 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009798 JSFunction* arguments_function) {
9799 NoHandleAllocation ha;
9800 AssertNoAllocation no_alloc;
9801
9802 // Iterate the heap.
9803 int count = 0;
9804 JSObject* last = NULL;
9805 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009806 HeapObject* heap_obj = NULL;
9807 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009808 (max_references == 0 || count < max_references)) {
9809 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009810 if (heap_obj->IsJSObject()) {
9811 // Skip context extension objects and argument arrays as these are
9812 // checked in the context of functions using them.
9813 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00009814 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009815 obj->map()->constructor() == arguments_function) {
9816 continue;
9817 }
9818
9819 // Check if the JS object has a reference to the object looked for.
9820 if (obj->ReferencesObject(target)) {
9821 // Check instance filter if supplied. This is normally used to avoid
9822 // references from mirror objects (see Runtime_IsInPrototypeChain).
9823 if (!instance_filter->IsUndefined()) {
9824 Object* V = obj;
9825 while (true) {
9826 Object* prototype = V->GetPrototype();
9827 if (prototype->IsNull()) {
9828 break;
9829 }
9830 if (instance_filter == prototype) {
9831 obj = NULL; // Don't add this object.
9832 break;
9833 }
9834 V = prototype;
9835 }
9836 }
9837
9838 if (obj != NULL) {
9839 // Valid reference found add to instance array if supplied an update
9840 // count.
9841 if (instances != NULL && count < instances_size) {
9842 instances->set(count, obj);
9843 }
9844 last = obj;
9845 count++;
9846 }
9847 }
9848 }
9849 }
9850
9851 // Check for circular reference only. This can happen when the object is only
9852 // referenced from mirrors and has a circular reference in which case the
9853 // object is not really alive and would have been garbage collected if not
9854 // referenced from the mirror.
9855 if (count == 1 && last == target) {
9856 count = 0;
9857 }
9858
9859 // Return the number of referencing objects found.
9860 return count;
9861}
9862
9863
9864// Scan the heap for objects with direct references to an object
9865// args[0]: the object to find references to
9866// args[1]: constructor function for instances to exclude (Mirror)
9867// args[2]: the the maximum number of objects to return
9868static Object* Runtime_DebugReferencedBy(Arguments args) {
9869 ASSERT(args.length() == 3);
9870
9871 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009872 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009873
9874 // Check parameters.
9875 CONVERT_CHECKED(JSObject, target, args[0]);
9876 Object* instance_filter = args[1];
9877 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
9878 instance_filter->IsJSObject());
9879 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
9880 RUNTIME_ASSERT(max_references >= 0);
9881
9882 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009883 JSObject* arguments_boilerplate =
9884 Top::context()->global_context()->arguments_boilerplate();
9885 JSFunction* arguments_function =
9886 JSFunction::cast(arguments_boilerplate->map()->constructor());
9887
9888 // Get the number of referencing objects.
9889 int count;
9890 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009891 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009892
9893 // Allocate an array to hold the result.
9894 Object* object = Heap::AllocateFixedArray(count);
9895 if (object->IsFailure()) return object;
9896 FixedArray* instances = FixedArray::cast(object);
9897
9898 // Fill the referencing objects.
9899 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009900 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009901
9902 // Return result as JS array.
9903 Object* result =
9904 Heap::AllocateJSObject(
9905 Top::context()->global_context()->array_function());
9906 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9907 return result;
9908}
9909
9910
9911// Helper function used by Runtime_DebugConstructedBy below.
9912static int DebugConstructedBy(JSFunction* constructor, int max_references,
9913 FixedArray* instances, int instances_size) {
9914 AssertNoAllocation no_alloc;
9915
9916 // Iterate the heap.
9917 int count = 0;
9918 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009919 HeapObject* heap_obj = NULL;
9920 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009921 (max_references == 0 || count < max_references)) {
9922 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009923 if (heap_obj->IsJSObject()) {
9924 JSObject* obj = JSObject::cast(heap_obj);
9925 if (obj->map()->constructor() == constructor) {
9926 // Valid reference found add to instance array if supplied an update
9927 // count.
9928 if (instances != NULL && count < instances_size) {
9929 instances->set(count, obj);
9930 }
9931 count++;
9932 }
9933 }
9934 }
9935
9936 // Return the number of referencing objects found.
9937 return count;
9938}
9939
9940
9941// Scan the heap for objects constructed by a specific function.
9942// args[0]: the constructor to find instances of
9943// args[1]: the the maximum number of objects to return
9944static Object* Runtime_DebugConstructedBy(Arguments args) {
9945 ASSERT(args.length() == 2);
9946
9947 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009948 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009949
9950 // Check parameters.
9951 CONVERT_CHECKED(JSFunction, constructor, args[0]);
9952 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
9953 RUNTIME_ASSERT(max_references >= 0);
9954
9955 // Get the number of referencing objects.
9956 int count;
9957 count = DebugConstructedBy(constructor, max_references, NULL, 0);
9958
9959 // Allocate an array to hold the result.
9960 Object* object = Heap::AllocateFixedArray(count);
9961 if (object->IsFailure()) return object;
9962 FixedArray* instances = FixedArray::cast(object);
9963
9964 // Fill the referencing objects.
9965 count = DebugConstructedBy(constructor, max_references, instances, count);
9966
9967 // Return result as JS array.
9968 Object* result =
9969 Heap::AllocateJSObject(
9970 Top::context()->global_context()->array_function());
9971 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9972 return result;
9973}
9974
9975
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009976// Find the effective prototype object as returned by __proto__.
9977// args[0]: the object to find the prototype for.
9978static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009979 ASSERT(args.length() == 1);
9980
9981 CONVERT_CHECKED(JSObject, obj, args[0]);
9982
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009983 // Use the __proto__ accessor.
9984 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009985}
9986
9987
9988static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009989 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009990 CPU::DebugBreak();
9991 return Heap::undefined_value();
9992}
9993
9994
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009995static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009996#ifdef DEBUG
9997 HandleScope scope;
9998 ASSERT(args.length() == 1);
9999 // Get the function and make sure it is compiled.
10000 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010001 Handle<SharedFunctionInfo> shared(func->shared());
10002 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010003 return Failure::Exception();
10004 }
10005 func->code()->PrintLn();
10006#endif // DEBUG
10007 return Heap::undefined_value();
10008}
ager@chromium.org9085a012009-05-11 19:22:57 +000010009
10010
ager@chromium.org18ad94b2009-09-02 08:22:29 +000010011static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
10012#ifdef DEBUG
10013 HandleScope scope;
10014 ASSERT(args.length() == 1);
10015 // Get the function and make sure it is compiled.
10016 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010017 Handle<SharedFunctionInfo> shared(func->shared());
10018 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +000010019 return Failure::Exception();
10020 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010021 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +000010022#endif // DEBUG
10023 return Heap::undefined_value();
10024}
10025
10026
ager@chromium.org9085a012009-05-11 19:22:57 +000010027static Object* Runtime_FunctionGetInferredName(Arguments args) {
10028 NoHandleAllocation ha;
10029 ASSERT(args.length() == 1);
10030
10031 CONVERT_CHECKED(JSFunction, f, args[0]);
10032 return f->shared()->inferred_name();
10033}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010034
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010035
10036static int FindSharedFunctionInfosForScript(Script* script,
10037 FixedArray* buffer) {
10038 AssertNoAllocation no_allocations;
10039
10040 int counter = 0;
10041 int buffer_size = buffer->length();
10042 HeapIterator iterator;
10043 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
10044 ASSERT(obj != NULL);
10045 if (!obj->IsSharedFunctionInfo()) {
10046 continue;
10047 }
10048 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
10049 if (shared->script() != script) {
10050 continue;
10051 }
10052 if (counter < buffer_size) {
10053 buffer->set(counter, shared);
10054 }
10055 counter++;
10056 }
10057 return counter;
10058}
10059
10060// For a script finds all SharedFunctionInfo's in the heap that points
10061// to this script. Returns JSArray of SharedFunctionInfo wrapped
10062// in OpaqueReferences.
10063static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
10064 Arguments args) {
10065 ASSERT(args.length() == 1);
10066 HandleScope scope;
10067 CONVERT_CHECKED(JSValue, script_value, args[0]);
10068
10069 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
10070
10071 const int kBufferSize = 32;
10072
10073 Handle<FixedArray> array;
10074 array = Factory::NewFixedArray(kBufferSize);
10075 int number = FindSharedFunctionInfosForScript(*script, *array);
10076 if (number > kBufferSize) {
10077 array = Factory::NewFixedArray(number);
10078 FindSharedFunctionInfosForScript(*script, *array);
10079 }
10080
10081 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
10082 result->set_length(Smi::FromInt(number));
10083
10084 LiveEdit::WrapSharedFunctionInfos(result);
10085
10086 return *result;
10087}
10088
10089// For a script calculates compilation information about all its functions.
10090// The script source is explicitly specified by the second argument.
10091// The source of the actual script is not used, however it is important that
10092// all generated code keeps references to this particular instance of script.
10093// Returns a JSArray of compilation infos. The array is ordered so that
10094// each function with all its descendant is always stored in a continues range
10095// with the function itself going first. The root function is a script function.
10096static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
10097 ASSERT(args.length() == 2);
10098 HandleScope scope;
10099 CONVERT_CHECKED(JSValue, script, args[0]);
10100 CONVERT_ARG_CHECKED(String, source, 1);
10101 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
10102
10103 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
10104
10105 if (Top::has_pending_exception()) {
10106 return Failure::Exception();
10107 }
10108
10109 return result;
10110}
10111
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010112// Changes the source of the script to a new_source.
10113// If old_script_name is provided (i.e. is a String), also creates a copy of
10114// the script with its original source and sends notification to debugger.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010115static Object* Runtime_LiveEditReplaceScript(Arguments args) {
10116 ASSERT(args.length() == 3);
10117 HandleScope scope;
10118 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
10119 CONVERT_ARG_CHECKED(String, new_source, 1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010120 Handle<Object> old_script_name(args[2]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010121
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010122 CONVERT_CHECKED(Script, original_script_pointer,
10123 original_script_value->value());
10124 Handle<Script> original_script(original_script_pointer);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010125
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010126 Object* old_script = LiveEdit::ChangeScriptSource(original_script,
10127 new_source,
10128 old_script_name);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010129
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010130 if (old_script->IsScript()) {
10131 Handle<Script> script_handle(Script::cast(old_script));
10132 return *(GetScriptWrapper(script_handle));
10133 } else {
10134 return Heap::null_value();
10135 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010136}
10137
10138// Replaces code of SharedFunctionInfo with a new one.
10139static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
10140 ASSERT(args.length() == 2);
10141 HandleScope scope;
10142 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
10143 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
10144
ager@chromium.orgac091b72010-05-05 07:34:42 +000010145 return LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010146}
10147
10148// Connects SharedFunctionInfo to another script.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010149static Object* Runtime_LiveEditFunctionSetScript(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010150 ASSERT(args.length() == 2);
10151 HandleScope scope;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010152 Handle<Object> function_object(args[0]);
10153 Handle<Object> script_object(args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010154
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010155 if (function_object->IsJSValue()) {
10156 Handle<JSValue> function_wrapper = Handle<JSValue>::cast(function_object);
10157 if (script_object->IsJSValue()) {
10158 CONVERT_CHECKED(Script, script, JSValue::cast(*script_object)->value());
10159 script_object = Handle<Object>(script);
10160 }
10161
10162 LiveEdit::SetFunctionScript(function_wrapper, script_object);
10163 } else {
10164 // Just ignore this. We may not have a SharedFunctionInfo for some functions
10165 // and we check it in this function.
10166 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010167
10168 return Heap::undefined_value();
10169}
10170
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010171
10172// In a code of a parent function replaces original function as embedded object
10173// with a substitution one.
10174static Object* Runtime_LiveEditReplaceRefToNestedFunction(Arguments args) {
10175 ASSERT(args.length() == 3);
10176 HandleScope scope;
10177
10178 CONVERT_ARG_CHECKED(JSValue, parent_wrapper, 0);
10179 CONVERT_ARG_CHECKED(JSValue, orig_wrapper, 1);
10180 CONVERT_ARG_CHECKED(JSValue, subst_wrapper, 2);
10181
10182 LiveEdit::ReplaceRefToNestedFunction(parent_wrapper, orig_wrapper,
10183 subst_wrapper);
10184
10185 return Heap::undefined_value();
10186}
10187
10188
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010189// Updates positions of a shared function info (first parameter) according
10190// to script source change. Text change is described in second parameter as
10191// array of groups of 3 numbers:
10192// (change_begin, change_end, change_end_new_position).
10193// Each group describes a change in text; groups are sorted by change_begin.
10194static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
10195 ASSERT(args.length() == 2);
10196 HandleScope scope;
10197 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
10198 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
10199
ager@chromium.orgac091b72010-05-05 07:34:42 +000010200 return LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010201}
10202
10203
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010204// For array of SharedFunctionInfo's (each wrapped in JSValue)
10205// checks that none of them have activations on stacks (of any thread).
10206// Returns array of the same length with corresponding results of
10207// LiveEdit::FunctionPatchabilityStatus type.
ager@chromium.org357bf652010-04-12 11:30:10 +000010208static Object* Runtime_LiveEditCheckAndDropActivations(Arguments args) {
10209 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010210 HandleScope scope;
10211 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +000010212 CONVERT_BOOLEAN_CHECKED(do_drop, args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010213
ager@chromium.org357bf652010-04-12 11:30:10 +000010214 return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010215}
10216
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010217// Compares 2 strings line-by-line and returns diff in form of JSArray of
fschneider@chromium.org013f3e12010-04-26 13:27:52 +000010218// triplets (pos1, pos1_end, pos2_end) describing list of diff chunks.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010219static Object* Runtime_LiveEditCompareStringsLinewise(Arguments args) {
10220 ASSERT(args.length() == 2);
10221 HandleScope scope;
10222 CONVERT_ARG_CHECKED(String, s1, 0);
10223 CONVERT_ARG_CHECKED(String, s2, 1);
10224
10225 return *LiveEdit::CompareStringsLinewise(s1, s2);
10226}
10227
10228
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010229
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010230// A testing entry. Returns statement position which is the closest to
10231// source_position.
10232static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
10233 ASSERT(args.length() == 2);
10234 HandleScope scope;
10235 CONVERT_ARG_CHECKED(JSFunction, function, 0);
10236 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
10237
10238 Handle<Code> code(function->code());
10239
10240 RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
10241 int closest_pc = 0;
10242 int distance = kMaxInt;
10243 while (!it.done()) {
10244 int statement_position = static_cast<int>(it.rinfo()->data());
10245 // Check if this break point is closer that what was previously found.
10246 if (source_position <= statement_position &&
10247 statement_position - source_position < distance) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +000010248 closest_pc =
10249 static_cast<int>(it.rinfo()->pc() - code->instruction_start());
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010250 distance = statement_position - source_position;
10251 // Check whether we can't get any closer.
10252 if (distance == 0) break;
10253 }
10254 it.next();
10255 }
10256
10257 return Smi::FromInt(closest_pc);
10258}
10259
10260
ager@chromium.org357bf652010-04-12 11:30:10 +000010261// Calls specified function with or without entering the debugger.
10262// This is used in unit tests to run code as if debugger is entered or simply
10263// to have a stack with C++ frame in the middle.
10264static Object* Runtime_ExecuteInDebugContext(Arguments args) {
10265 ASSERT(args.length() == 2);
10266 HandleScope scope;
10267 CONVERT_ARG_CHECKED(JSFunction, function, 0);
10268 CONVERT_BOOLEAN_CHECKED(without_debugger, args[1]);
10269
10270 Handle<Object> result;
10271 bool pending_exception;
10272 {
10273 if (without_debugger) {
10274 result = Execution::Call(function, Top::global(), 0, NULL,
10275 &pending_exception);
10276 } else {
10277 EnterDebugger enter_debugger;
10278 result = Execution::Call(function, Top::global(), 0, NULL,
10279 &pending_exception);
10280 }
10281 }
10282 if (!pending_exception) {
10283 return *result;
10284 } else {
10285 return Failure::Exception();
10286 }
10287}
10288
10289
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010290#endif // ENABLE_DEBUGGER_SUPPORT
10291
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010292#ifdef ENABLE_LOGGING_AND_PROFILING
10293
10294static Object* Runtime_ProfilerResume(Arguments args) {
10295 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010296 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010297
10298 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010299 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10300 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010301 return Heap::undefined_value();
10302}
10303
10304
10305static Object* Runtime_ProfilerPause(Arguments args) {
10306 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010307 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010308
10309 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010310 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10311 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010312 return Heap::undefined_value();
10313}
10314
10315#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010316
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010317// Finds the script object from the script data. NOTE: This operation uses
10318// heap traversal to find the function generated for the source position
10319// for the requested break point. For lazily compiled functions several heap
10320// traversals might be required rendering this operation as a rather slow
10321// operation. However for setting break points which is normally done through
10322// some kind of user interaction the performance is not crucial.
10323static Handle<Object> Runtime_GetScriptFromScriptName(
10324 Handle<String> script_name) {
10325 // Scan the heap for Script objects to find the script with the requested
10326 // script data.
10327 Handle<Script> script;
10328 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010329 HeapObject* obj = NULL;
10330 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010331 // If a script is found check if it has the script data requested.
10332 if (obj->IsScript()) {
10333 if (Script::cast(obj)->name()->IsString()) {
10334 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
10335 script = Handle<Script>(Script::cast(obj));
10336 }
10337 }
10338 }
10339 }
10340
10341 // If no script with the requested script data is found return undefined.
10342 if (script.is_null()) return Factory::undefined_value();
10343
10344 // Return the script found.
10345 return GetScriptWrapper(script);
10346}
10347
10348
10349// Get the script object from script data. NOTE: Regarding performance
10350// see the NOTE for GetScriptFromScriptData.
10351// args[0]: script data for the script to find the source for
10352static Object* Runtime_GetScript(Arguments args) {
10353 HandleScope scope;
10354
10355 ASSERT(args.length() == 1);
10356
10357 CONVERT_CHECKED(String, script_name, args[0]);
10358
10359 // Find the requested script.
10360 Handle<Object> result =
10361 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
10362 return *result;
10363}
10364
10365
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010366// Determines whether the given stack frame should be displayed in
10367// a stack trace. The caller is the error constructor that asked
10368// for the stack trace to be collected. The first time a construct
10369// call to this function is encountered it is skipped. The seen_caller
10370// in/out parameter is used to remember if the caller has been seen
10371// yet.
10372static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
10373 bool* seen_caller) {
10374 // Only display JS frames.
10375 if (!raw_frame->is_java_script())
10376 return false;
10377 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
10378 Object* raw_fun = frame->function();
10379 // Not sure when this can happen but skip it just in case.
10380 if (!raw_fun->IsJSFunction())
10381 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010382 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010383 *seen_caller = true;
10384 return false;
10385 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010386 // Skip all frames until we've seen the caller. Also, skip the most
10387 // obvious builtin calls. Some builtin calls (such as Number.ADD
10388 // which is invoked using 'call') are very difficult to recognize
10389 // so we're leaving them in for now.
10390 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010391}
10392
10393
10394// Collect the raw data for a stack trace. Returns an array of three
10395// element segments each containing a receiver, function and native
10396// code offset.
10397static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010398 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010399 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010400 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
10401
10402 HandleScope scope;
10403
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000010404 limit = Max(limit, 0); // Ensure that limit is not negative.
10405 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010406 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010407
10408 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010409 // If the caller parameter is a function we skip frames until we're
10410 // under it before starting to collect.
10411 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010412 int cursor = 0;
10413 int frames_seen = 0;
10414 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010415 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010416 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010417 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010418 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010419 Object* recv = frame->receiver();
10420 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010421 Address pc = frame->pc();
10422 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010423 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010424 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010425 if (cursor + 2 < elements->length()) {
10426 elements->set(cursor++, recv);
10427 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010428 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010429 } else {
10430 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010431 Handle<Object> recv_handle(recv);
10432 Handle<Object> fun_handle(fun);
10433 SetElement(result, cursor++, recv_handle);
10434 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010435 SetElement(result, cursor++, Handle<Smi>(offset));
10436 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010437 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010438 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010439 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010440
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010441 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010442 return *result;
10443}
10444
10445
ager@chromium.org3811b432009-10-28 14:53:37 +000010446// Returns V8 version as a string.
10447static Object* Runtime_GetV8Version(Arguments args) {
10448 ASSERT_EQ(args.length(), 0);
10449
10450 NoHandleAllocation ha;
10451
10452 const char* version_string = v8::V8::GetVersion();
10453
10454 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
10455}
10456
10457
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010458static Object* Runtime_Abort(Arguments args) {
10459 ASSERT(args.length() == 2);
10460 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
10461 Smi::cast(args[1])->value());
10462 Top::PrintStack();
10463 OS::Abort();
10464 UNREACHABLE();
10465 return NULL;
10466}
10467
10468
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010469static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
10470 ASSERT(args.length() == 0);
10471 HandleScope::DeleteExtensions();
10472 return Heap::undefined_value();
10473}
10474
10475
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010476static Object* CacheMiss(FixedArray* cache_obj, int index, Object* key_obj) {
10477 ASSERT(index % 2 == 0); // index of the key
10478 ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
10479 ASSERT(index < cache_obj->length());
10480
10481 HandleScope scope;
10482
10483 Handle<FixedArray> cache(cache_obj);
10484 Handle<Object> key(key_obj);
10485 Handle<JSFunction> factory(JSFunction::cast(
10486 cache->get(JSFunctionResultCache::kFactoryIndex)));
10487 // TODO(antonm): consider passing a receiver when constructing a cache.
10488 Handle<Object> receiver(Top::global_context()->global());
10489
10490 Handle<Object> value;
10491 {
10492 // This handle is nor shared, nor used later, so it's safe.
10493 Object** argv[] = { key.location() };
10494 bool pending_exception = false;
10495 value = Execution::Call(factory,
10496 receiver,
10497 1,
10498 argv,
10499 &pending_exception);
10500 if (pending_exception) return Failure::Exception();
10501 }
10502
10503 cache->set(index, *key);
10504 cache->set(index + 1, *value);
10505 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(index));
10506
10507 return *value;
10508}
10509
10510
10511static Object* Runtime_GetFromCache(Arguments args) {
10512 // This is only called from codegen, so checks might be more lax.
10513 CONVERT_CHECKED(FixedArray, cache, args[0]);
10514 Object* key = args[1];
10515
10516 const int finger_index =
10517 Smi::cast(cache->get(JSFunctionResultCache::kFingerIndex))->value();
10518
10519 Object* o = cache->get(finger_index);
10520 if (o == key) {
10521 // The fastest case: hit the same place again.
10522 return cache->get(finger_index + 1);
10523 }
10524
10525 for (int i = finger_index - 2;
10526 i >= JSFunctionResultCache::kEntriesIndex;
10527 i -= 2) {
10528 o = cache->get(i);
10529 if (o == key) {
10530 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10531 return cache->get(i + 1);
10532 }
10533 }
10534
10535 const int size =
10536 Smi::cast(cache->get(JSFunctionResultCache::kCacheSizeIndex))->value();
10537 ASSERT(size <= cache->length());
10538
10539 for (int i = size - 2; i > finger_index; i -= 2) {
10540 o = cache->get(i);
10541 if (o == key) {
10542 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10543 return cache->get(i + 1);
10544 }
10545 }
10546
10547 // Cache miss. If we have spare room, put new data into it, otherwise
10548 // evict post finger entry which must be least recently used.
10549 if (size < cache->length()) {
10550 cache->set(JSFunctionResultCache::kCacheSizeIndex, Smi::FromInt(size + 2));
10551 return CacheMiss(cache, size, key);
10552 } else {
antonm@chromium.org397e23c2010-04-21 12:00:05 +000010553 int target_index = finger_index + JSFunctionResultCache::kEntrySize;
10554 if (target_index == cache->length()) {
10555 target_index = JSFunctionResultCache::kEntriesIndex;
10556 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010557 return CacheMiss(cache, target_index, key);
10558 }
10559}
10560
kasper.lund44510672008-07-25 07:37:58 +000010561#ifdef DEBUG
10562// ListNatives is ONLY used by the fuzz-natives.js in debug mode
10563// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010564static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +000010565 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010566 HandleScope scope;
10567 Handle<JSArray> result = Factory::NewJSArray(0);
10568 int index = 0;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010569 bool inline_runtime_functions = false;
ager@chromium.orga1645e22009-09-09 19:27:10 +000010570#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010571 { \
10572 HandleScope inner; \
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010573 Handle<String> name; \
10574 /* Inline runtime functions have an underscore in front of the name. */ \
10575 if (inline_runtime_functions) { \
10576 name = Factory::NewStringFromAscii( \
10577 Vector<const char>("_" #Name, StrLength("_" #Name))); \
10578 } else { \
10579 name = Factory::NewStringFromAscii( \
10580 Vector<const char>(#Name, StrLength(#Name))); \
10581 } \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010582 Handle<JSArray> pair = Factory::NewJSArray(0); \
10583 SetElement(pair, 0, name); \
10584 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
10585 SetElement(result, index++, pair); \
10586 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010587 inline_runtime_functions = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010588 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010589 inline_runtime_functions = true;
10590 INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010591#undef ADD_ENTRY
10592 return *result;
10593}
kasper.lund44510672008-07-25 07:37:58 +000010594#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010595
10596
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010597static Object* Runtime_Log(Arguments args) {
10598 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +000010599 CONVERT_CHECKED(String, format, args[0]);
10600 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010601 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010602 Logger::LogRuntime(chars, elms);
10603 return Heap::undefined_value();
10604}
10605
10606
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010607static Object* Runtime_IS_VAR(Arguments args) {
10608 UNREACHABLE(); // implemented as macro in the parser
10609 return NULL;
10610}
10611
10612
10613// ----------------------------------------------------------------------------
10614// Implementation of Runtime
10615
ager@chromium.orga1645e22009-09-09 19:27:10 +000010616#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010617 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +000010618 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010619
10620static Runtime::Function Runtime_functions[] = {
10621 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010622 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010623};
10624
10625#undef F
10626
10627
10628Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
10629 ASSERT(0 <= fid && fid < kNofFunctions);
10630 return &Runtime_functions[fid];
10631}
10632
10633
10634Runtime::Function* Runtime::FunctionForName(const char* name) {
10635 for (Function* f = Runtime_functions; f->name != NULL; f++) {
10636 if (strcmp(f->name, name) == 0) {
10637 return f;
10638 }
10639 }
10640 return NULL;
10641}
10642
10643
10644void Runtime::PerformGC(Object* result) {
10645 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010646 if (failure->IsRetryAfterGC()) {
10647 // Try to do a garbage collection; ignore it if it fails. The C
10648 // entry stub will throw an out-of-memory exception in that case.
10649 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
10650 } else {
10651 // Handle last resort GC and make sure to allow future allocations
10652 // to grow the heap without causing GCs (if possible).
10653 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +000010654 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010655 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010656}
10657
10658
10659} } // namespace v8::internal