blob: 1122d331a4fd0519799fc6bf2b97ace49889109c [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
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00001367static Object* Runtime_RegExpCloneResult(Arguments args) {
1368 ASSERT(args.length() == 1);
1369 Map* regexp_result_map;
1370 {
1371 AssertNoAllocation no_gc;
1372 HandleScope handles;
1373 regexp_result_map = Top::global_context()->regexp_result_map();
1374 }
1375 if (!args[0]->IsJSArray()) return args[0];
1376
1377 JSArray* result = JSArray::cast(args[0]);
1378 // Arguments to RegExpCloneResult should always be fresh RegExp exec call
1379 // results (either a fresh JSRegExpResult or null).
1380 // If the argument is not a JSRegExpResult, or isn't unmodified, just return
1381 // the argument uncloned.
1382 if (result->map() != regexp_result_map) return result;
1383
1384 // Having the original JSRegExpResult map guarantees that we have
1385 // fast elements and no properties except the two in-object properties.
1386 ASSERT(result->HasFastElements());
1387 ASSERT(result->properties() == Heap::empty_fixed_array());
1388 ASSERT_EQ(2, regexp_result_map->inobject_properties());
1389
1390 Object* new_array_alloc = Heap::AllocateRaw(JSRegExpResult::kSize,
1391 NEW_SPACE,
1392 OLD_POINTER_SPACE);
1393 if (new_array_alloc->IsFailure()) return new_array_alloc;
1394
1395 // Set HeapObject map to JSRegExpResult map.
1396 reinterpret_cast<HeapObject*>(new_array_alloc)->set_map(regexp_result_map);
1397
1398 JSArray* new_array = JSArray::cast(new_array_alloc);
1399
1400 // Copy JSObject properties.
1401 new_array->set_properties(result->properties()); // Empty FixedArray.
1402
1403 // Copy JSObject elements as copy-on-write.
1404 FixedArray* elements = FixedArray::cast(result->elements());
1405 if (elements != Heap::empty_fixed_array()) {
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00001406 elements->set_map(Heap::fixed_cow_array_map());
1407 }
1408 new_array->set_elements(elements);
1409
1410 // Copy JSArray length.
1411 new_array->set_length(result->length());
1412
1413 // Copy JSRegExpResult in-object property fields input and index.
1414 new_array->FastPropertyAtPut(JSRegExpResult::kIndexIndex,
1415 result->FastPropertyAt(
1416 JSRegExpResult::kIndexIndex));
1417 new_array->FastPropertyAtPut(JSRegExpResult::kInputIndex,
1418 result->FastPropertyAt(
1419 JSRegExpResult::kInputIndex));
1420 return new_array;
1421}
1422
1423
lrn@chromium.org25156de2010-04-06 13:10:27 +00001424static Object* Runtime_RegExpInitializeObject(Arguments args) {
1425 AssertNoAllocation no_alloc;
1426 ASSERT(args.length() == 5);
1427 CONVERT_CHECKED(JSRegExp, regexp, args[0]);
1428 CONVERT_CHECKED(String, source, args[1]);
1429
1430 Object* global = args[2];
1431 if (!global->IsTrue()) global = Heap::false_value();
1432
1433 Object* ignoreCase = args[3];
1434 if (!ignoreCase->IsTrue()) ignoreCase = Heap::false_value();
1435
1436 Object* multiline = args[4];
1437 if (!multiline->IsTrue()) multiline = Heap::false_value();
1438
1439 Map* map = regexp->map();
1440 Object* constructor = map->constructor();
1441 if (constructor->IsJSFunction() &&
1442 JSFunction::cast(constructor)->initial_map() == map) {
1443 // If we still have the original map, set in-object properties directly.
1444 regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, source);
1445 // TODO(lrn): Consider skipping write barrier on booleans as well.
1446 // Both true and false should be in oldspace at all times.
1447 regexp->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, global);
1448 regexp->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex, ignoreCase);
1449 regexp->InObjectPropertyAtPut(JSRegExp::kMultilineFieldIndex, multiline);
1450 regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex,
1451 Smi::FromInt(0),
1452 SKIP_WRITE_BARRIER);
1453 return regexp;
1454 }
1455
1456 // Map has changed, so use generic, but slower, method.
1457 PropertyAttributes final =
1458 static_cast<PropertyAttributes>(READ_ONLY | DONT_ENUM | DONT_DELETE);
1459 PropertyAttributes writable =
1460 static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
1461 regexp->IgnoreAttributesAndSetLocalProperty(Heap::source_symbol(),
1462 source,
1463 final);
1464 regexp->IgnoreAttributesAndSetLocalProperty(Heap::global_symbol(),
1465 global,
1466 final);
1467 regexp->IgnoreAttributesAndSetLocalProperty(Heap::ignore_case_symbol(),
1468 ignoreCase,
1469 final);
1470 regexp->IgnoreAttributesAndSetLocalProperty(Heap::multiline_symbol(),
1471 multiline,
1472 final);
1473 regexp->IgnoreAttributesAndSetLocalProperty(Heap::last_index_symbol(),
1474 Smi::FromInt(0),
1475 writable);
1476 return regexp;
1477}
1478
1479
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001480static Object* Runtime_FinishArrayPrototypeSetup(Arguments args) {
1481 HandleScope scope;
1482 ASSERT(args.length() == 1);
1483 CONVERT_ARG_CHECKED(JSArray, prototype, 0);
1484 // This is necessary to enable fast checks for absence of elements
1485 // on Array.prototype and below.
1486 prototype->set_elements(Heap::empty_fixed_array());
1487 return Smi::FromInt(0);
1488}
1489
1490
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001491static Handle<JSFunction> InstallBuiltin(Handle<JSObject> holder,
1492 const char* name,
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001493 Builtins::Name builtin_name) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001494 Handle<String> key = Factory::LookupAsciiSymbol(name);
1495 Handle<Code> code(Builtins::builtin(builtin_name));
1496 Handle<JSFunction> optimized = Factory::NewFunction(key,
1497 JS_OBJECT_TYPE,
1498 JSObject::kHeaderSize,
1499 code,
1500 false);
1501 optimized->shared()->DontAdaptArguments();
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001502 SetProperty(holder, key, optimized, NONE);
1503 return optimized;
1504}
1505
1506
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001507static Object* Runtime_SpecialArrayFunctions(Arguments args) {
1508 HandleScope scope;
1509 ASSERT(args.length() == 1);
1510 CONVERT_ARG_CHECKED(JSObject, holder, 0);
1511
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001512 InstallBuiltin(holder, "pop", Builtins::ArrayPop);
1513 InstallBuiltin(holder, "push", Builtins::ArrayPush);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001514 InstallBuiltin(holder, "shift", Builtins::ArrayShift);
1515 InstallBuiltin(holder, "unshift", Builtins::ArrayUnshift);
1516 InstallBuiltin(holder, "slice", Builtins::ArraySlice);
1517 InstallBuiltin(holder, "splice", Builtins::ArraySplice);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001518 InstallBuiltin(holder, "concat", Builtins::ArrayConcat);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001519
1520 return *holder;
1521}
1522
1523
ager@chromium.org357bf652010-04-12 11:30:10 +00001524static Object* Runtime_GetGlobalReceiver(Arguments args) {
1525 // Returns a real global receiver, not one of builtins object.
1526 Context* global_context = Top::context()->global()->global_context();
1527 return global_context->global()->global_receiver();
1528}
1529
1530
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001531static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1532 HandleScope scope;
1533 ASSERT(args.length() == 4);
1534 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1535 int index = Smi::cast(args[1])->value();
1536 Handle<String> pattern = args.at<String>(2);
1537 Handle<String> flags = args.at<String>(3);
1538
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001539 // Get the RegExp function from the context in the literals array.
1540 // This is the RegExp function from the context in which the
1541 // function was created. We do not use the RegExp function from the
1542 // current global context because this might be the RegExp function
1543 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001544 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001545 Handle<JSFunction>(
1546 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001547 // Compute the regular expression literal.
1548 bool has_pending_exception;
1549 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001550 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1551 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001552 if (has_pending_exception) {
1553 ASSERT(Top::has_pending_exception());
1554 return Failure::Exception();
1555 }
1556 literals->set(index, *regexp);
1557 return *regexp;
1558}
1559
1560
1561static Object* Runtime_FunctionGetName(Arguments args) {
1562 NoHandleAllocation ha;
1563 ASSERT(args.length() == 1);
1564
1565 CONVERT_CHECKED(JSFunction, f, args[0]);
1566 return f->shared()->name();
1567}
1568
1569
ager@chromium.org236ad962008-09-25 09:45:57 +00001570static Object* Runtime_FunctionSetName(Arguments args) {
1571 NoHandleAllocation ha;
1572 ASSERT(args.length() == 2);
1573
1574 CONVERT_CHECKED(JSFunction, f, args[0]);
1575 CONVERT_CHECKED(String, name, args[1]);
1576 f->shared()->set_name(name);
1577 return Heap::undefined_value();
1578}
1579
1580
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001581static Object* Runtime_FunctionRemovePrototype(Arguments args) {
1582 NoHandleAllocation ha;
1583 ASSERT(args.length() == 1);
1584
1585 CONVERT_CHECKED(JSFunction, f, args[0]);
1586 Object* obj = f->RemovePrototype();
1587 if (obj->IsFailure()) return obj;
1588
1589 return Heap::undefined_value();
1590}
1591
1592
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001593static Object* Runtime_FunctionGetScript(Arguments args) {
1594 HandleScope scope;
1595 ASSERT(args.length() == 1);
1596
1597 CONVERT_CHECKED(JSFunction, fun, args[0]);
1598 Handle<Object> script = Handle<Object>(fun->shared()->script());
1599 if (!script->IsScript()) return Heap::undefined_value();
1600
1601 return *GetScriptWrapper(Handle<Script>::cast(script));
1602}
1603
1604
1605static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1606 NoHandleAllocation ha;
1607 ASSERT(args.length() == 1);
1608
1609 CONVERT_CHECKED(JSFunction, f, args[0]);
1610 return f->shared()->GetSourceCode();
1611}
1612
1613
1614static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1615 NoHandleAllocation ha;
1616 ASSERT(args.length() == 1);
1617
1618 CONVERT_CHECKED(JSFunction, fun, args[0]);
1619 int pos = fun->shared()->start_position();
1620 return Smi::FromInt(pos);
1621}
1622
1623
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001624static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1625 ASSERT(args.length() == 2);
1626
1627 CONVERT_CHECKED(JSFunction, fun, args[0]);
1628 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1629
1630 Code* code = fun->code();
1631 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1632
1633 Address pc = code->address() + offset;
1634 return Smi::FromInt(fun->code()->SourcePosition(pc));
1635}
1636
1637
1638
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001639static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1640 NoHandleAllocation ha;
1641 ASSERT(args.length() == 2);
1642
1643 CONVERT_CHECKED(JSFunction, fun, args[0]);
1644 CONVERT_CHECKED(String, name, args[1]);
1645 fun->SetInstanceClassName(name);
1646 return Heap::undefined_value();
1647}
1648
1649
1650static Object* Runtime_FunctionSetLength(Arguments args) {
1651 NoHandleAllocation ha;
1652 ASSERT(args.length() == 2);
1653
1654 CONVERT_CHECKED(JSFunction, fun, args[0]);
1655 CONVERT_CHECKED(Smi, length, args[1]);
1656 fun->shared()->set_length(length->value());
1657 return length;
1658}
1659
1660
1661static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001662 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001663 ASSERT(args.length() == 2);
1664
1665 CONVERT_CHECKED(JSFunction, fun, args[0]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001666 ASSERT(fun->should_have_prototype());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001667 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1668 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001669 return args[0]; // return TOS
1670}
1671
1672
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001673static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1674 NoHandleAllocation ha;
1675 ASSERT(args.length() == 1);
1676
1677 CONVERT_CHECKED(JSFunction, f, args[0]);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001678 return f->shared()->IsApiFunction() ? Heap::true_value()
1679 : Heap::false_value();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001680}
1681
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001682static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1683 NoHandleAllocation ha;
1684 ASSERT(args.length() == 1);
1685
1686 CONVERT_CHECKED(JSFunction, f, args[0]);
1687 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1688}
1689
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001690
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001691static Object* Runtime_SetCode(Arguments args) {
1692 HandleScope scope;
1693 ASSERT(args.length() == 2);
1694
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001695 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001696 Handle<Object> code = args.at<Object>(1);
1697
1698 Handle<Context> context(target->context());
1699
1700 if (!code->IsNull()) {
1701 RUNTIME_ASSERT(code->IsJSFunction());
1702 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001703 Handle<SharedFunctionInfo> shared(fun->shared());
1704 SetExpectedNofProperties(target, shared->expected_nof_properties());
1705
1706 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001707 return Failure::Exception();
1708 }
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00001709 // Set the code, scope info, formal parameter count,
1710 // and the length of the target function.
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001711 target->shared()->set_code(shared->code());
1712 target->set_code(shared->code());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00001713 target->shared()->set_scope_info(shared->scope_info());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001714 target->shared()->set_length(shared->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001715 target->shared()->set_formal_parameter_count(
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001716 shared->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001717 // Set the source code of the target function to undefined.
1718 // SetCode is only used for built-in constructors like String,
1719 // Array, and Object, and some web code
1720 // doesn't like seeing source code for constructors.
1721 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001722 // Clear the optimization hints related to the compiled code as these are no
1723 // longer valid when the code is overwritten.
1724 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001725 context = Handle<Context>(fun->context());
1726
1727 // Make sure we get a fresh copy of the literal vector to avoid
1728 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001729 int number_of_literals = fun->NumberOfLiterals();
1730 Handle<FixedArray> literals =
1731 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001732 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001733 // Insert the object, regexp and array functions in the literals
1734 // array prefix. These are the functions that will be used when
1735 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001736 literals->set(JSFunction::kLiteralGlobalContextIndex,
1737 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001738 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001739 // It's okay to skip the write barrier here because the literals
1740 // are guaranteed to be in old space.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001741 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001742 }
1743
1744 target->set_context(*context);
1745 return *target;
1746}
1747
1748
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001749static Object* CharFromCode(Object* char_code) {
1750 uint32_t code;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001751 if (char_code->ToArrayIndex(&code)) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001752 if (code <= 0xffff) {
1753 return Heap::LookupSingleCharacterStringFromCode(code);
1754 }
1755 }
1756 return Heap::empty_string();
1757}
1758
1759
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001760static Object* Runtime_StringCharCodeAt(Arguments args) {
1761 NoHandleAllocation ha;
1762 ASSERT(args.length() == 2);
1763
1764 CONVERT_CHECKED(String, subject, args[0]);
1765 Object* index = args[1];
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001766 RUNTIME_ASSERT(index->IsNumber());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001767
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001768 uint32_t i = 0;
1769 if (index->IsSmi()) {
1770 int value = Smi::cast(index)->value();
1771 if (value < 0) return Heap::nan_value();
1772 i = value;
1773 } else {
1774 ASSERT(index->IsHeapNumber());
1775 double value = HeapNumber::cast(index)->value();
1776 i = static_cast<uint32_t>(DoubleToInteger(value));
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001777 }
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001778
1779 // Flatten the string. If someone wants to get a char at an index
1780 // in a cons string, it is likely that more indices will be
1781 // accessed.
1782 Object* flat = subject->TryFlatten();
1783 if (flat->IsFailure()) return flat;
1784 subject = String::cast(flat);
1785
1786 if (i >= static_cast<uint32_t>(subject->length())) {
1787 return Heap::nan_value();
1788 }
1789
1790 return Smi::FromInt(subject->Get(i));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001791}
1792
1793
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001794static Object* Runtime_CharFromCode(Arguments args) {
1795 NoHandleAllocation ha;
1796 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001797 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001798}
1799
lrn@chromium.org25156de2010-04-06 13:10:27 +00001800
1801class FixedArrayBuilder {
1802 public:
1803 explicit FixedArrayBuilder(int initial_capacity)
1804 : array_(Factory::NewFixedArrayWithHoles(initial_capacity)),
1805 length_(0) {
1806 // Require a non-zero initial size. Ensures that doubling the size to
1807 // extend the array will work.
1808 ASSERT(initial_capacity > 0);
1809 }
1810
1811 explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
1812 : array_(backing_store),
1813 length_(0) {
1814 // Require a non-zero initial size. Ensures that doubling the size to
1815 // extend the array will work.
1816 ASSERT(backing_store->length() > 0);
1817 }
1818
1819 bool HasCapacity(int elements) {
1820 int length = array_->length();
1821 int required_length = length_ + elements;
1822 return (length >= required_length);
1823 }
1824
1825 void EnsureCapacity(int elements) {
1826 int length = array_->length();
1827 int required_length = length_ + elements;
1828 if (length < required_length) {
1829 int new_length = length;
1830 do {
1831 new_length *= 2;
1832 } while (new_length < required_length);
1833 Handle<FixedArray> extended_array =
1834 Factory::NewFixedArrayWithHoles(new_length);
1835 array_->CopyTo(0, *extended_array, 0, length_);
1836 array_ = extended_array;
1837 }
1838 }
1839
1840 void Add(Object* value) {
1841 ASSERT(length_ < capacity());
1842 array_->set(length_, value);
1843 length_++;
1844 }
1845
1846 void Add(Smi* value) {
1847 ASSERT(length_ < capacity());
1848 array_->set(length_, value);
1849 length_++;
1850 }
1851
1852 Handle<FixedArray> array() {
1853 return array_;
1854 }
1855
1856 int length() {
1857 return length_;
1858 }
1859
1860 int capacity() {
1861 return array_->length();
1862 }
1863
1864 Handle<JSArray> ToJSArray() {
1865 Handle<JSArray> result_array = Factory::NewJSArrayWithElements(array_);
1866 result_array->set_length(Smi::FromInt(length_));
1867 return result_array;
1868 }
1869
1870 Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
1871 target_array->set_elements(*array_);
1872 target_array->set_length(Smi::FromInt(length_));
1873 return target_array;
1874 }
1875
1876 private:
1877 Handle<FixedArray> array_;
1878 int length_;
1879};
1880
1881
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001882// Forward declarations.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001883const int kStringBuilderConcatHelperLengthBits = 11;
1884const int kStringBuilderConcatHelperPositionBits = 19;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001885
1886template <typename schar>
1887static inline void StringBuilderConcatHelper(String*,
1888 schar*,
1889 FixedArray*,
1890 int);
1891
lrn@chromium.org25156de2010-04-06 13:10:27 +00001892typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
1893 StringBuilderSubstringLength;
1894typedef BitField<int,
1895 kStringBuilderConcatHelperLengthBits,
1896 kStringBuilderConcatHelperPositionBits>
1897 StringBuilderSubstringPosition;
1898
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001899
1900class ReplacementStringBuilder {
1901 public:
1902 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
lrn@chromium.org25156de2010-04-06 13:10:27 +00001903 : array_builder_(estimated_part_count),
1904 subject_(subject),
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001905 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001906 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001907 // Require a non-zero initial size. Ensures that doubling the size to
1908 // extend the array will work.
1909 ASSERT(estimated_part_count > 0);
1910 }
1911
lrn@chromium.org25156de2010-04-06 13:10:27 +00001912 static inline void AddSubjectSlice(FixedArrayBuilder* builder,
1913 int from,
1914 int to) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001915 ASSERT(from >= 0);
1916 int length = to - from;
1917 ASSERT(length > 0);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001918 if (StringBuilderSubstringLength::is_valid(length) &&
1919 StringBuilderSubstringPosition::is_valid(from)) {
1920 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1921 StringBuilderSubstringPosition::encode(from);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001922 builder->Add(Smi::FromInt(encoded_slice));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001923 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001924 // Otherwise encode as two smis.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001925 builder->Add(Smi::FromInt(-length));
1926 builder->Add(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001927 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00001928 }
1929
1930
1931 void EnsureCapacity(int elements) {
1932 array_builder_.EnsureCapacity(elements);
1933 }
1934
1935
1936 void AddSubjectSlice(int from, int to) {
1937 AddSubjectSlice(&array_builder_, from, to);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001938 IncrementCharacterCount(to - from);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001939 }
1940
1941
1942 void AddString(Handle<String> string) {
1943 int length = string->length();
1944 ASSERT(length > 0);
1945 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001946 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001947 is_ascii_ = false;
1948 }
1949 IncrementCharacterCount(length);
1950 }
1951
1952
1953 Handle<String> ToString() {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001954 if (array_builder_.length() == 0) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001955 return Factory::empty_string();
1956 }
1957
1958 Handle<String> joined_string;
1959 if (is_ascii_) {
1960 joined_string = NewRawAsciiString(character_count_);
1961 AssertNoAllocation no_alloc;
1962 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1963 char* char_buffer = seq->GetChars();
1964 StringBuilderConcatHelper(*subject_,
1965 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001966 *array_builder_.array(),
1967 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001968 } else {
1969 // Non-ASCII.
1970 joined_string = NewRawTwoByteString(character_count_);
1971 AssertNoAllocation no_alloc;
1972 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1973 uc16* char_buffer = seq->GetChars();
1974 StringBuilderConcatHelper(*subject_,
1975 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001976 *array_builder_.array(),
1977 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001978 }
1979 return joined_string;
1980 }
1981
1982
1983 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001984 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001985 V8::FatalProcessOutOfMemory("String.replace result too large.");
1986 }
1987 character_count_ += by;
1988 }
1989
lrn@chromium.org25156de2010-04-06 13:10:27 +00001990 Handle<JSArray> GetParts() {
1991 Handle<JSArray> result =
1992 Factory::NewJSArrayWithElements(array_builder_.array());
1993 result->set_length(Smi::FromInt(array_builder_.length()));
1994 return result;
1995 }
kmillikin@chromium.orgd9825192010-03-30 08:36:16 +00001996
lrn@chromium.org25156de2010-04-06 13:10:27 +00001997 private:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001998 Handle<String> NewRawAsciiString(int size) {
1999 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
2000 }
2001
2002
2003 Handle<String> NewRawTwoByteString(int size) {
2004 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
2005 }
2006
2007
2008 void AddElement(Object* element) {
2009 ASSERT(element->IsSmi() || element->IsString());
lrn@chromium.org25156de2010-04-06 13:10:27 +00002010 ASSERT(array_builder_.capacity() > array_builder_.length());
2011 array_builder_.Add(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002012 }
2013
lrn@chromium.org25156de2010-04-06 13:10:27 +00002014 FixedArrayBuilder array_builder_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002015 Handle<String> subject_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002016 int character_count_;
2017 bool is_ascii_;
2018};
2019
2020
2021class CompiledReplacement {
2022 public:
2023 CompiledReplacement()
2024 : parts_(1), replacement_substrings_(0) {}
2025
2026 void Compile(Handle<String> replacement,
2027 int capture_count,
2028 int subject_length);
2029
2030 void Apply(ReplacementStringBuilder* builder,
2031 int match_from,
2032 int match_to,
2033 Handle<JSArray> last_match_info);
2034
2035 // Number of distinct parts of the replacement pattern.
2036 int parts() {
2037 return parts_.length();
2038 }
2039 private:
2040 enum PartType {
2041 SUBJECT_PREFIX = 1,
2042 SUBJECT_SUFFIX,
2043 SUBJECT_CAPTURE,
2044 REPLACEMENT_SUBSTRING,
2045 REPLACEMENT_STRING,
2046
2047 NUMBER_OF_PART_TYPES
2048 };
2049
2050 struct ReplacementPart {
2051 static inline ReplacementPart SubjectMatch() {
2052 return ReplacementPart(SUBJECT_CAPTURE, 0);
2053 }
2054 static inline ReplacementPart SubjectCapture(int capture_index) {
2055 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
2056 }
2057 static inline ReplacementPart SubjectPrefix() {
2058 return ReplacementPart(SUBJECT_PREFIX, 0);
2059 }
2060 static inline ReplacementPart SubjectSuffix(int subject_length) {
2061 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
2062 }
2063 static inline ReplacementPart ReplacementString() {
2064 return ReplacementPart(REPLACEMENT_STRING, 0);
2065 }
2066 static inline ReplacementPart ReplacementSubString(int from, int to) {
2067 ASSERT(from >= 0);
2068 ASSERT(to > from);
2069 return ReplacementPart(-from, to);
2070 }
2071
2072 // If tag <= 0 then it is the negation of a start index of a substring of
2073 // the replacement pattern, otherwise it's a value from PartType.
2074 ReplacementPart(int tag, int data)
2075 : tag(tag), data(data) {
2076 // Must be non-positive or a PartType value.
2077 ASSERT(tag < NUMBER_OF_PART_TYPES);
2078 }
2079 // Either a value of PartType or a non-positive number that is
2080 // the negation of an index into the replacement string.
2081 int tag;
2082 // The data value's interpretation depends on the value of tag:
2083 // tag == SUBJECT_PREFIX ||
2084 // tag == SUBJECT_SUFFIX: data is unused.
2085 // tag == SUBJECT_CAPTURE: data is the number of the capture.
2086 // tag == REPLACEMENT_SUBSTRING ||
2087 // tag == REPLACEMENT_STRING: data is index into array of substrings
2088 // of the replacement string.
2089 // tag <= 0: Temporary representation of the substring of the replacement
2090 // string ranging over -tag .. data.
2091 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
2092 // substring objects.
2093 int data;
2094 };
2095
2096 template<typename Char>
2097 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
2098 Vector<Char> characters,
2099 int capture_count,
2100 int subject_length) {
2101 int length = characters.length();
2102 int last = 0;
2103 for (int i = 0; i < length; i++) {
2104 Char c = characters[i];
2105 if (c == '$') {
2106 int next_index = i + 1;
2107 if (next_index == length) { // No next character!
2108 break;
2109 }
2110 Char c2 = characters[next_index];
2111 switch (c2) {
2112 case '$':
2113 if (i > last) {
2114 // There is a substring before. Include the first "$".
2115 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
2116 last = next_index + 1; // Continue after the second "$".
2117 } else {
2118 // Let the next substring start with the second "$".
2119 last = next_index;
2120 }
2121 i = next_index;
2122 break;
2123 case '`':
2124 if (i > last) {
2125 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2126 }
2127 parts->Add(ReplacementPart::SubjectPrefix());
2128 i = next_index;
2129 last = i + 1;
2130 break;
2131 case '\'':
2132 if (i > last) {
2133 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2134 }
2135 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
2136 i = next_index;
2137 last = i + 1;
2138 break;
2139 case '&':
2140 if (i > last) {
2141 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2142 }
2143 parts->Add(ReplacementPart::SubjectMatch());
2144 i = next_index;
2145 last = i + 1;
2146 break;
2147 case '0':
2148 case '1':
2149 case '2':
2150 case '3':
2151 case '4':
2152 case '5':
2153 case '6':
2154 case '7':
2155 case '8':
2156 case '9': {
2157 int capture_ref = c2 - '0';
2158 if (capture_ref > capture_count) {
2159 i = next_index;
2160 continue;
2161 }
2162 int second_digit_index = next_index + 1;
2163 if (second_digit_index < length) {
2164 // Peek ahead to see if we have two digits.
2165 Char c3 = characters[second_digit_index];
2166 if ('0' <= c3 && c3 <= '9') { // Double digits.
2167 int double_digit_ref = capture_ref * 10 + c3 - '0';
2168 if (double_digit_ref <= capture_count) {
2169 next_index = second_digit_index;
2170 capture_ref = double_digit_ref;
2171 }
2172 }
2173 }
2174 if (capture_ref > 0) {
2175 if (i > last) {
2176 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2177 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002178 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002179 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
2180 last = next_index + 1;
2181 }
2182 i = next_index;
2183 break;
2184 }
2185 default:
2186 i = next_index;
2187 break;
2188 }
2189 }
2190 }
2191 if (length > last) {
2192 if (last == 0) {
2193 parts->Add(ReplacementPart::ReplacementString());
2194 } else {
2195 parts->Add(ReplacementPart::ReplacementSubString(last, length));
2196 }
2197 }
2198 }
2199
2200 ZoneList<ReplacementPart> parts_;
2201 ZoneList<Handle<String> > replacement_substrings_;
2202};
2203
2204
2205void CompiledReplacement::Compile(Handle<String> replacement,
2206 int capture_count,
2207 int subject_length) {
2208 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00002209 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002210 AssertNoAllocation no_alloc;
2211 ParseReplacementPattern(&parts_,
2212 replacement->ToAsciiVector(),
2213 capture_count,
2214 subject_length);
2215 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00002216 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002217 AssertNoAllocation no_alloc;
2218
2219 ParseReplacementPattern(&parts_,
2220 replacement->ToUC16Vector(),
2221 capture_count,
2222 subject_length);
2223 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002224 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002225 int substring_index = 0;
2226 for (int i = 0, n = parts_.length(); i < n; i++) {
2227 int tag = parts_[i].tag;
2228 if (tag <= 0) { // A replacement string slice.
2229 int from = -tag;
2230 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002231 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002232 parts_[i].tag = REPLACEMENT_SUBSTRING;
2233 parts_[i].data = substring_index;
2234 substring_index++;
2235 } else if (tag == REPLACEMENT_STRING) {
2236 replacement_substrings_.Add(replacement);
2237 parts_[i].data = substring_index;
2238 substring_index++;
2239 }
2240 }
2241}
2242
2243
2244void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
2245 int match_from,
2246 int match_to,
2247 Handle<JSArray> last_match_info) {
2248 for (int i = 0, n = parts_.length(); i < n; i++) {
2249 ReplacementPart part = parts_[i];
2250 switch (part.tag) {
2251 case SUBJECT_PREFIX:
2252 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
2253 break;
2254 case SUBJECT_SUFFIX: {
2255 int subject_length = part.data;
2256 if (match_to < subject_length) {
2257 builder->AddSubjectSlice(match_to, subject_length);
2258 }
2259 break;
2260 }
2261 case SUBJECT_CAPTURE: {
2262 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002263 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002264 int from = RegExpImpl::GetCapture(match_info, capture * 2);
2265 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
2266 if (from >= 0 && to > from) {
2267 builder->AddSubjectSlice(from, to);
2268 }
2269 break;
2270 }
2271 case REPLACEMENT_SUBSTRING:
2272 case REPLACEMENT_STRING:
2273 builder->AddString(replacement_substrings_[part.data]);
2274 break;
2275 default:
2276 UNREACHABLE();
2277 }
2278 }
2279}
2280
2281
2282
2283static Object* StringReplaceRegExpWithString(String* subject,
2284 JSRegExp* regexp,
2285 String* replacement,
2286 JSArray* last_match_info) {
2287 ASSERT(subject->IsFlat());
2288 ASSERT(replacement->IsFlat());
2289
2290 HandleScope handles;
2291
2292 int length = subject->length();
2293 Handle<String> subject_handle(subject);
2294 Handle<JSRegExp> regexp_handle(regexp);
2295 Handle<String> replacement_handle(replacement);
2296 Handle<JSArray> last_match_info_handle(last_match_info);
2297 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2298 subject_handle,
2299 0,
2300 last_match_info_handle);
2301 if (match.is_null()) {
2302 return Failure::Exception();
2303 }
2304 if (match->IsNull()) {
2305 return *subject_handle;
2306 }
2307
2308 int capture_count = regexp_handle->CaptureCount();
2309
2310 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002311 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002312 CompiledReplacement compiled_replacement;
2313 compiled_replacement.Compile(replacement_handle,
2314 capture_count,
2315 length);
2316
2317 bool is_global = regexp_handle->GetFlags().is_global();
2318
2319 // Guessing the number of parts that the final result string is built
2320 // from. Global regexps can match any number of times, so we guess
2321 // conservatively.
2322 int expected_parts =
2323 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
2324 ReplacementStringBuilder builder(subject_handle, expected_parts);
2325
2326 // Index of end of last match.
2327 int prev = 0;
2328
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002329 // Number of parts added by compiled replacement plus preceeding
2330 // string and possibly suffix after last match. It is possible for
2331 // all components to use two elements when encoded as two smis.
2332 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002333 bool matched = true;
2334 do {
2335 ASSERT(last_match_info_handle->HasFastElements());
2336 // Increase the capacity of the builder before entering local handle-scope,
2337 // so its internal buffer can safely allocate a new handle if it grows.
2338 builder.EnsureCapacity(parts_added_per_loop);
2339
2340 HandleScope loop_scope;
2341 int start, end;
2342 {
2343 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002344 FixedArray* match_info_array =
2345 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002346
2347 ASSERT_EQ(capture_count * 2 + 2,
2348 RegExpImpl::GetLastCaptureCount(match_info_array));
2349 start = RegExpImpl::GetCapture(match_info_array, 0);
2350 end = RegExpImpl::GetCapture(match_info_array, 1);
2351 }
2352
2353 if (prev < start) {
2354 builder.AddSubjectSlice(prev, start);
2355 }
2356 compiled_replacement.Apply(&builder,
2357 start,
2358 end,
2359 last_match_info_handle);
2360 prev = end;
2361
2362 // Only continue checking for global regexps.
2363 if (!is_global) break;
2364
2365 // Continue from where the match ended, unless it was an empty match.
2366 int next = end;
2367 if (start == end) {
2368 next = end + 1;
2369 if (next > length) break;
2370 }
2371
2372 match = RegExpImpl::Exec(regexp_handle,
2373 subject_handle,
2374 next,
2375 last_match_info_handle);
2376 if (match.is_null()) {
2377 return Failure::Exception();
2378 }
2379 matched = !match->IsNull();
2380 } while (matched);
2381
2382 if (prev < length) {
2383 builder.AddSubjectSlice(prev, length);
2384 }
2385
2386 return *(builder.ToString());
2387}
2388
2389
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00002390template <typename ResultSeqString>
2391static Object* StringReplaceRegExpWithEmptyString(String* subject,
2392 JSRegExp* regexp,
2393 JSArray* last_match_info) {
2394 ASSERT(subject->IsFlat());
2395
2396 HandleScope handles;
2397
2398 Handle<String> subject_handle(subject);
2399 Handle<JSRegExp> regexp_handle(regexp);
2400 Handle<JSArray> last_match_info_handle(last_match_info);
2401 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2402 subject_handle,
2403 0,
2404 last_match_info_handle);
2405 if (match.is_null()) return Failure::Exception();
2406 if (match->IsNull()) return *subject_handle;
2407
2408 ASSERT(last_match_info_handle->HasFastElements());
2409
2410 HandleScope loop_scope;
2411 int start, end;
2412 {
2413 AssertNoAllocation match_info_array_is_not_in_a_handle;
2414 FixedArray* match_info_array =
2415 FixedArray::cast(last_match_info_handle->elements());
2416
2417 start = RegExpImpl::GetCapture(match_info_array, 0);
2418 end = RegExpImpl::GetCapture(match_info_array, 1);
2419 }
2420
2421 int length = subject->length();
2422 int new_length = length - (end - start);
2423 if (new_length == 0) {
2424 return Heap::empty_string();
2425 }
2426 Handle<ResultSeqString> answer;
2427 if (ResultSeqString::kHasAsciiEncoding) {
2428 answer =
2429 Handle<ResultSeqString>::cast(Factory::NewRawAsciiString(new_length));
2430 } else {
2431 answer =
2432 Handle<ResultSeqString>::cast(Factory::NewRawTwoByteString(new_length));
2433 }
2434
2435 // If the regexp isn't global, only match once.
2436 if (!regexp_handle->GetFlags().is_global()) {
2437 if (start > 0) {
2438 String::WriteToFlat(*subject_handle,
2439 answer->GetChars(),
2440 0,
2441 start);
2442 }
2443 if (end < length) {
2444 String::WriteToFlat(*subject_handle,
2445 answer->GetChars() + start,
2446 end,
2447 length);
2448 }
2449 return *answer;
2450 }
2451
2452 int prev = 0; // Index of end of last match.
2453 int next = 0; // Start of next search (prev unless last match was empty).
2454 int position = 0;
2455
2456 do {
2457 if (prev < start) {
2458 // Add substring subject[prev;start] to answer string.
2459 String::WriteToFlat(*subject_handle,
2460 answer->GetChars() + position,
2461 prev,
2462 start);
2463 position += start - prev;
2464 }
2465 prev = end;
2466 next = end;
2467 // Continue from where the match ended, unless it was an empty match.
2468 if (start == end) {
2469 next++;
2470 if (next > length) break;
2471 }
2472 match = RegExpImpl::Exec(regexp_handle,
2473 subject_handle,
2474 next,
2475 last_match_info_handle);
2476 if (match.is_null()) return Failure::Exception();
2477 if (match->IsNull()) break;
2478
2479 ASSERT(last_match_info_handle->HasFastElements());
2480 HandleScope loop_scope;
2481 {
2482 AssertNoAllocation match_info_array_is_not_in_a_handle;
2483 FixedArray* match_info_array =
2484 FixedArray::cast(last_match_info_handle->elements());
2485 start = RegExpImpl::GetCapture(match_info_array, 0);
2486 end = RegExpImpl::GetCapture(match_info_array, 1);
2487 }
2488 } while (true);
2489
2490 if (prev < length) {
2491 // Add substring subject[prev;length] to answer string.
2492 String::WriteToFlat(*subject_handle,
2493 answer->GetChars() + position,
2494 prev,
2495 length);
2496 position += length - prev;
2497 }
2498
2499 if (position == 0) {
2500 return Heap::empty_string();
2501 }
2502
2503 // Shorten string and fill
2504 int string_size = ResultSeqString::SizeFor(position);
2505 int allocated_string_size = ResultSeqString::SizeFor(new_length);
2506 int delta = allocated_string_size - string_size;
2507
2508 answer->set_length(position);
2509 if (delta == 0) return *answer;
2510
2511 Address end_of_string = answer->address() + string_size;
2512 Heap::CreateFillerObjectAt(end_of_string, delta);
2513
2514 return *answer;
2515}
2516
2517
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002518static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2519 ASSERT(args.length() == 4);
2520
2521 CONVERT_CHECKED(String, subject, args[0]);
2522 if (!subject->IsFlat()) {
2523 Object* flat_subject = subject->TryFlatten();
2524 if (flat_subject->IsFailure()) {
2525 return flat_subject;
2526 }
2527 subject = String::cast(flat_subject);
2528 }
2529
2530 CONVERT_CHECKED(String, replacement, args[2]);
2531 if (!replacement->IsFlat()) {
2532 Object* flat_replacement = replacement->TryFlatten();
2533 if (flat_replacement->IsFailure()) {
2534 return flat_replacement;
2535 }
2536 replacement = String::cast(flat_replacement);
2537 }
2538
2539 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2540 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2541
2542 ASSERT(last_match_info->HasFastElements());
2543
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00002544 if (replacement->length() == 0) {
2545 if (subject->HasOnlyAsciiChars()) {
2546 return StringReplaceRegExpWithEmptyString<SeqAsciiString>(
2547 subject, regexp, last_match_info);
2548 } else {
2549 return StringReplaceRegExpWithEmptyString<SeqTwoByteString>(
2550 subject, regexp, last_match_info);
2551 }
2552 }
2553
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002554 return StringReplaceRegExpWithString(subject,
2555 regexp,
2556 replacement,
2557 last_match_info);
2558}
2559
2560
ager@chromium.org7c537e22008-10-16 08:43:32 +00002561// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2562// limit, we can fix the size of tables.
2563static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002564// Reduce alphabet to this size.
2565static const int kBMAlphabetSize = 0x100;
2566// For patterns below this length, the skip length of Boyer-Moore is too short
2567// to compensate for the algorithmic overhead compared to simple brute force.
2568static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002569
ager@chromium.org7c537e22008-10-16 08:43:32 +00002570// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2571// shift. Only allows the last kBMMaxShift characters of the needle
2572// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002573class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002574 public:
2575 BMGoodSuffixBuffers() {}
2576 inline void init(int needle_length) {
2577 ASSERT(needle_length > 1);
2578 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2579 int len = needle_length - start;
2580 biased_suffixes_ = suffixes_ - start;
2581 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2582 for (int i = 0; i <= len; i++) {
2583 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002584 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002585 }
2586 inline int& suffix(int index) {
2587 ASSERT(biased_suffixes_ + index >= suffixes_);
2588 return biased_suffixes_[index];
2589 }
2590 inline int& shift(int index) {
2591 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2592 return biased_good_suffix_shift_[index];
2593 }
2594 private:
2595 int suffixes_[kBMMaxShift + 1];
2596 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002597 int* biased_suffixes_;
2598 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002599 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2600};
2601
2602// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002603static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002604static BMGoodSuffixBuffers bmgs_buffers;
2605
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002606// State of the string match tables.
2607// SIMPLE: No usable content in the buffers.
2608// BOYER_MOORE_HORSPOOL: The bad_char_occurences table has been populated.
2609// BOYER_MOORE: The bmgs_buffers tables have also been populated.
2610// Whenever starting with a new needle, one should call InitializeStringSearch
2611// to determine which search strategy to use, and in the case of a long-needle
2612// strategy, the call also initializes the algorithm to SIMPLE.
2613enum StringSearchAlgorithm { SIMPLE_SEARCH, BOYER_MOORE_HORSPOOL, BOYER_MOORE };
2614static StringSearchAlgorithm algorithm;
2615
2616
ager@chromium.org7c537e22008-10-16 08:43:32 +00002617// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002618template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002619static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern) {
2620 // Only preprocess at most kBMMaxShift last characters of pattern.
2621 int start = pattern.length() < kBMMaxShift ? 0
2622 : pattern.length() - kBMMaxShift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002623 // Run forwards to populate bad_char_table, so that *last* instance
2624 // of character equivalence class is the one registered.
2625 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002626 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2627 : kBMAlphabetSize;
2628 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002629 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002630 } else {
2631 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002632 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002633 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002634 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002635 for (int i = start; i < pattern.length() - 1; i++) {
2636 pchar c = pattern[i];
2637 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002638 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002639 }
2640}
2641
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002642
ager@chromium.org7c537e22008-10-16 08:43:32 +00002643template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002644static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002645 int m = pattern.length();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002646 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002647 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002648 // Compute Good Suffix tables.
2649 bmgs_buffers.init(m);
2650
2651 bmgs_buffers.shift(m-1) = 1;
2652 bmgs_buffers.suffix(m) = m + 1;
2653 pchar last_char = pattern[m - 1];
2654 int suffix = m + 1;
2655 for (int i = m; i > start;) {
2656 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2657 if (bmgs_buffers.shift(suffix) == len) {
2658 bmgs_buffers.shift(suffix) = suffix - i;
2659 }
2660 suffix = bmgs_buffers.suffix(suffix);
2661 }
2662 i--;
2663 suffix--;
2664 bmgs_buffers.suffix(i) = suffix;
2665 if (suffix == m) {
2666 // No suffix to extend, so we check against last_char only.
2667 while (i > start && pattern[i - 1] != last_char) {
2668 if (bmgs_buffers.shift(m) == len) {
2669 bmgs_buffers.shift(m) = m - i;
2670 }
2671 i--;
2672 bmgs_buffers.suffix(i) = m;
2673 }
2674 if (i > start) {
2675 i--;
2676 suffix--;
2677 bmgs_buffers.suffix(i) = suffix;
2678 }
2679 }
2680 }
2681 if (suffix < m) {
2682 for (int i = start; i <= m; i++) {
2683 if (bmgs_buffers.shift(i) == len) {
2684 bmgs_buffers.shift(i) = suffix - start;
2685 }
2686 if (i == suffix) {
2687 suffix = bmgs_buffers.suffix(suffix);
2688 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002689 }
2690 }
2691}
2692
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002693
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002694template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002695static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002696 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002697 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002698 }
2699 if (sizeof(pchar) == 1) {
2700 if (char_code > String::kMaxAsciiCharCode) {
2701 return -1;
2702 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002703 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002704 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002705 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002706}
2707
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002708
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002709// Restricted simplified Boyer-Moore string matching.
2710// Uses only the bad-shift table of Boyer-Moore and only uses it
2711// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002712template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002713static int BoyerMooreHorspool(Vector<const schar> subject,
2714 Vector<const pchar> pattern,
2715 int start_index,
2716 bool* complete) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002717 ASSERT(algorithm <= BOYER_MOORE_HORSPOOL);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002718 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002719 int m = pattern.length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002720
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002721 int badness = -m;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002722
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002723 // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002724 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002725 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002726 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002727 // Perform search
2728 for (idx = start_index; idx <= n - m;) {
2729 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002730 int c;
2731 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002732 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002733 int shift = j - bc_occ;
2734 idx += shift;
2735 badness += 1 - shift; // at most zero, so badness cannot increase.
2736 if (idx > n - m) {
2737 *complete = true;
2738 return -1;
2739 }
2740 }
2741 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002742 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002743 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002744 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002745 return idx;
2746 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002747 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002748 // Badness increases by the number of characters we have
2749 // checked, and decreases by the number of characters we
2750 // can skip by shifting. It's a measure of how we are doing
2751 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002752 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002753 if (badness > 0) {
2754 *complete = false;
2755 return idx;
2756 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002757 }
2758 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002759 *complete = true;
2760 return -1;
2761}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002762
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002763
2764template <typename schar, typename pchar>
2765static int BoyerMooreIndexOf(Vector<const schar> subject,
2766 Vector<const pchar> pattern,
2767 int idx) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002768 ASSERT(algorithm <= BOYER_MOORE);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002769 int n = subject.length();
2770 int m = pattern.length();
2771 // Only preprocess at most kBMMaxShift last characters of pattern.
2772 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2773
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002774 pchar last_char = pattern[m - 1];
2775 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002776 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002777 int j = m - 1;
2778 schar c;
2779 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002780 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002781 idx += shift;
2782 if (idx > n - m) {
2783 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002784 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002785 }
2786 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2787 if (j < 0) {
2788 return idx;
2789 } else if (j < start) {
2790 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002791 // Fall back on BMH shift.
2792 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002793 } else {
2794 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002795 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002796 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002797 if (gs_shift > shift) {
2798 shift = gs_shift;
2799 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002800 idx += shift;
2801 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002802 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002803
2804 return -1;
2805}
2806
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002807
2808template <typename schar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002809static inline int SingleCharIndexOf(Vector<const schar> string,
2810 schar pattern_char,
2811 int start_index) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002812 if (sizeof(schar) == 1) {
2813 const schar* pos = reinterpret_cast<const schar*>(
2814 memchr(string.start() + start_index,
2815 pattern_char,
2816 string.length() - start_index));
2817 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002818 return static_cast<int>(pos - string.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002819 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002820 for (int i = start_index, n = string.length(); i < n; i++) {
2821 if (pattern_char == string[i]) {
2822 return i;
2823 }
2824 }
2825 return -1;
2826}
2827
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002828
2829template <typename schar>
2830static int SingleCharLastIndexOf(Vector<const schar> string,
2831 schar pattern_char,
2832 int start_index) {
2833 for (int i = start_index; i >= 0; i--) {
2834 if (pattern_char == string[i]) {
2835 return i;
2836 }
2837 }
2838 return -1;
2839}
2840
2841
ager@chromium.org7c537e22008-10-16 08:43:32 +00002842// Trivial string search for shorter strings.
2843// On return, if "complete" is set to true, the return value is the
2844// final result of searching for the patter in the subject.
2845// If "complete" is set to false, the return value is the index where
2846// further checking should start, i.e., it's guaranteed that the pattern
2847// does not occur at a position prior to the returned index.
2848template <typename pchar, typename schar>
2849static int SimpleIndexOf(Vector<const schar> subject,
2850 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002851 int idx,
2852 bool* complete) {
2853 // Badness is a count of how much work we have done. When we have
2854 // done enough work we decide it's probably worth switching to a better
2855 // algorithm.
2856 int badness = -10 - (pattern.length() << 2);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002857
ager@chromium.org7c537e22008-10-16 08:43:32 +00002858 // We know our pattern is at least 2 characters, we cache the first so
2859 // the common case of the first character not matching is faster.
2860 pchar pattern_first_char = pattern[0];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002861 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2862 badness++;
2863 if (badness > 0) {
2864 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002865 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002866 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002867 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2868 const schar* pos = reinterpret_cast<const schar*>(
2869 memchr(subject.start() + i,
2870 pattern_first_char,
2871 n - i + 1));
2872 if (pos == NULL) {
2873 *complete = true;
2874 return -1;
2875 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002876 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002877 } else {
2878 if (subject[i] != pattern_first_char) continue;
2879 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002880 int j = 1;
2881 do {
2882 if (pattern[j] != subject[i+j]) {
2883 break;
2884 }
2885 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002886 } while (j < pattern.length());
2887 if (j == pattern.length()) {
2888 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002889 return i;
2890 }
2891 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002892 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002893 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002894 return -1;
2895}
2896
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002897// Simple indexOf that never bails out. For short patterns only.
2898template <typename pchar, typename schar>
2899static int SimpleIndexOf(Vector<const schar> subject,
2900 Vector<const pchar> pattern,
2901 int idx) {
2902 pchar pattern_first_char = pattern[0];
2903 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002904 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2905 const schar* pos = reinterpret_cast<const schar*>(
2906 memchr(subject.start() + i,
2907 pattern_first_char,
2908 n - i + 1));
2909 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002910 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002911 } else {
2912 if (subject[i] != pattern_first_char) continue;
2913 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002914 int j = 1;
2915 do {
2916 if (pattern[j] != subject[i+j]) {
2917 break;
2918 }
2919 j++;
2920 } while (j < pattern.length());
2921 if (j == pattern.length()) {
2922 return i;
2923 }
2924 }
2925 return -1;
2926}
2927
2928
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002929// Strategy for searching for a string in another string.
2930enum StringSearchStrategy { SEARCH_FAIL, SEARCH_SHORT, SEARCH_LONG };
ager@chromium.org7c537e22008-10-16 08:43:32 +00002931
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002932
2933template <typename pchar>
2934static inline StringSearchStrategy InitializeStringSearch(
2935 Vector<const pchar> pat, bool ascii_subject) {
2936 ASSERT(pat.length() > 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002937 // We have an ASCII haystack and a non-ASCII needle. Check if there
2938 // really is a non-ASCII character in the needle and bail out if there
2939 // is.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002940 if (ascii_subject && sizeof(pchar) > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002941 for (int i = 0; i < pat.length(); i++) {
2942 uc16 c = pat[i];
2943 if (c > String::kMaxAsciiCharCode) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002944 return SEARCH_FAIL;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002945 }
2946 }
2947 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002948 if (pat.length() < kBMMinPatternLength) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002949 return SEARCH_SHORT;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002950 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002951 algorithm = SIMPLE_SEARCH;
2952 return SEARCH_LONG;
2953}
2954
2955
2956// Dispatch long needle searches to different algorithms.
2957template <typename schar, typename pchar>
2958static int ComplexIndexOf(Vector<const schar> sub,
2959 Vector<const pchar> pat,
2960 int start_index) {
2961 ASSERT(pat.length() >= kBMMinPatternLength);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002962 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002963 bool complete;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002964 int idx = start_index;
2965 switch (algorithm) {
2966 case SIMPLE_SEARCH:
2967 idx = SimpleIndexOf(sub, pat, idx, &complete);
2968 if (complete) return idx;
2969 BoyerMoorePopulateBadCharTable(pat);
2970 algorithm = BOYER_MOORE_HORSPOOL;
2971 // FALLTHROUGH.
2972 case BOYER_MOORE_HORSPOOL:
2973 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
2974 if (complete) return idx;
2975 // Build the Good Suffix table and continue searching.
2976 BoyerMoorePopulateGoodSuffixTable(pat);
2977 algorithm = BOYER_MOORE;
2978 // FALLTHROUGH.
2979 case BOYER_MOORE:
2980 return BoyerMooreIndexOf(sub, pat, idx);
2981 }
2982 UNREACHABLE();
2983 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002984}
2985
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002986
2987// Dispatch to different search strategies for a single search.
2988// If searching multiple times on the same needle, the search
2989// strategy should only be computed once and then dispatch to different
2990// loops.
2991template <typename schar, typename pchar>
2992static int StringSearch(Vector<const schar> sub,
2993 Vector<const pchar> pat,
2994 int start_index) {
2995 bool ascii_subject = (sizeof(schar) == 1);
2996 StringSearchStrategy strategy = InitializeStringSearch(pat, ascii_subject);
2997 switch (strategy) {
2998 case SEARCH_FAIL: return -1;
2999 case SEARCH_SHORT: return SimpleIndexOf(sub, pat, start_index);
3000 case SEARCH_LONG: return ComplexIndexOf(sub, pat, start_index);
3001 }
3002 UNREACHABLE();
3003 return -1;
3004}
3005
3006
ager@chromium.org7c537e22008-10-16 08:43:32 +00003007// Perform string match of pattern on subject, starting at start index.
3008// Caller must ensure that 0 <= start_index <= sub->length(),
3009// and should check that pat->length() + start_index <= sub->length()
3010int Runtime::StringMatch(Handle<String> sub,
3011 Handle<String> pat,
3012 int start_index) {
3013 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003014 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00003015
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003016 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003017 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003018
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003019 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003020 if (start_index + pattern_length > subject_length) return -1;
3021
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003022 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003023 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003024 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00003025
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003026 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00003027 // character patterns linear search is necessary, so any smart
3028 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003029 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003030 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
lrn@chromium.org32d961d2010-06-30 09:09:34 +00003031 String* seq_sub = *sub;
3032 if (seq_sub->IsConsString()) {
3033 seq_sub = ConsString::cast(seq_sub)->first();
3034 }
3035 if (seq_sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003036 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003037 if (pchar > String::kMaxAsciiCharCode) {
3038 return -1;
3039 }
3040 Vector<const char> ascii_vector =
lrn@chromium.org32d961d2010-06-30 09:09:34 +00003041 seq_sub->ToAsciiVector().SubVector(start_index, subject_length);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003042 const void* pos = memchr(ascii_vector.start(),
3043 static_cast<const char>(pchar),
3044 static_cast<size_t>(ascii_vector.length()));
3045 if (pos == NULL) {
3046 return -1;
3047 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003048 return static_cast<int>(reinterpret_cast<const char*>(pos)
3049 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003050 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00003051 return SingleCharIndexOf(seq_sub->ToUC16Vector(),
3052 pat->Get(0),
3053 start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003054 }
3055
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003056 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003057 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003058 }
ager@chromium.org236ad962008-09-25 09:45:57 +00003059
ager@chromium.org7c537e22008-10-16 08:43:32 +00003060 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
lrn@chromium.org32d961d2010-06-30 09:09:34 +00003061 // Extract flattened substrings of cons strings before determining asciiness.
3062 String* seq_sub = *sub;
3063 if (seq_sub->IsConsString()) {
3064 seq_sub = ConsString::cast(seq_sub)->first();
3065 }
3066 String* seq_pat = *pat;
3067 if (seq_pat->IsConsString()) {
3068 seq_pat = ConsString::cast(seq_pat)->first();
3069 }
3070
ager@chromium.org7c537e22008-10-16 08:43:32 +00003071 // dispatch on type of strings
lrn@chromium.org32d961d2010-06-30 09:09:34 +00003072 if (seq_pat->IsAsciiRepresentation()) {
3073 Vector<const char> pat_vector = seq_pat->ToAsciiVector();
3074 if (seq_sub->IsAsciiRepresentation()) {
3075 return StringSearch(seq_sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00003076 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00003077 return StringSearch(seq_sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00003078 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00003079 Vector<const uc16> pat_vector = seq_pat->ToUC16Vector();
3080 if (seq_sub->IsAsciiRepresentation()) {
3081 return StringSearch(seq_sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003082 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00003083 return StringSearch(seq_sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003084}
3085
3086
3087static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003088 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003089 ASSERT(args.length() == 3);
3090
ager@chromium.org7c537e22008-10-16 08:43:32 +00003091 CONVERT_ARG_CHECKED(String, sub, 0);
3092 CONVERT_ARG_CHECKED(String, pat, 1);
3093
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003094 Object* index = args[2];
3095 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003096 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003097
ager@chromium.org870a0b62008-11-04 11:43:05 +00003098 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00003099 int position = Runtime::StringMatch(sub, pat, start_index);
3100 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003101}
3102
3103
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003104template <typename schar, typename pchar>
3105static int StringMatchBackwards(Vector<const schar> sub,
3106 Vector<const pchar> pat,
3107 int idx) {
3108 ASSERT(pat.length() >= 1);
3109 ASSERT(idx + pat.length() <= sub.length());
3110
3111 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
3112 for (int i = 0; i < pat.length(); i++) {
3113 uc16 c = pat[i];
3114 if (c > String::kMaxAsciiCharCode) {
3115 return -1;
3116 }
3117 }
3118 }
3119
3120 pchar pattern_first_char = pat[0];
3121 for (int i = idx; i >= 0; i--) {
3122 if (sub[i] != pattern_first_char) continue;
3123 int j = 1;
3124 while (j < pat.length()) {
3125 if (pat[j] != sub[i+j]) {
3126 break;
3127 }
3128 j++;
3129 }
3130 if (j == pat.length()) {
3131 return i;
3132 }
3133 }
3134 return -1;
3135}
3136
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003137static Object* Runtime_StringLastIndexOf(Arguments args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003138 HandleScope scope; // create a new handle scope
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003139 ASSERT(args.length() == 3);
3140
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003141 CONVERT_ARG_CHECKED(String, sub, 0);
3142 CONVERT_ARG_CHECKED(String, pat, 1);
3143
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003144 Object* index = args[2];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003145 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003146 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003147
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003148 uint32_t pat_length = pat->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003149 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003150
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003151 if (start_index + pat_length > sub_length) {
3152 start_index = sub_length - pat_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00003153 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003154
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003155 if (pat_length == 0) {
3156 return Smi::FromInt(start_index);
3157 }
3158
3159 if (!sub->IsFlat()) {
3160 FlattenString(sub);
3161 }
3162
3163 if (pat_length == 1) {
3164 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
3165 if (sub->IsAsciiRepresentation()) {
3166 uc16 pchar = pat->Get(0);
3167 if (pchar > String::kMaxAsciiCharCode) {
3168 return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003169 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003170 return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
3171 static_cast<char>(pat->Get(0)),
3172 start_index));
3173 } else {
3174 return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
3175 pat->Get(0),
3176 start_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003177 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003178 }
3179
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003180 if (!pat->IsFlat()) {
3181 FlattenString(pat);
3182 }
3183
3184 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
3185
3186 int position = -1;
3187
3188 if (pat->IsAsciiRepresentation()) {
3189 Vector<const char> pat_vector = pat->ToAsciiVector();
3190 if (sub->IsAsciiRepresentation()) {
3191 position = StringMatchBackwards(sub->ToAsciiVector(),
3192 pat_vector,
3193 start_index);
3194 } else {
3195 position = StringMatchBackwards(sub->ToUC16Vector(),
3196 pat_vector,
3197 start_index);
3198 }
3199 } else {
3200 Vector<const uc16> pat_vector = pat->ToUC16Vector();
3201 if (sub->IsAsciiRepresentation()) {
3202 position = StringMatchBackwards(sub->ToAsciiVector(),
3203 pat_vector,
3204 start_index);
3205 } else {
3206 position = StringMatchBackwards(sub->ToUC16Vector(),
3207 pat_vector,
3208 start_index);
3209 }
3210 }
3211
3212 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003213}
3214
3215
3216static Object* Runtime_StringLocaleCompare(Arguments args) {
3217 NoHandleAllocation ha;
3218 ASSERT(args.length() == 2);
3219
3220 CONVERT_CHECKED(String, str1, args[0]);
3221 CONVERT_CHECKED(String, str2, args[1]);
3222
3223 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003224 int str1_length = str1->length();
3225 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003226
3227 // Decide trivial cases without flattening.
3228 if (str1_length == 0) {
3229 if (str2_length == 0) return Smi::FromInt(0); // Equal.
3230 return Smi::FromInt(-str2_length);
3231 } else {
3232 if (str2_length == 0) return Smi::FromInt(str1_length);
3233 }
3234
3235 int end = str1_length < str2_length ? str1_length : str2_length;
3236
3237 // No need to flatten if we are going to find the answer on the first
3238 // character. At this point we know there is at least one character
3239 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003240 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003241 if (d != 0) return Smi::FromInt(d);
3242
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003243 str1->TryFlatten();
3244 str2->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003245
3246 static StringInputBuffer buf1;
3247 static StringInputBuffer buf2;
3248
3249 buf1.Reset(str1);
3250 buf2.Reset(str2);
3251
3252 for (int i = 0; i < end; i++) {
3253 uint16_t char1 = buf1.GetNext();
3254 uint16_t char2 = buf2.GetNext();
3255 if (char1 != char2) return Smi::FromInt(char1 - char2);
3256 }
3257
3258 return Smi::FromInt(str1_length - str2_length);
3259}
3260
3261
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003262static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003263 NoHandleAllocation ha;
3264 ASSERT(args.length() == 3);
3265
3266 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00003267 Object* from = args[1];
3268 Object* to = args[2];
3269 int start, end;
3270 // We have a fast integer-only case here to avoid a conversion to double in
3271 // the common case where from and to are Smis.
3272 if (from->IsSmi() && to->IsSmi()) {
3273 start = Smi::cast(from)->value();
3274 end = Smi::cast(to)->value();
3275 } else {
3276 CONVERT_DOUBLE_CHECKED(from_number, from);
3277 CONVERT_DOUBLE_CHECKED(to_number, to);
3278 start = FastD2I(from_number);
3279 end = FastD2I(to_number);
3280 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003281 RUNTIME_ASSERT(end >= start);
3282 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003283 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003284 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003285 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003286}
3287
3288
ager@chromium.org41826e72009-03-30 13:30:57 +00003289static Object* Runtime_StringMatch(Arguments args) {
3290 ASSERT_EQ(3, args.length());
3291
3292 CONVERT_ARG_CHECKED(String, subject, 0);
3293 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
3294 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
3295 HandleScope handles;
3296
3297 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
3298
3299 if (match.is_null()) {
3300 return Failure::Exception();
3301 }
3302 if (match->IsNull()) {
3303 return Heap::null_value();
3304 }
3305 int length = subject->length();
3306
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00003307 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00003308 ZoneList<int> offsets(8);
3309 do {
3310 int start;
3311 int end;
3312 {
3313 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003314 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00003315 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
3316 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
3317 }
3318 offsets.Add(start);
3319 offsets.Add(end);
3320 int index = start < end ? end : end + 1;
3321 if (index > length) break;
3322 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
3323 if (match.is_null()) {
3324 return Failure::Exception();
3325 }
3326 } while (!match->IsNull());
3327 int matches = offsets.length() / 2;
3328 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
3329 for (int i = 0; i < matches ; i++) {
3330 int from = offsets.at(i * 2);
3331 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003332 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00003333 }
3334 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
3335 result->set_length(Smi::FromInt(matches));
3336 return *result;
3337}
3338
3339
lrn@chromium.org25156de2010-04-06 13:10:27 +00003340// Two smis before and after the match, for very long strings.
3341const int kMaxBuilderEntriesPerRegExpMatch = 5;
3342
3343
3344static void SetLastMatchInfoNoCaptures(Handle<String> subject,
3345 Handle<JSArray> last_match_info,
3346 int match_start,
3347 int match_end) {
3348 // Fill last_match_info with a single capture.
3349 last_match_info->EnsureSize(2 + RegExpImpl::kLastMatchOverhead);
3350 AssertNoAllocation no_gc;
3351 FixedArray* elements = FixedArray::cast(last_match_info->elements());
3352 RegExpImpl::SetLastCaptureCount(elements, 2);
3353 RegExpImpl::SetLastInput(elements, *subject);
3354 RegExpImpl::SetLastSubject(elements, *subject);
3355 RegExpImpl::SetCapture(elements, 0, match_start);
3356 RegExpImpl::SetCapture(elements, 1, match_end);
3357}
3358
3359
3360template <typename schar>
3361static bool SearchCharMultiple(Vector<schar> subject,
3362 String* pattern,
3363 schar pattern_char,
3364 FixedArrayBuilder* builder,
3365 int* match_pos) {
3366 // Position of last match.
3367 int pos = *match_pos;
3368 int subject_length = subject.length();
3369 while (pos < subject_length) {
3370 int match_end = pos + 1;
3371 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3372 *match_pos = pos;
3373 return false;
3374 }
3375 int new_pos = SingleCharIndexOf(subject, pattern_char, match_end);
3376 if (new_pos >= 0) {
3377 // Match has been found.
3378 if (new_pos > match_end) {
3379 ReplacementStringBuilder::AddSubjectSlice(builder, match_end, new_pos);
3380 }
3381 pos = new_pos;
3382 builder->Add(pattern);
3383 } else {
3384 break;
3385 }
3386 }
3387 if (pos + 1 < subject_length) {
3388 ReplacementStringBuilder::AddSubjectSlice(builder, pos + 1, subject_length);
3389 }
3390 *match_pos = pos;
3391 return true;
3392}
3393
3394
3395static bool SearchCharMultiple(Handle<String> subject,
3396 Handle<String> pattern,
3397 Handle<JSArray> last_match_info,
3398 FixedArrayBuilder* builder) {
3399 ASSERT(subject->IsFlat());
3400 ASSERT_EQ(1, pattern->length());
3401 uc16 pattern_char = pattern->Get(0);
3402 // Treating position before first as initial "previous match position".
3403 int match_pos = -1;
3404
3405 for (;;) { // Break when search complete.
3406 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3407 AssertNoAllocation no_gc;
3408 if (subject->IsAsciiRepresentation()) {
3409 if (pattern_char > String::kMaxAsciiCharCode) {
3410 break;
3411 }
3412 Vector<const char> subject_vector = subject->ToAsciiVector();
3413 char pattern_ascii_char = static_cast<char>(pattern_char);
3414 bool complete = SearchCharMultiple<const char>(subject_vector,
3415 *pattern,
3416 pattern_ascii_char,
3417 builder,
3418 &match_pos);
3419 if (complete) break;
3420 } else {
3421 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3422 bool complete = SearchCharMultiple<const uc16>(subject_vector,
3423 *pattern,
3424 pattern_char,
3425 builder,
3426 &match_pos);
3427 if (complete) break;
3428 }
3429 }
3430
3431 if (match_pos >= 0) {
3432 SetLastMatchInfoNoCaptures(subject,
3433 last_match_info,
3434 match_pos,
3435 match_pos + 1);
3436 return true;
3437 }
3438 return false; // No matches at all.
3439}
3440
3441
3442template <typename schar, typename pchar>
3443static bool SearchStringMultiple(Vector<schar> subject,
3444 String* pattern,
3445 Vector<pchar> pattern_string,
3446 FixedArrayBuilder* builder,
3447 int* match_pos) {
3448 int pos = *match_pos;
3449 int subject_length = subject.length();
3450 int pattern_length = pattern_string.length();
3451 int max_search_start = subject_length - pattern_length;
3452 bool is_ascii = (sizeof(schar) == 1);
3453 StringSearchStrategy strategy =
3454 InitializeStringSearch(pattern_string, is_ascii);
3455 switch (strategy) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003456 case SEARCH_FAIL: break;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003457 case SEARCH_SHORT:
3458 while (pos <= max_search_start) {
3459 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3460 *match_pos = pos;
3461 return false;
3462 }
3463 // Position of end of previous match.
3464 int match_end = pos + pattern_length;
3465 int new_pos = SimpleIndexOf(subject, pattern_string, match_end);
3466 if (new_pos >= 0) {
3467 // A match.
3468 if (new_pos > match_end) {
3469 ReplacementStringBuilder::AddSubjectSlice(builder,
3470 match_end,
3471 new_pos);
3472 }
3473 pos = new_pos;
3474 builder->Add(pattern);
3475 } else {
3476 break;
3477 }
3478 }
3479 break;
3480 case SEARCH_LONG:
3481 while (pos <= max_search_start) {
3482 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003483 *match_pos = pos;
3484 return false;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003485 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003486 int match_end = pos + pattern_length;
3487 int new_pos = ComplexIndexOf(subject, pattern_string, match_end);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003488 if (new_pos >= 0) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003489 // A match has been found.
3490 if (new_pos > match_end) {
3491 ReplacementStringBuilder::AddSubjectSlice(builder,
3492 match_end,
3493 new_pos);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003494 }
3495 pos = new_pos;
3496 builder->Add(pattern);
3497 } else {
3498 break;
3499 }
3500 }
3501 break;
3502 }
3503 if (pos < max_search_start) {
3504 ReplacementStringBuilder::AddSubjectSlice(builder,
3505 pos + pattern_length,
3506 subject_length);
3507 }
3508 *match_pos = pos;
3509 return true;
3510}
3511
3512
3513static bool SearchStringMultiple(Handle<String> subject,
3514 Handle<String> pattern,
3515 Handle<JSArray> last_match_info,
3516 FixedArrayBuilder* builder) {
3517 ASSERT(subject->IsFlat());
3518 ASSERT(pattern->IsFlat());
3519 ASSERT(pattern->length() > 1);
3520
3521 // Treating as if a previous match was before first character.
3522 int match_pos = -pattern->length();
3523
3524 for (;;) { // Break when search complete.
3525 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3526 AssertNoAllocation no_gc;
3527 if (subject->IsAsciiRepresentation()) {
3528 Vector<const char> subject_vector = subject->ToAsciiVector();
3529 if (pattern->IsAsciiRepresentation()) {
3530 if (SearchStringMultiple(subject_vector,
3531 *pattern,
3532 pattern->ToAsciiVector(),
3533 builder,
3534 &match_pos)) break;
3535 } else {
3536 if (SearchStringMultiple(subject_vector,
3537 *pattern,
3538 pattern->ToUC16Vector(),
3539 builder,
3540 &match_pos)) break;
3541 }
3542 } else {
3543 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3544 if (pattern->IsAsciiRepresentation()) {
3545 if (SearchStringMultiple(subject_vector,
3546 *pattern,
3547 pattern->ToAsciiVector(),
3548 builder,
3549 &match_pos)) break;
3550 } else {
3551 if (SearchStringMultiple(subject_vector,
3552 *pattern,
3553 pattern->ToUC16Vector(),
3554 builder,
3555 &match_pos)) break;
3556 }
3557 }
3558 }
3559
3560 if (match_pos >= 0) {
3561 SetLastMatchInfoNoCaptures(subject,
3562 last_match_info,
3563 match_pos,
3564 match_pos + pattern->length());
3565 return true;
3566 }
3567 return false; // No matches at all.
3568}
3569
3570
3571static RegExpImpl::IrregexpResult SearchRegExpNoCaptureMultiple(
3572 Handle<String> subject,
3573 Handle<JSRegExp> regexp,
3574 Handle<JSArray> last_match_array,
3575 FixedArrayBuilder* builder) {
3576 ASSERT(subject->IsFlat());
3577 int match_start = -1;
3578 int match_end = 0;
3579 int pos = 0;
3580 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3581 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3582
3583 OffsetsVector registers(required_registers);
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00003584 Vector<int32_t> register_vector(registers.vector(), registers.length());
lrn@chromium.org25156de2010-04-06 13:10:27 +00003585 int subject_length = subject->length();
3586
3587 for (;;) { // Break on failure, return on exception.
3588 RegExpImpl::IrregexpResult result =
3589 RegExpImpl::IrregexpExecOnce(regexp,
3590 subject,
3591 pos,
3592 register_vector);
3593 if (result == RegExpImpl::RE_SUCCESS) {
3594 match_start = register_vector[0];
3595 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3596 if (match_end < match_start) {
3597 ReplacementStringBuilder::AddSubjectSlice(builder,
3598 match_end,
3599 match_start);
3600 }
3601 match_end = register_vector[1];
3602 HandleScope loop_scope;
3603 builder->Add(*Factory::NewSubString(subject, match_start, match_end));
3604 if (match_start != match_end) {
3605 pos = match_end;
3606 } else {
3607 pos = match_end + 1;
3608 if (pos > subject_length) break;
3609 }
3610 } else if (result == RegExpImpl::RE_FAILURE) {
3611 break;
3612 } else {
3613 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3614 return result;
3615 }
3616 }
3617
3618 if (match_start >= 0) {
3619 if (match_end < subject_length) {
3620 ReplacementStringBuilder::AddSubjectSlice(builder,
3621 match_end,
3622 subject_length);
3623 }
3624 SetLastMatchInfoNoCaptures(subject,
3625 last_match_array,
3626 match_start,
3627 match_end);
3628 return RegExpImpl::RE_SUCCESS;
3629 } else {
3630 return RegExpImpl::RE_FAILURE; // No matches at all.
3631 }
3632}
3633
3634
3635static RegExpImpl::IrregexpResult SearchRegExpMultiple(
3636 Handle<String> subject,
3637 Handle<JSRegExp> regexp,
3638 Handle<JSArray> last_match_array,
3639 FixedArrayBuilder* builder) {
3640
3641 ASSERT(subject->IsFlat());
3642 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3643 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3644
3645 OffsetsVector registers(required_registers);
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00003646 Vector<int32_t> register_vector(registers.vector(), registers.length());
lrn@chromium.org25156de2010-04-06 13:10:27 +00003647
3648 RegExpImpl::IrregexpResult result =
3649 RegExpImpl::IrregexpExecOnce(regexp,
3650 subject,
3651 0,
3652 register_vector);
3653
3654 int capture_count = regexp->CaptureCount();
3655 int subject_length = subject->length();
3656
3657 // Position to search from.
3658 int pos = 0;
3659 // End of previous match. Differs from pos if match was empty.
3660 int match_end = 0;
3661 if (result == RegExpImpl::RE_SUCCESS) {
3662 // Need to keep a copy of the previous match for creating last_match_info
3663 // at the end, so we have two vectors that we swap between.
3664 OffsetsVector registers2(required_registers);
3665 Vector<int> prev_register_vector(registers2.vector(), registers2.length());
3666
3667 do {
3668 int match_start = register_vector[0];
3669 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3670 if (match_end < match_start) {
3671 ReplacementStringBuilder::AddSubjectSlice(builder,
3672 match_end,
3673 match_start);
3674 }
3675 match_end = register_vector[1];
3676
3677 {
3678 // Avoid accumulating new handles inside loop.
3679 HandleScope temp_scope;
3680 // Arguments array to replace function is match, captures, index and
3681 // subject, i.e., 3 + capture count in total.
3682 Handle<FixedArray> elements = Factory::NewFixedArray(3 + capture_count);
3683 elements->set(0, *Factory::NewSubString(subject,
3684 match_start,
3685 match_end));
3686 for (int i = 1; i <= capture_count; i++) {
3687 int start = register_vector[i * 2];
3688 if (start >= 0) {
3689 int end = register_vector[i * 2 + 1];
3690 ASSERT(start <= end);
3691 Handle<String> substring = Factory::NewSubString(subject,
3692 start,
3693 end);
3694 elements->set(i, *substring);
3695 } else {
3696 ASSERT(register_vector[i * 2 + 1] < 0);
3697 elements->set(i, Heap::undefined_value());
3698 }
3699 }
3700 elements->set(capture_count + 1, Smi::FromInt(match_start));
3701 elements->set(capture_count + 2, *subject);
3702 builder->Add(*Factory::NewJSArrayWithElements(elements));
3703 }
3704 // Swap register vectors, so the last successful match is in
3705 // prev_register_vector.
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00003706 Vector<int32_t> tmp = prev_register_vector;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003707 prev_register_vector = register_vector;
3708 register_vector = tmp;
3709
3710 if (match_end > match_start) {
3711 pos = match_end;
3712 } else {
3713 pos = match_end + 1;
3714 if (pos > subject_length) {
3715 break;
3716 }
3717 }
3718
3719 result = RegExpImpl::IrregexpExecOnce(regexp,
3720 subject,
3721 pos,
3722 register_vector);
3723 } while (result == RegExpImpl::RE_SUCCESS);
3724
3725 if (result != RegExpImpl::RE_EXCEPTION) {
3726 // Finished matching, with at least one match.
3727 if (match_end < subject_length) {
3728 ReplacementStringBuilder::AddSubjectSlice(builder,
3729 match_end,
3730 subject_length);
3731 }
3732
3733 int last_match_capture_count = (capture_count + 1) * 2;
3734 int last_match_array_size =
3735 last_match_capture_count + RegExpImpl::kLastMatchOverhead;
3736 last_match_array->EnsureSize(last_match_array_size);
3737 AssertNoAllocation no_gc;
3738 FixedArray* elements = FixedArray::cast(last_match_array->elements());
3739 RegExpImpl::SetLastCaptureCount(elements, last_match_capture_count);
3740 RegExpImpl::SetLastSubject(elements, *subject);
3741 RegExpImpl::SetLastInput(elements, *subject);
3742 for (int i = 0; i < last_match_capture_count; i++) {
3743 RegExpImpl::SetCapture(elements, i, prev_register_vector[i]);
3744 }
3745 return RegExpImpl::RE_SUCCESS;
3746 }
3747 }
3748 // No matches at all, return failure or exception result directly.
3749 return result;
3750}
3751
3752
3753static Object* Runtime_RegExpExecMultiple(Arguments args) {
3754 ASSERT(args.length() == 4);
3755 HandleScope handles;
3756
3757 CONVERT_ARG_CHECKED(String, subject, 1);
3758 if (!subject->IsFlat()) { FlattenString(subject); }
3759 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
3760 CONVERT_ARG_CHECKED(JSArray, last_match_info, 2);
3761 CONVERT_ARG_CHECKED(JSArray, result_array, 3);
3762
3763 ASSERT(last_match_info->HasFastElements());
3764 ASSERT(regexp->GetFlags().is_global());
3765 Handle<FixedArray> result_elements;
3766 if (result_array->HasFastElements()) {
3767 result_elements =
3768 Handle<FixedArray>(FixedArray::cast(result_array->elements()));
3769 } else {
3770 result_elements = Factory::NewFixedArrayWithHoles(16);
3771 }
3772 FixedArrayBuilder builder(result_elements);
3773
3774 if (regexp->TypeTag() == JSRegExp::ATOM) {
3775 Handle<String> pattern(
3776 String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex)));
3777 int pattern_length = pattern->length();
3778 if (pattern_length == 1) {
3779 if (SearchCharMultiple(subject, pattern, last_match_info, &builder)) {
3780 return *builder.ToJSArray(result_array);
3781 }
3782 return Heap::null_value();
3783 }
3784
3785 if (!pattern->IsFlat()) FlattenString(pattern);
3786 if (SearchStringMultiple(subject, pattern, last_match_info, &builder)) {
3787 return *builder.ToJSArray(result_array);
3788 }
3789 return Heap::null_value();
3790 }
3791
3792 ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
3793
3794 RegExpImpl::IrregexpResult result;
3795 if (regexp->CaptureCount() == 0) {
3796 result = SearchRegExpNoCaptureMultiple(subject,
3797 regexp,
3798 last_match_info,
3799 &builder);
3800 } else {
3801 result = SearchRegExpMultiple(subject, regexp, last_match_info, &builder);
3802 }
3803 if (result == RegExpImpl::RE_SUCCESS) return *builder.ToJSArray(result_array);
3804 if (result == RegExpImpl::RE_FAILURE) return Heap::null_value();
3805 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3806 return Failure::Exception();
3807}
3808
3809
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003810static Object* Runtime_NumberToRadixString(Arguments args) {
3811 NoHandleAllocation ha;
3812 ASSERT(args.length() == 2);
3813
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003814 // Fast case where the result is a one character string.
3815 if (args[0]->IsSmi() && args[1]->IsSmi()) {
3816 int value = Smi::cast(args[0])->value();
3817 int radix = Smi::cast(args[1])->value();
3818 if (value >= 0 && value < radix) {
3819 RUNTIME_ASSERT(radix <= 36);
3820 // Character array used for conversion.
3821 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
3822 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
3823 }
3824 }
3825
3826 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003827 CONVERT_DOUBLE_CHECKED(value, args[0]);
3828 if (isnan(value)) {
3829 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3830 }
3831 if (isinf(value)) {
3832 if (value < 0) {
3833 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3834 }
3835 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3836 }
3837 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
3838 int radix = FastD2I(radix_number);
3839 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3840 char* str = DoubleToRadixCString(value, radix);
3841 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3842 DeleteArray(str);
3843 return result;
3844}
3845
3846
3847static Object* Runtime_NumberToFixed(Arguments args) {
3848 NoHandleAllocation ha;
3849 ASSERT(args.length() == 2);
3850
3851 CONVERT_DOUBLE_CHECKED(value, args[0]);
3852 if (isnan(value)) {
3853 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3854 }
3855 if (isinf(value)) {
3856 if (value < 0) {
3857 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3858 }
3859 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3860 }
3861 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3862 int f = FastD2I(f_number);
3863 RUNTIME_ASSERT(f >= 0);
3864 char* str = DoubleToFixedCString(value, f);
3865 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3866 DeleteArray(str);
3867 return res;
3868}
3869
3870
3871static Object* Runtime_NumberToExponential(Arguments args) {
3872 NoHandleAllocation ha;
3873 ASSERT(args.length() == 2);
3874
3875 CONVERT_DOUBLE_CHECKED(value, args[0]);
3876 if (isnan(value)) {
3877 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3878 }
3879 if (isinf(value)) {
3880 if (value < 0) {
3881 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3882 }
3883 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3884 }
3885 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3886 int f = FastD2I(f_number);
3887 RUNTIME_ASSERT(f >= -1 && f <= 20);
3888 char* str = DoubleToExponentialCString(value, f);
3889 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3890 DeleteArray(str);
3891 return res;
3892}
3893
3894
3895static Object* Runtime_NumberToPrecision(Arguments args) {
3896 NoHandleAllocation ha;
3897 ASSERT(args.length() == 2);
3898
3899 CONVERT_DOUBLE_CHECKED(value, args[0]);
3900 if (isnan(value)) {
3901 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3902 }
3903 if (isinf(value)) {
3904 if (value < 0) {
3905 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3906 }
3907 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3908 }
3909 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3910 int f = FastD2I(f_number);
3911 RUNTIME_ASSERT(f >= 1 && f <= 21);
3912 char* str = DoubleToPrecisionCString(value, f);
3913 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3914 DeleteArray(str);
3915 return res;
3916}
3917
3918
3919// Returns a single character string where first character equals
3920// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003921static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003922 if (index < static_cast<uint32_t>(string->length())) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003923 string->TryFlatten();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003924 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003925 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003926 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003927 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003928}
3929
3930
3931Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
3932 // Handle [] indexing on Strings
3933 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003934 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
3935 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003936 }
3937
3938 // Handle [] indexing on String objects
3939 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003940 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
3941 Handle<Object> result =
3942 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
3943 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003944 }
3945
3946 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003947 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003948 return prototype->GetElement(index);
3949 }
3950
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003951 return GetElement(object, index);
3952}
3953
3954
3955Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003956 return object->GetElement(index);
3957}
3958
3959
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003960Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
3961 HandleScope scope;
3962
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003963 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003964 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003965 Handle<Object> error =
3966 Factory::NewTypeError("non_object_property_load",
3967 HandleVector(args, 2));
3968 return Top::Throw(*error);
3969 }
3970
3971 // Check if the given key is an array index.
3972 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003973 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003974 return GetElementOrCharAt(object, index);
3975 }
3976
3977 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003978 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003979 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003980 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003981 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003982 bool has_pending_exception = false;
3983 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003984 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003985 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003986 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003987 }
3988
ager@chromium.org32912102009-01-16 10:38:43 +00003989 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003990 // the element if so.
3991 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003992 return GetElementOrCharAt(object, index);
3993 } else {
3994 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003995 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003996 }
3997}
3998
3999
4000static Object* Runtime_GetProperty(Arguments args) {
4001 NoHandleAllocation ha;
4002 ASSERT(args.length() == 2);
4003
4004 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004005 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004006
4007 return Runtime::GetObjectProperty(object, key);
4008}
4009
4010
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004011// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00004012static Object* Runtime_KeyedGetProperty(Arguments args) {
4013 NoHandleAllocation ha;
4014 ASSERT(args.length() == 2);
4015
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004016 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00004017 // itself.
4018 //
4019 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00004020 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00004021 // global proxy object never has properties. This is the case
4022 // because the global proxy object forwards everything to its hidden
4023 // prototype including local lookups.
4024 //
4025 // Additionally, we need to make sure that we do not cache results
4026 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004027 if (args[0]->IsJSObject() &&
4028 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00004029 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004030 args[1]->IsString()) {
4031 JSObject* receiver = JSObject::cast(args[0]);
4032 String* key = String::cast(args[1]);
4033 if (receiver->HasFastProperties()) {
4034 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004035 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004036 int offset = KeyedLookupCache::Lookup(receiver_map, key);
4037 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004038 Object* value = receiver->FastPropertyAt(offset);
4039 return value->IsTheHole() ? Heap::undefined_value() : value;
4040 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004041 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004042 LookupResult result;
4043 receiver->LocalLookup(key, &result);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00004044 if (result.IsProperty() && result.type() == FIELD) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004045 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004046 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004047 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004048 }
4049 } else {
4050 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00004051 StringDictionary* dictionary = receiver->property_dictionary();
4052 int entry = dictionary->FindEntry(key);
4053 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004054 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004055 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004056 if (!receiver->IsGlobalObject()) return value;
4057 value = JSGlobalPropertyCell::cast(value)->value();
4058 if (!value->IsTheHole()) return value;
4059 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004060 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00004061 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004062 } else if (args[0]->IsString() && args[1]->IsSmi()) {
4063 // Fast case for string indexing using [] with a smi index.
4064 HandleScope scope;
4065 Handle<String> str = args.at<String>(0);
4066 int index = Smi::cast(args[1])->value();
4067 Handle<Object> result = GetCharAt(str, index);
4068 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00004069 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004070
4071 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00004072 return Runtime::GetObjectProperty(args.at<Object>(0),
4073 args.at<Object>(1));
4074}
4075
4076
ager@chromium.org5c838252010-02-19 08:53:10 +00004077static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
4078 ASSERT(args.length() == 5);
4079 HandleScope scope;
4080 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4081 CONVERT_CHECKED(String, name, args[1]);
4082 CONVERT_CHECKED(Smi, flag_setter, args[2]);
4083 CONVERT_CHECKED(JSFunction, fun, args[3]);
4084 CONVERT_CHECKED(Smi, flag_attr, args[4]);
4085 int unchecked = flag_attr->value();
4086 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4087 RUNTIME_ASSERT(!obj->IsNull());
4088 LookupResult result;
4089 obj->LocalLookupRealNamedProperty(name, &result);
4090
4091 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
4092 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
4093 // delete it to avoid running into trouble in DefineAccessor, which
4094 // handles this incorrectly if the property is readonly (does nothing)
4095 if (result.IsProperty() &&
4096 (result.type() == FIELD || result.type() == NORMAL
4097 || result.type() == CONSTANT_FUNCTION)) {
4098 obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
4099 }
4100 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
4101}
4102
4103static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
4104 ASSERT(args.length() == 4);
4105 HandleScope scope;
4106 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
4107 CONVERT_ARG_CHECKED(String, name, 1);
4108 Handle<Object> obj_value = args.at<Object>(2);
4109
4110 CONVERT_CHECKED(Smi, flag, args[3]);
4111 int unchecked = flag->value();
4112 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4113
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004114 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
4115
4116 // Check if this is an element.
4117 uint32_t index;
4118 bool is_element = name->AsArrayIndex(&index);
4119
4120 // Special case for elements if any of the flags are true.
4121 // If elements are in fast case we always implicitly assume that:
4122 // DONT_DELETE: false, DONT_ENUM: false, READ_ONLY: false.
4123 if (((unchecked & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) &&
4124 is_element) {
4125 // Normalize the elements to enable attributes on the property.
4126 js_object->NormalizeElements();
4127 NumberDictionary* dictionary = js_object->element_dictionary();
4128 // Make sure that we never go back to fast case.
4129 dictionary->set_requires_slow_elements();
4130 PropertyDetails details = PropertyDetails(attr, NORMAL);
4131 dictionary->Set(index, *obj_value, details);
4132 }
4133
ager@chromium.org5c838252010-02-19 08:53:10 +00004134 LookupResult result;
4135 js_object->LocalLookupRealNamedProperty(*name, &result);
4136
ager@chromium.org5c838252010-02-19 08:53:10 +00004137 // Take special care when attributes are different and there is already
4138 // a property. For simplicity we normalize the property which enables us
4139 // to not worry about changing the instance_descriptor and creating a new
4140 // map. The current version of SetObjectProperty does not handle attributes
4141 // correctly in the case where a property is a field and is reset with
4142 // new attributes.
4143 if (result.IsProperty() && attr != result.GetAttributes()) {
4144 // New attributes - normalize to avoid writing to instance descriptor
4145 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
4146 // Use IgnoreAttributes version since a readonly property may be
4147 // overridden and SetProperty does not allow this.
4148 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
4149 *obj_value,
4150 attr);
4151 }
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004152
ager@chromium.org5c838252010-02-19 08:53:10 +00004153 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
4154}
4155
4156
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004157Object* Runtime::SetObjectProperty(Handle<Object> object,
4158 Handle<Object> key,
4159 Handle<Object> value,
4160 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004161 HandleScope scope;
4162
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004163 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004164 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004165 Handle<Object> error =
4166 Factory::NewTypeError("non_object_property_store",
4167 HandleVector(args, 2));
4168 return Top::Throw(*error);
4169 }
4170
4171 // If the object isn't a JavaScript object, we ignore the store.
4172 if (!object->IsJSObject()) return *value;
4173
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004174 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
4175
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004176 // Check if the given key is an array index.
4177 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004178 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004179 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
4180 // of a string using [] notation. We need to support this too in
4181 // JavaScript.
4182 // In the case of a String object we just need to redirect the assignment to
4183 // the underlying string if the index is in range. Since the underlying
4184 // string does nothing with the assignment then we can ignore such
4185 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004186 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004187 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004188 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004189
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004190 Handle<Object> result = SetElement(js_object, index, value);
4191 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004192 return *value;
4193 }
4194
4195 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004196 Handle<Object> result;
4197 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004198 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004199 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004200 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004201 key_string->TryFlatten();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004202 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004203 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004204 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004205 return *value;
4206 }
4207
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004208 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004209 bool has_pending_exception = false;
4210 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4211 if (has_pending_exception) return Failure::Exception();
4212 Handle<String> name = Handle<String>::cast(converted);
4213
4214 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004215 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004216 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004217 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004218 }
4219}
4220
4221
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004222Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
4223 Handle<Object> key,
4224 Handle<Object> value,
4225 PropertyAttributes attr) {
4226 HandleScope scope;
4227
4228 // Check if the given key is an array index.
4229 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004230 if (key->ToArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004231 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
4232 // of a string using [] notation. We need to support this too in
4233 // JavaScript.
4234 // In the case of a String object we just need to redirect the assignment to
4235 // the underlying string if the index is in range. Since the underlying
4236 // string does nothing with the assignment then we can ignore such
4237 // assignments.
4238 if (js_object->IsStringObjectWithCharacterAt(index)) {
4239 return *value;
4240 }
4241
4242 return js_object->SetElement(index, *value);
4243 }
4244
4245 if (key->IsString()) {
4246 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004247 return js_object->SetElement(index, *value);
4248 } else {
4249 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004250 key_string->TryFlatten();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004251 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
4252 *value,
4253 attr);
4254 }
4255 }
4256
4257 // Call-back into JavaScript to convert the key to a string.
4258 bool has_pending_exception = false;
4259 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4260 if (has_pending_exception) return Failure::Exception();
4261 Handle<String> name = Handle<String>::cast(converted);
4262
4263 if (name->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004264 return js_object->SetElement(index, *value);
4265 } else {
4266 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
4267 }
4268}
4269
4270
ager@chromium.orge2902be2009-06-08 12:21:35 +00004271Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
4272 Handle<Object> key) {
4273 HandleScope scope;
4274
4275 // Check if the given key is an array index.
4276 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004277 if (key->ToArrayIndex(&index)) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00004278 // In Firefox/SpiderMonkey, Safari and Opera you can access the
4279 // characters of a string using [] notation. In the case of a
4280 // String object we just need to redirect the deletion to the
4281 // underlying string if the index is in range. Since the
4282 // underlying string does nothing with the deletion, we can ignore
4283 // such deletions.
4284 if (js_object->IsStringObjectWithCharacterAt(index)) {
4285 return Heap::true_value();
4286 }
4287
4288 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
4289 }
4290
4291 Handle<String> key_string;
4292 if (key->IsString()) {
4293 key_string = Handle<String>::cast(key);
4294 } else {
4295 // Call-back into JavaScript to convert the key to a string.
4296 bool has_pending_exception = false;
4297 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4298 if (has_pending_exception) return Failure::Exception();
4299 key_string = Handle<String>::cast(converted);
4300 }
4301
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004302 key_string->TryFlatten();
ager@chromium.orge2902be2009-06-08 12:21:35 +00004303 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
4304}
4305
4306
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004307static Object* Runtime_SetProperty(Arguments args) {
4308 NoHandleAllocation ha;
4309 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
4310
4311 Handle<Object> object = args.at<Object>(0);
4312 Handle<Object> key = args.at<Object>(1);
4313 Handle<Object> value = args.at<Object>(2);
4314
4315 // Compute attributes.
4316 PropertyAttributes attributes = NONE;
4317 if (args.length() == 4) {
4318 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004319 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004320 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004321 RUNTIME_ASSERT(
4322 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4323 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004324 }
4325 return Runtime::SetObjectProperty(object, key, value, attributes);
4326}
4327
4328
4329// Set a local property, even if it is READ_ONLY. If the property does not
4330// exist, it will be added with attributes NONE.
4331static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
4332 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004333 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004334 CONVERT_CHECKED(JSObject, object, args[0]);
4335 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004336 // Compute attributes.
4337 PropertyAttributes attributes = NONE;
4338 if (args.length() == 4) {
4339 CONVERT_CHECKED(Smi, value_obj, args[3]);
4340 int unchecked_value = value_obj->value();
4341 // Only attribute bits should be set.
4342 RUNTIME_ASSERT(
4343 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4344 attributes = static_cast<PropertyAttributes>(unchecked_value);
4345 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004346
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004347 return object->
4348 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004349}
4350
4351
4352static Object* Runtime_DeleteProperty(Arguments args) {
4353 NoHandleAllocation ha;
4354 ASSERT(args.length() == 2);
4355
4356 CONVERT_CHECKED(JSObject, object, args[0]);
4357 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00004358 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004359}
4360
4361
ager@chromium.org9085a012009-05-11 19:22:57 +00004362static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
4363 Handle<String> key) {
4364 if (object->HasLocalProperty(*key)) return Heap::true_value();
4365 // Handle hidden prototypes. If there's a hidden prototype above this thing
4366 // then we have to check it for properties, because they are supposed to
4367 // look like they are on this object.
4368 Handle<Object> proto(object->GetPrototype());
4369 if (proto->IsJSObject() &&
4370 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
4371 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
4372 }
4373 return Heap::false_value();
4374}
4375
4376
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004377static Object* Runtime_HasLocalProperty(Arguments args) {
4378 NoHandleAllocation ha;
4379 ASSERT(args.length() == 2);
4380 CONVERT_CHECKED(String, key, args[1]);
4381
ager@chromium.org9085a012009-05-11 19:22:57 +00004382 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004383 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00004384 if (obj->IsJSObject()) {
4385 JSObject* object = JSObject::cast(obj);
4386 // Fast case - no interceptors.
4387 if (object->HasRealNamedProperty(key)) return Heap::true_value();
4388 // Slow case. Either it's not there or we have an interceptor. We should
4389 // have handles for this kind of deal.
4390 HandleScope scope;
4391 return HasLocalPropertyImplementation(Handle<JSObject>(object),
4392 Handle<String>(key));
4393 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004394 // Well, there is one exception: Handle [] on strings.
4395 uint32_t index;
4396 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00004397 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004398 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004399 return Heap::true_value();
4400 }
4401 }
4402 return Heap::false_value();
4403}
4404
4405
4406static Object* Runtime_HasProperty(Arguments args) {
4407 NoHandleAllocation na;
4408 ASSERT(args.length() == 2);
4409
4410 // Only JS objects can have properties.
4411 if (args[0]->IsJSObject()) {
4412 JSObject* object = JSObject::cast(args[0]);
4413 CONVERT_CHECKED(String, key, args[1]);
4414 if (object->HasProperty(key)) return Heap::true_value();
4415 }
4416 return Heap::false_value();
4417}
4418
4419
4420static Object* Runtime_HasElement(Arguments args) {
4421 NoHandleAllocation na;
4422 ASSERT(args.length() == 2);
4423
4424 // Only JS objects can have elements.
4425 if (args[0]->IsJSObject()) {
4426 JSObject* object = JSObject::cast(args[0]);
4427 CONVERT_CHECKED(Smi, index_obj, args[1]);
4428 uint32_t index = index_obj->value();
4429 if (object->HasElement(index)) return Heap::true_value();
4430 }
4431 return Heap::false_value();
4432}
4433
4434
4435static Object* Runtime_IsPropertyEnumerable(Arguments args) {
4436 NoHandleAllocation ha;
4437 ASSERT(args.length() == 2);
4438
4439 CONVERT_CHECKED(JSObject, object, args[0]);
4440 CONVERT_CHECKED(String, key, args[1]);
4441
4442 uint32_t index;
4443 if (key->AsArrayIndex(&index)) {
4444 return Heap::ToBoolean(object->HasElement(index));
4445 }
4446
ager@chromium.org870a0b62008-11-04 11:43:05 +00004447 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
4448 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004449}
4450
4451
4452static Object* Runtime_GetPropertyNames(Arguments args) {
4453 HandleScope scope;
4454 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004455 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004456 return *GetKeysFor(object);
4457}
4458
4459
4460// Returns either a FixedArray as Runtime_GetPropertyNames,
4461// or, if the given object has an enum cache that contains
4462// all enumerable properties of the object and its prototypes
4463// have none, the map of the object. This is used to speed up
4464// the check for deletions during a for-in.
4465static Object* Runtime_GetPropertyNamesFast(Arguments args) {
4466 ASSERT(args.length() == 1);
4467
4468 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4469
4470 if (raw_object->IsSimpleEnum()) return raw_object->map();
4471
4472 HandleScope scope;
4473 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004474 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
4475 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004476
4477 // Test again, since cache may have been built by preceding call.
4478 if (object->IsSimpleEnum()) return object->map();
4479
4480 return *content;
4481}
4482
4483
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004484// Find the length of the prototype chain that is to to handled as one. If a
4485// prototype object is hidden it is to be viewed as part of the the object it
4486// is prototype for.
4487static int LocalPrototypeChainLength(JSObject* obj) {
4488 int count = 1;
4489 Object* proto = obj->GetPrototype();
4490 while (proto->IsJSObject() &&
4491 JSObject::cast(proto)->map()->is_hidden_prototype()) {
4492 count++;
4493 proto = JSObject::cast(proto)->GetPrototype();
4494 }
4495 return count;
4496}
4497
4498
4499// Return the names of the local named properties.
4500// args[0]: object
4501static Object* Runtime_GetLocalPropertyNames(Arguments args) {
4502 HandleScope scope;
4503 ASSERT(args.length() == 1);
4504 if (!args[0]->IsJSObject()) {
4505 return Heap::undefined_value();
4506 }
4507 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4508
4509 // Skip the global proxy as it has no properties and always delegates to the
4510 // real global object.
4511 if (obj->IsJSGlobalProxy()) {
4512 // Only collect names if access is permitted.
4513 if (obj->IsAccessCheckNeeded() &&
4514 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
4515 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
4516 return *Factory::NewJSArray(0);
4517 }
4518 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
4519 }
4520
4521 // Find the number of objects making up this.
4522 int length = LocalPrototypeChainLength(*obj);
4523
4524 // Find the number of local properties for each of the objects.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004525 ScopedVector<int> local_property_count(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004526 int total_property_count = 0;
4527 Handle<JSObject> jsproto = obj;
4528 for (int i = 0; i < length; i++) {
4529 // Only collect names if access is permitted.
4530 if (jsproto->IsAccessCheckNeeded() &&
4531 !Top::MayNamedAccess(*jsproto,
4532 Heap::undefined_value(),
4533 v8::ACCESS_KEYS)) {
4534 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
4535 return *Factory::NewJSArray(0);
4536 }
4537 int n;
4538 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4539 local_property_count[i] = n;
4540 total_property_count += n;
4541 if (i < length - 1) {
4542 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4543 }
4544 }
4545
4546 // Allocate an array with storage for all the property names.
4547 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
4548
4549 // Get the property names.
4550 jsproto = obj;
4551 int proto_with_hidden_properties = 0;
4552 for (int i = 0; i < length; i++) {
4553 jsproto->GetLocalPropertyNames(*names,
4554 i == 0 ? 0 : local_property_count[i - 1]);
4555 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
4556 proto_with_hidden_properties++;
4557 }
4558 if (i < length - 1) {
4559 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4560 }
4561 }
4562
4563 // Filter out name of hidden propeties object.
4564 if (proto_with_hidden_properties > 0) {
4565 Handle<FixedArray> old_names = names;
4566 names = Factory::NewFixedArray(
4567 names->length() - proto_with_hidden_properties);
4568 int dest_pos = 0;
4569 for (int i = 0; i < total_property_count; i++) {
4570 Object* name = old_names->get(i);
4571 if (name == Heap::hidden_symbol()) {
4572 continue;
4573 }
4574 names->set(dest_pos++, name);
4575 }
4576 }
4577
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004578 return *Factory::NewJSArrayWithElements(names);
4579}
4580
4581
4582// Return the names of the local indexed properties.
4583// args[0]: object
4584static Object* Runtime_GetLocalElementNames(Arguments args) {
4585 HandleScope scope;
4586 ASSERT(args.length() == 1);
4587 if (!args[0]->IsJSObject()) {
4588 return Heap::undefined_value();
4589 }
4590 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4591
4592 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4593 Handle<FixedArray> names = Factory::NewFixedArray(n);
4594 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4595 return *Factory::NewJSArrayWithElements(names);
4596}
4597
4598
4599// Return information on whether an object has a named or indexed interceptor.
4600// args[0]: object
4601static Object* Runtime_GetInterceptorInfo(Arguments args) {
4602 HandleScope scope;
4603 ASSERT(args.length() == 1);
4604 if (!args[0]->IsJSObject()) {
4605 return Smi::FromInt(0);
4606 }
4607 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4608
4609 int result = 0;
4610 if (obj->HasNamedInterceptor()) result |= 2;
4611 if (obj->HasIndexedInterceptor()) result |= 1;
4612
4613 return Smi::FromInt(result);
4614}
4615
4616
4617// Return property names from named interceptor.
4618// args[0]: object
4619static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
4620 HandleScope scope;
4621 ASSERT(args.length() == 1);
4622 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4623
4624 if (obj->HasNamedInterceptor()) {
4625 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4626 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4627 }
4628 return Heap::undefined_value();
4629}
4630
4631
4632// Return element names from indexed interceptor.
4633// args[0]: object
4634static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
4635 HandleScope scope;
4636 ASSERT(args.length() == 1);
4637 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4638
4639 if (obj->HasIndexedInterceptor()) {
4640 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4641 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4642 }
4643 return Heap::undefined_value();
4644}
4645
4646
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004647static Object* Runtime_LocalKeys(Arguments args) {
4648 ASSERT_EQ(args.length(), 1);
4649 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4650 HandleScope scope;
4651 Handle<JSObject> object(raw_object);
4652 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
4653 LOCAL_ONLY);
4654 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
4655 // property array and since the result is mutable we have to create
4656 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004657 int length = contents->length();
4658 Handle<FixedArray> copy = Factory::NewFixedArray(length);
4659 for (int i = 0; i < length; i++) {
4660 Object* entry = contents->get(i);
4661 if (entry->IsString()) {
4662 copy->set(i, entry);
4663 } else {
4664 ASSERT(entry->IsNumber());
4665 HandleScope scope;
4666 Handle<Object> entry_handle(entry);
4667 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
4668 copy->set(i, *entry_str);
4669 }
4670 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004671 return *Factory::NewJSArrayWithElements(copy);
4672}
4673
4674
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004675static Object* Runtime_GetArgumentsProperty(Arguments args) {
4676 NoHandleAllocation ha;
4677 ASSERT(args.length() == 1);
4678
4679 // Compute the frame holding the arguments.
4680 JavaScriptFrameIterator it;
4681 it.AdvanceToArgumentsFrame();
4682 JavaScriptFrame* frame = it.frame();
4683
4684 // Get the actual number of provided arguments.
4685 const uint32_t n = frame->GetProvidedParametersCount();
4686
4687 // Try to convert the key to an index. If successful and within
4688 // index return the the argument from the frame.
4689 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004690 if (args[0]->ToArrayIndex(&index) && index < n) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004691 return frame->GetParameter(index);
4692 }
4693
4694 // Convert the key to a string.
4695 HandleScope scope;
4696 bool exception = false;
4697 Handle<Object> converted =
4698 Execution::ToString(args.at<Object>(0), &exception);
4699 if (exception) return Failure::Exception();
4700 Handle<String> key = Handle<String>::cast(converted);
4701
4702 // Try to convert the string key into an array index.
4703 if (key->AsArrayIndex(&index)) {
4704 if (index < n) {
4705 return frame->GetParameter(index);
4706 } else {
4707 return Top::initial_object_prototype()->GetElement(index);
4708 }
4709 }
4710
4711 // Handle special arguments properties.
4712 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
4713 if (key->Equals(Heap::callee_symbol())) return frame->function();
4714
4715 // Lookup in the initial Object.prototype object.
4716 return Top::initial_object_prototype()->GetProperty(*key);
4717}
4718
4719
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004720static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004721 HandleScope scope;
4722
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004723 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004724 Handle<Object> object = args.at<Object>(0);
4725 if (object->IsJSObject()) {
4726 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00004727 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
4728 js_object->TransformToFastProperties(0);
4729 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004730 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004731 return *object;
4732}
4733
4734
4735static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004736 HandleScope scope;
4737
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004738 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004739 Handle<Object> object = args.at<Object>(0);
4740 if (object->IsJSObject()) {
4741 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004742 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004743 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004744 return *object;
4745}
4746
4747
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004748static Object* Runtime_ToBool(Arguments args) {
4749 NoHandleAllocation ha;
4750 ASSERT(args.length() == 1);
4751
4752 return args[0]->ToBoolean();
4753}
4754
4755
4756// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
4757// Possible optimizations: put the type string into the oddballs.
4758static Object* Runtime_Typeof(Arguments args) {
4759 NoHandleAllocation ha;
4760
4761 Object* obj = args[0];
4762 if (obj->IsNumber()) return Heap::number_symbol();
4763 HeapObject* heap_obj = HeapObject::cast(obj);
4764
4765 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004766 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004767
4768 InstanceType instance_type = heap_obj->map()->instance_type();
4769 if (instance_type < FIRST_NONSTRING_TYPE) {
4770 return Heap::string_symbol();
4771 }
4772
4773 switch (instance_type) {
4774 case ODDBALL_TYPE:
4775 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
4776 return Heap::boolean_symbol();
4777 }
4778 if (heap_obj->IsNull()) {
4779 return Heap::object_symbol();
4780 }
4781 ASSERT(heap_obj->IsUndefined());
4782 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004783 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004784 return Heap::function_symbol();
4785 default:
4786 // For any kind of object not handled above, the spec rule for
4787 // host objects gives that it is okay to return "object"
4788 return Heap::object_symbol();
4789 }
4790}
4791
4792
lrn@chromium.org25156de2010-04-06 13:10:27 +00004793static bool AreDigits(const char*s, int from, int to) {
4794 for (int i = from; i < to; i++) {
4795 if (s[i] < '0' || s[i] > '9') return false;
4796 }
4797
4798 return true;
4799}
4800
4801
4802static int ParseDecimalInteger(const char*s, int from, int to) {
4803 ASSERT(to - from < 10); // Overflow is not possible.
4804 ASSERT(from < to);
4805 int d = s[from] - '0';
4806
4807 for (int i = from + 1; i < to; i++) {
4808 d = 10 * d + (s[i] - '0');
4809 }
4810
4811 return d;
4812}
4813
4814
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004815static Object* Runtime_StringToNumber(Arguments args) {
4816 NoHandleAllocation ha;
4817 ASSERT(args.length() == 1);
4818 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004819 subject->TryFlatten();
lrn@chromium.org25156de2010-04-06 13:10:27 +00004820
4821 // Fast case: short integer or some sorts of junk values.
4822 int len = subject->length();
4823 if (subject->IsSeqAsciiString()) {
4824 if (len == 0) return Smi::FromInt(0);
4825
4826 char const* data = SeqAsciiString::cast(subject)->GetChars();
4827 bool minus = (data[0] == '-');
4828 int start_pos = (minus ? 1 : 0);
4829
4830 if (start_pos == len) {
4831 return Heap::nan_value();
4832 } else if (data[start_pos] > '9') {
4833 // Fast check for a junk value. A valid string may start from a
4834 // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit or
4835 // the 'I' character ('Infinity'). All of that have codes not greater than
4836 // '9' except 'I'.
4837 if (data[start_pos] != 'I') {
4838 return Heap::nan_value();
4839 }
4840 } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
4841 // The maximal/minimal smi has 10 digits. If the string has less digits we
4842 // know it will fit into the smi-data type.
4843 int d = ParseDecimalInteger(data, start_pos, len);
4844 if (minus) {
4845 if (d == 0) return Heap::minus_zero_value();
4846 d = -d;
4847 }
4848 return Smi::FromInt(d);
4849 }
4850 }
4851
4852 // Slower case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004853 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
4854}
4855
4856
4857static Object* Runtime_StringFromCharCodeArray(Arguments args) {
4858 NoHandleAllocation ha;
4859 ASSERT(args.length() == 1);
4860
4861 CONVERT_CHECKED(JSArray, codes, args[0]);
4862 int length = Smi::cast(codes->length())->value();
4863
4864 // Check if the string can be ASCII.
4865 int i;
4866 for (i = 0; i < length; i++) {
4867 Object* element = codes->GetElement(i);
4868 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
4869 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
4870 break;
4871 }
4872
4873 Object* object = NULL;
4874 if (i == length) { // The string is ASCII.
4875 object = Heap::AllocateRawAsciiString(length);
4876 } else { // The string is not ASCII.
4877 object = Heap::AllocateRawTwoByteString(length);
4878 }
4879
4880 if (object->IsFailure()) return object;
4881 String* result = String::cast(object);
4882 for (int i = 0; i < length; i++) {
4883 Object* element = codes->GetElement(i);
4884 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004885 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004886 }
4887 return result;
4888}
4889
4890
4891// kNotEscaped is generated by the following:
4892//
4893// #!/bin/perl
4894// for (my $i = 0; $i < 256; $i++) {
4895// print "\n" if $i % 16 == 0;
4896// my $c = chr($i);
4897// my $escaped = 1;
4898// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
4899// print $escaped ? "0, " : "1, ";
4900// }
4901
4902
4903static bool IsNotEscaped(uint16_t character) {
4904 // Only for 8 bit characters, the rest are always escaped (in a different way)
4905 ASSERT(character < 256);
4906 static const char kNotEscaped[256] = {
4907 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4908 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4909 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
4910 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
4911 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4912 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
4913 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4914 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
4915 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4916 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4917 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4918 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4919 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4920 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4921 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4922 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4923 };
4924 return kNotEscaped[character] != 0;
4925}
4926
4927
4928static Object* Runtime_URIEscape(Arguments args) {
4929 const char hex_chars[] = "0123456789ABCDEF";
4930 NoHandleAllocation ha;
4931 ASSERT(args.length() == 1);
4932 CONVERT_CHECKED(String, source, args[0]);
4933
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004934 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004935
4936 int escaped_length = 0;
4937 int length = source->length();
4938 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004939 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004940 buffer->Reset(source);
4941 while (buffer->has_more()) {
4942 uint16_t character = buffer->GetNext();
4943 if (character >= 256) {
4944 escaped_length += 6;
4945 } else if (IsNotEscaped(character)) {
4946 escaped_length++;
4947 } else {
4948 escaped_length += 3;
4949 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004950 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004951 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004952 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004953 Top::context()->mark_out_of_memory();
4954 return Failure::OutOfMemoryException();
4955 }
4956 }
4957 }
4958 // No length change implies no change. Return original string if no change.
4959 if (escaped_length == length) {
4960 return source;
4961 }
4962 Object* o = Heap::AllocateRawAsciiString(escaped_length);
4963 if (o->IsFailure()) return o;
4964 String* destination = String::cast(o);
4965 int dest_position = 0;
4966
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004967 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004968 buffer->Rewind();
4969 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00004970 uint16_t chr = buffer->GetNext();
4971 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004972 destination->Set(dest_position, '%');
4973 destination->Set(dest_position+1, 'u');
4974 destination->Set(dest_position+2, hex_chars[chr >> 12]);
4975 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
4976 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
4977 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004978 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00004979 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004980 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004981 dest_position++;
4982 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004983 destination->Set(dest_position, '%');
4984 destination->Set(dest_position+1, hex_chars[chr >> 4]);
4985 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004986 dest_position += 3;
4987 }
4988 }
4989 return destination;
4990}
4991
4992
4993static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
4994 static const signed char kHexValue['g'] = {
4995 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4996 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4997 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4998 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
4999 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
5000 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
5001 -1, 10, 11, 12, 13, 14, 15 };
5002
5003 if (character1 > 'f') return -1;
5004 int hi = kHexValue[character1];
5005 if (hi == -1) return -1;
5006 if (character2 > 'f') return -1;
5007 int lo = kHexValue[character2];
5008 if (lo == -1) return -1;
5009 return (hi << 4) + lo;
5010}
5011
5012
ager@chromium.org870a0b62008-11-04 11:43:05 +00005013static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00005014 int i,
5015 int length,
5016 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005017 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00005018 int32_t hi = 0;
5019 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005020 if (character == '%' &&
5021 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005022 source->Get(i + 1) == 'u' &&
5023 (hi = TwoDigitHex(source->Get(i + 2),
5024 source->Get(i + 3))) != -1 &&
5025 (lo = TwoDigitHex(source->Get(i + 4),
5026 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005027 *step = 6;
5028 return (hi << 8) + lo;
5029 } else if (character == '%' &&
5030 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005031 (lo = TwoDigitHex(source->Get(i + 1),
5032 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005033 *step = 3;
5034 return lo;
5035 } else {
5036 *step = 1;
5037 return character;
5038 }
5039}
5040
5041
5042static Object* Runtime_URIUnescape(Arguments args) {
5043 NoHandleAllocation ha;
5044 ASSERT(args.length() == 1);
5045 CONVERT_CHECKED(String, source, args[0]);
5046
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005047 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005048
5049 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005050 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005051
5052 int unescaped_length = 0;
5053 for (int i = 0; i < length; unescaped_length++) {
5054 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005055 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005056 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005057 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005058 i += step;
5059 }
5060
5061 // No length change implies no change. Return original string if no change.
5062 if (unescaped_length == length)
5063 return source;
5064
5065 Object* o = ascii ?
5066 Heap::AllocateRawAsciiString(unescaped_length) :
5067 Heap::AllocateRawTwoByteString(unescaped_length);
5068 if (o->IsFailure()) return o;
5069 String* destination = String::cast(o);
5070
5071 int dest_position = 0;
5072 for (int i = 0; i < length; dest_position++) {
5073 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005074 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005075 i += step;
5076 }
5077 return destination;
5078}
5079
5080
5081static Object* Runtime_StringParseInt(Arguments args) {
5082 NoHandleAllocation ha;
5083
5084 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005085 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005086
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005087 s->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005088
lrn@chromium.org25156de2010-04-06 13:10:27 +00005089 RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
5090 double value = StringToInt(s, radix);
5091 return Heap::NumberFromDouble(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005092 return Heap::nan_value();
5093}
5094
5095
5096static Object* Runtime_StringParseFloat(Arguments args) {
5097 NoHandleAllocation ha;
5098 CONVERT_CHECKED(String, str, args[0]);
5099
5100 // ECMA-262 section 15.1.2.3, empty string is NaN
5101 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
5102
5103 // Create a number object from the value.
5104 return Heap::NumberFromDouble(value);
5105}
5106
5107
5108static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
5109static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
5110
5111
5112template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005113static Object* ConvertCaseHelper(String* s,
5114 int length,
5115 int input_string_length,
5116 unibrow::Mapping<Converter, 128>* mapping) {
5117 // We try this twice, once with the assumption that the result is no longer
5118 // than the input and, if that assumption breaks, again with the exact
5119 // length. This may not be pretty, but it is nicer than what was here before
5120 // and I hereby claim my vaffel-is.
5121 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005122 // Allocate the resulting string.
5123 //
5124 // NOTE: This assumes that the upper/lower case of an ascii
5125 // character is also ascii. This is currently the case, but it
5126 // might break in the future if we implement more context and locale
5127 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00005128 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005129 ? Heap::AllocateRawAsciiString(length)
5130 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005131 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005132 String* result = String::cast(o);
5133 bool has_changed_character = false;
5134
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005135 // Convert all characters to upper case, assuming that they will fit
5136 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005137 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005138 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005139 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005140 // We can assume that the string is not empty
5141 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005142 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00005143 bool has_next = buffer->has_more();
5144 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005145 int char_length = mapping->get(current, next, chars);
5146 if (char_length == 0) {
5147 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005148 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005149 i++;
5150 } else if (char_length == 1) {
5151 // Common case: converting the letter resulted in one character.
5152 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005153 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005154 has_changed_character = true;
5155 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005156 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005157 // We've assumed that the result would be as long as the
5158 // input but here is a character that converts to several
5159 // characters. No matter, we calculate the exact length
5160 // of the result and try the whole thing again.
5161 //
5162 // Note that this leaves room for optimization. We could just
5163 // memcpy what we already have to the result string. Also,
5164 // the result string is the last object allocated we could
5165 // "realloc" it and probably, in the vast majority of cases,
5166 // extend the existing string to be able to hold the full
5167 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00005168 int next_length = 0;
5169 if (has_next) {
5170 next_length = mapping->get(next, 0, chars);
5171 if (next_length == 0) next_length = 1;
5172 }
5173 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005174 while (buffer->has_more()) {
5175 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00005176 // NOTE: we use 0 as the next character here because, while
5177 // the next character may affect what a character converts to,
5178 // it does not in any case affect the length of what it convert
5179 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005180 int char_length = mapping->get(current, 0, chars);
5181 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00005182 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005183 if (current_length > Smi::kMaxValue) {
5184 Top::context()->mark_out_of_memory();
5185 return Failure::OutOfMemoryException();
5186 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005187 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005188 // Try again with the real length.
5189 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005190 } else {
5191 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005192 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005193 i++;
5194 }
5195 has_changed_character = true;
5196 }
5197 current = next;
5198 }
5199 if (has_changed_character) {
5200 return result;
5201 } else {
5202 // If we didn't actually change anything in doing the conversion
5203 // we simple return the result and let the converted string
5204 // become garbage; there is no reason to keep two identical strings
5205 // alive.
5206 return s;
5207 }
5208}
5209
5210
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005211namespace {
5212
5213struct ToLowerTraits {
5214 typedef unibrow::ToLowercase UnibrowConverter;
5215
5216 static bool ConvertAscii(char* dst, char* src, int length) {
5217 bool changed = false;
5218 for (int i = 0; i < length; ++i) {
5219 char c = src[i];
5220 if ('A' <= c && c <= 'Z') {
5221 c += ('a' - 'A');
5222 changed = true;
5223 }
5224 dst[i] = c;
5225 }
5226 return changed;
5227 }
5228};
5229
5230
5231struct ToUpperTraits {
5232 typedef unibrow::ToUppercase UnibrowConverter;
5233
5234 static bool ConvertAscii(char* dst, char* src, int length) {
5235 bool changed = false;
5236 for (int i = 0; i < length; ++i) {
5237 char c = src[i];
5238 if ('a' <= c && c <= 'z') {
5239 c -= ('a' - 'A');
5240 changed = true;
5241 }
5242 dst[i] = c;
5243 }
5244 return changed;
5245 }
5246};
5247
5248} // namespace
5249
5250
5251template <typename ConvertTraits>
5252static Object* ConvertCase(
5253 Arguments args,
5254 unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005255 NoHandleAllocation ha;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005256 CONVERT_CHECKED(String, s, args[0]);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005257 s = s->TryFlattenGetString();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005258
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005259 const int length = s->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005260 // Assume that the string is not empty; we need this assumption later
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005261 if (length == 0) return s;
5262
5263 // Simpler handling of ascii strings.
5264 //
5265 // NOTE: This assumes that the upper/lower case of an ascii
5266 // character is also ascii. This is currently the case, but it
5267 // might break in the future if we implement more context and locale
5268 // dependent upper/lower conversions.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005269 if (s->IsSeqAsciiString()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005270 Object* o = Heap::AllocateRawAsciiString(length);
5271 if (o->IsFailure()) return o;
5272 SeqAsciiString* result = SeqAsciiString::cast(o);
5273 bool has_changed_character = ConvertTraits::ConvertAscii(
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005274 result->GetChars(), SeqAsciiString::cast(s)->GetChars(), length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005275 return has_changed_character ? result : s;
5276 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005277
5278 Object* answer = ConvertCaseHelper(s, length, length, mapping);
5279 if (answer->IsSmi()) {
5280 // Retry with correct length.
5281 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
5282 }
5283 return answer; // This may be a failure.
5284}
5285
5286
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005287static Object* Runtime_StringToLowerCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005288 return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005289}
5290
5291
5292static Object* Runtime_StringToUpperCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005293 return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005294}
5295
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005296
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005297static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
5298 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
5299}
5300
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005301
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005302static Object* Runtime_StringTrim(Arguments args) {
5303 NoHandleAllocation ha;
5304 ASSERT(args.length() == 3);
5305
5306 CONVERT_CHECKED(String, s, args[0]);
5307 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
5308 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
5309
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005310 s->TryFlatten();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005311 int length = s->length();
5312
5313 int left = 0;
5314 if (trimLeft) {
5315 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
5316 left++;
5317 }
5318 }
5319
5320 int right = length;
5321 if (trimRight) {
5322 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
5323 right--;
5324 }
5325 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005326 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005327}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005328
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005329
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005330template <typename schar, typename pchar>
5331void FindStringIndices(Vector<const schar> subject,
5332 Vector<const pchar> pattern,
5333 ZoneList<int>* indices,
5334 unsigned int limit) {
5335 ASSERT(limit > 0);
5336 // Collect indices of pattern in subject, and the end-of-string index.
5337 // Stop after finding at most limit values.
5338 StringSearchStrategy strategy =
5339 InitializeStringSearch(pattern, sizeof(schar) == 1);
5340 switch (strategy) {
5341 case SEARCH_FAIL: return;
5342 case SEARCH_SHORT: {
5343 int pattern_length = pattern.length();
5344 int index = 0;
5345 while (limit > 0) {
5346 index = SimpleIndexOf(subject, pattern, index);
5347 if (index < 0) return;
5348 indices->Add(index);
5349 index += pattern_length;
5350 limit--;
5351 }
5352 return;
5353 }
5354 case SEARCH_LONG: {
5355 int pattern_length = pattern.length();
5356 int index = 0;
5357 while (limit > 0) {
5358 index = ComplexIndexOf(subject, pattern, index);
5359 if (index < 0) return;
5360 indices->Add(index);
5361 index += pattern_length;
5362 limit--;
5363 }
5364 return;
5365 }
5366 default:
5367 UNREACHABLE();
5368 return;
5369 }
5370}
5371
5372template <typename schar>
5373inline void FindCharIndices(Vector<const schar> subject,
5374 const schar pattern_char,
5375 ZoneList<int>* indices,
5376 unsigned int limit) {
5377 // Collect indices of pattern_char in subject, and the end-of-string index.
5378 // Stop after finding at most limit values.
5379 int index = 0;
5380 while (limit > 0) {
5381 index = SingleCharIndexOf(subject, pattern_char, index);
5382 if (index < 0) return;
5383 indices->Add(index);
5384 index++;
5385 limit--;
5386 }
5387}
5388
5389
5390static Object* Runtime_StringSplit(Arguments args) {
5391 ASSERT(args.length() == 3);
5392 HandleScope handle_scope;
5393 CONVERT_ARG_CHECKED(String, subject, 0);
5394 CONVERT_ARG_CHECKED(String, pattern, 1);
5395 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
5396
5397 int subject_length = subject->length();
5398 int pattern_length = pattern->length();
5399 RUNTIME_ASSERT(pattern_length > 0);
5400
5401 // The limit can be very large (0xffffffffu), but since the pattern
5402 // isn't empty, we can never create more parts than ~half the length
5403 // of the subject.
5404
5405 if (!subject->IsFlat()) FlattenString(subject);
5406
5407 static const int kMaxInitialListCapacity = 16;
5408
5409 ZoneScope scope(DELETE_ON_EXIT);
5410
5411 // Find (up to limit) indices of separator and end-of-string in subject
5412 int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
5413 ZoneList<int> indices(initial_capacity);
5414 if (pattern_length == 1) {
5415 // Special case, go directly to fast single-character split.
5416 AssertNoAllocation nogc;
5417 uc16 pattern_char = pattern->Get(0);
5418 if (subject->IsTwoByteRepresentation()) {
5419 FindCharIndices(subject->ToUC16Vector(), pattern_char,
5420 &indices,
5421 limit);
5422 } else if (pattern_char <= String::kMaxAsciiCharCode) {
5423 FindCharIndices(subject->ToAsciiVector(),
5424 static_cast<char>(pattern_char),
5425 &indices,
5426 limit);
5427 }
5428 } else {
5429 if (!pattern->IsFlat()) FlattenString(pattern);
5430 AssertNoAllocation nogc;
5431 if (subject->IsAsciiRepresentation()) {
5432 Vector<const char> subject_vector = subject->ToAsciiVector();
5433 if (pattern->IsAsciiRepresentation()) {
5434 FindStringIndices(subject_vector,
5435 pattern->ToAsciiVector(),
5436 &indices,
5437 limit);
5438 } else {
5439 FindStringIndices(subject_vector,
5440 pattern->ToUC16Vector(),
5441 &indices,
5442 limit);
5443 }
5444 } else {
5445 Vector<const uc16> subject_vector = subject->ToUC16Vector();
5446 if (pattern->IsAsciiRepresentation()) {
5447 FindStringIndices(subject_vector,
5448 pattern->ToAsciiVector(),
5449 &indices,
5450 limit);
5451 } else {
5452 FindStringIndices(subject_vector,
5453 pattern->ToUC16Vector(),
5454 &indices,
5455 limit);
5456 }
5457 }
5458 }
5459 if (static_cast<uint32_t>(indices.length()) < limit) {
5460 indices.Add(subject_length);
5461 }
5462 // The list indices now contains the end of each part to create.
5463
5464
5465 // Create JSArray of substrings separated by separator.
5466 int part_count = indices.length();
5467
5468 Handle<JSArray> result = Factory::NewJSArray(part_count);
5469 result->set_length(Smi::FromInt(part_count));
5470
5471 ASSERT(result->HasFastElements());
5472
5473 if (part_count == 1 && indices.at(0) == subject_length) {
5474 FixedArray::cast(result->elements())->set(0, *subject);
5475 return *result;
5476 }
5477
5478 Handle<FixedArray> elements(FixedArray::cast(result->elements()));
5479 int part_start = 0;
5480 for (int i = 0; i < part_count; i++) {
5481 HandleScope local_loop_handle;
5482 int part_end = indices.at(i);
5483 Handle<String> substring =
5484 Factory::NewSubString(subject, part_start, part_end);
5485 elements->set(i, *substring);
5486 part_start = part_end + pattern_length;
5487 }
5488
5489 return *result;
5490}
5491
5492
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005493// Copies ascii characters to the given fixed array looking up
5494// one-char strings in the cache. Gives up on the first char that is
5495// not in the cache and fills the remainder with smi zeros. Returns
5496// the length of the successfully copied prefix.
5497static int CopyCachedAsciiCharsToArray(const char* chars,
5498 FixedArray* elements,
5499 int length) {
5500 AssertNoAllocation nogc;
5501 FixedArray* ascii_cache = Heap::single_character_string_cache();
5502 Object* undefined = Heap::undefined_value();
5503 int i;
5504 for (i = 0; i < length; ++i) {
5505 Object* value = ascii_cache->get(chars[i]);
5506 if (value == undefined) break;
5507 ASSERT(!Heap::InNewSpace(value));
5508 elements->set(i, value, SKIP_WRITE_BARRIER);
5509 }
5510 if (i < length) {
5511 ASSERT(Smi::FromInt(0) == 0);
5512 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
5513 }
5514#ifdef DEBUG
5515 for (int j = 0; j < length; ++j) {
5516 Object* element = elements->get(j);
5517 ASSERT(element == Smi::FromInt(0) ||
5518 (element->IsString() && String::cast(element)->LooksValid()));
5519 }
5520#endif
5521 return i;
5522}
5523
5524
5525// Converts a String to JSArray.
5526// For example, "foo" => ["f", "o", "o"].
5527static Object* Runtime_StringToArray(Arguments args) {
5528 HandleScope scope;
5529 ASSERT(args.length() == 1);
5530 CONVERT_ARG_CHECKED(String, s, 0);
5531
5532 s->TryFlatten();
5533 const int length = s->length();
5534
5535 Handle<FixedArray> elements;
5536 if (s->IsFlat() && s->IsAsciiRepresentation()) {
5537 Object* obj = Heap::AllocateUninitializedFixedArray(length);
5538 if (obj->IsFailure()) return obj;
5539 elements = Handle<FixedArray>(FixedArray::cast(obj));
5540
5541 Vector<const char> chars = s->ToAsciiVector();
5542 // Note, this will initialize all elements (not only the prefix)
5543 // to prevent GC from seeing partially initialized array.
5544 int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
5545 *elements,
5546 length);
5547
5548 for (int i = num_copied_from_cache; i < length; ++i) {
5549 elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
5550 }
5551 } else {
5552 elements = Factory::NewFixedArray(length);
5553 for (int i = 0; i < length; ++i) {
5554 elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
5555 }
5556 }
5557
5558#ifdef DEBUG
5559 for (int i = 0; i < length; ++i) {
5560 ASSERT(String::cast(elements->get(i))->length() == 1);
5561 }
5562#endif
5563
5564 return *Factory::NewJSArrayWithElements(elements);
5565}
5566
5567
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00005568bool Runtime::IsUpperCaseChar(uint16_t ch) {
5569 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
5570 int char_length = to_upper_mapping.get(ch, 0, chars);
5571 return char_length == 0;
5572}
5573
5574
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005575static Object* Runtime_NumberToString(Arguments args) {
5576 NoHandleAllocation ha;
5577 ASSERT(args.length() == 1);
5578
5579 Object* number = args[0];
5580 RUNTIME_ASSERT(number->IsNumber());
5581
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005582 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005583}
5584
5585
ager@chromium.org357bf652010-04-12 11:30:10 +00005586static Object* Runtime_NumberToStringSkipCache(Arguments args) {
5587 NoHandleAllocation ha;
5588 ASSERT(args.length() == 1);
5589
5590 Object* number = args[0];
5591 RUNTIME_ASSERT(number->IsNumber());
5592
5593 return Heap::NumberToString(number, false);
5594}
5595
5596
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005597static Object* Runtime_NumberToInteger(Arguments args) {
5598 NoHandleAllocation ha;
5599 ASSERT(args.length() == 1);
5600
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005601 CONVERT_DOUBLE_CHECKED(number, args[0]);
5602
5603 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5604 if (number > 0 && number <= Smi::kMaxValue) {
5605 return Smi::FromInt(static_cast<int>(number));
5606 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005607 return Heap::NumberFromDouble(DoubleToInteger(number));
5608}
5609
5610
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005611static Object* Runtime_NumberToIntegerMapMinusZero(Arguments args) {
5612 NoHandleAllocation ha;
5613 ASSERT(args.length() == 1);
5614
5615 CONVERT_DOUBLE_CHECKED(number, args[0]);
5616
5617 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5618 if (number > 0 && number <= Smi::kMaxValue) {
5619 return Smi::FromInt(static_cast<int>(number));
5620 }
5621
5622 double double_value = DoubleToInteger(number);
5623 // Map both -0 and +0 to +0.
5624 if (double_value == 0) double_value = 0;
5625
5626 return Heap::NumberFromDouble(double_value);
5627}
5628
5629
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005630static Object* Runtime_NumberToJSUint32(Arguments args) {
5631 NoHandleAllocation ha;
5632 ASSERT(args.length() == 1);
5633
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005634 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005635 return Heap::NumberFromUint32(number);
5636}
5637
5638
5639static Object* Runtime_NumberToJSInt32(Arguments args) {
5640 NoHandleAllocation ha;
5641 ASSERT(args.length() == 1);
5642
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005643 CONVERT_DOUBLE_CHECKED(number, args[0]);
5644
5645 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5646 if (number > 0 && number <= Smi::kMaxValue) {
5647 return Smi::FromInt(static_cast<int>(number));
5648 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005649 return Heap::NumberFromInt32(DoubleToInt32(number));
5650}
5651
5652
ager@chromium.org870a0b62008-11-04 11:43:05 +00005653// Converts a Number to a Smi, if possible. Returns NaN if the number is not
5654// a small integer.
5655static Object* Runtime_NumberToSmi(Arguments args) {
5656 NoHandleAllocation ha;
5657 ASSERT(args.length() == 1);
5658
5659 Object* obj = args[0];
5660 if (obj->IsSmi()) {
5661 return obj;
5662 }
5663 if (obj->IsHeapNumber()) {
5664 double value = HeapNumber::cast(obj)->value();
5665 int int_value = FastD2I(value);
5666 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
5667 return Smi::FromInt(int_value);
5668 }
5669 }
5670 return Heap::nan_value();
5671}
5672
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005673
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005674static Object* Runtime_NumberAdd(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]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005680 return Heap::NumberFromDouble(x + y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005681}
5682
5683
5684static Object* Runtime_NumberSub(Arguments args) {
5685 NoHandleAllocation ha;
5686 ASSERT(args.length() == 2);
5687
5688 CONVERT_DOUBLE_CHECKED(x, args[0]);
5689 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005690 return Heap::NumberFromDouble(x - y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005691}
5692
5693
5694static Object* Runtime_NumberMul(Arguments args) {
5695 NoHandleAllocation ha;
5696 ASSERT(args.length() == 2);
5697
5698 CONVERT_DOUBLE_CHECKED(x, args[0]);
5699 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005700 return Heap::NumberFromDouble(x * y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005701}
5702
5703
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005704static Object* Runtime_NumberUnaryMinus(Arguments args) {
5705 NoHandleAllocation ha;
5706 ASSERT(args.length() == 1);
5707
5708 CONVERT_DOUBLE_CHECKED(x, args[0]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005709 return Heap::NumberFromDouble(-x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005710}
5711
5712
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00005713static Object* Runtime_NumberAlloc(Arguments args) {
5714 NoHandleAllocation ha;
5715 ASSERT(args.length() == 0);
5716
5717 return Heap::NumberFromDouble(9876543210.0);
5718}
5719
5720
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005721static Object* Runtime_NumberDiv(Arguments args) {
5722 NoHandleAllocation ha;
5723 ASSERT(args.length() == 2);
5724
5725 CONVERT_DOUBLE_CHECKED(x, args[0]);
5726 CONVERT_DOUBLE_CHECKED(y, args[1]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005727 return Heap::NumberFromDouble(x / y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005728}
5729
5730
5731static Object* Runtime_NumberMod(Arguments args) {
5732 NoHandleAllocation ha;
5733 ASSERT(args.length() == 2);
5734
5735 CONVERT_DOUBLE_CHECKED(x, args[0]);
5736 CONVERT_DOUBLE_CHECKED(y, args[1]);
5737
ager@chromium.org3811b432009-10-28 14:53:37 +00005738 x = modulo(x, y);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005739 // NumberFromDouble may return a Smi instead of a Number object
5740 return Heap::NumberFromDouble(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005741}
5742
5743
5744static Object* Runtime_StringAdd(Arguments args) {
5745 NoHandleAllocation ha;
5746 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005747 CONVERT_CHECKED(String, str1, args[0]);
5748 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005749 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00005750 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005751}
5752
5753
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005754template <typename sinkchar>
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005755static inline void StringBuilderConcatHelper(String* special,
5756 sinkchar* sink,
5757 FixedArray* fixed_array,
5758 int array_length) {
5759 int position = 0;
5760 for (int i = 0; i < array_length; i++) {
5761 Object* element = fixed_array->get(i);
5762 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005763 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005764 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005765 int pos;
5766 int len;
5767 if (encoded_slice > 0) {
5768 // Position and length encoded in one smi.
5769 pos = StringBuilderSubstringPosition::decode(encoded_slice);
5770 len = StringBuilderSubstringLength::decode(encoded_slice);
5771 } else {
5772 // Position and length encoded in two smis.
5773 Object* obj = fixed_array->get(++i);
5774 ASSERT(obj->IsSmi());
5775 pos = Smi::cast(obj)->value();
5776 len = -encoded_slice;
5777 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00005778 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00005779 sink + position,
5780 pos,
5781 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005782 position += len;
5783 } else {
5784 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005785 int element_length = string->length();
5786 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005787 position += element_length;
5788 }
5789 }
5790}
5791
5792
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005793static Object* Runtime_StringBuilderConcat(Arguments args) {
5794 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005795 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005796 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005797 if (!args[1]->IsSmi()) {
5798 Top::context()->mark_out_of_memory();
5799 return Failure::OutOfMemoryException();
5800 }
5801 int array_length = Smi::cast(args[1])->value();
5802 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005803
5804 // This assumption is used by the slice encoding in one or two smis.
5805 ASSERT(Smi::kMaxValue >= String::kMaxLength);
5806
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005807 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005808 if (!array->HasFastElements()) {
5809 return Top::Throw(Heap::illegal_argument_symbol());
5810 }
5811 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005812 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005813 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005814 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005815
5816 if (array_length == 0) {
5817 return Heap::empty_string();
5818 } else if (array_length == 1) {
5819 Object* first = fixed_array->get(0);
5820 if (first->IsString()) return first;
5821 }
5822
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005823 bool ascii = special->HasOnlyAsciiChars();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005824 int position = 0;
5825 for (int i = 0; i < array_length; i++) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005826 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005827 Object* elt = fixed_array->get(i);
5828 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005829 // Smi encoding of position and length.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005830 int smi_value = Smi::cast(elt)->value();
5831 int pos;
5832 int len;
5833 if (smi_value > 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005834 // Position and length encoded in one smi.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005835 pos = StringBuilderSubstringPosition::decode(smi_value);
5836 len = StringBuilderSubstringLength::decode(smi_value);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005837 } else {
5838 // Position and length encoded in two smis.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005839 len = -smi_value;
5840 // Get the position and check that it is a positive smi.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005841 i++;
5842 if (i >= array_length) {
5843 return Top::Throw(Heap::illegal_argument_symbol());
5844 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005845 Object* next_smi = fixed_array->get(i);
5846 if (!next_smi->IsSmi()) {
5847 return Top::Throw(Heap::illegal_argument_symbol());
5848 }
5849 pos = Smi::cast(next_smi)->value();
5850 if (pos < 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005851 return Top::Throw(Heap::illegal_argument_symbol());
5852 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005853 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005854 ASSERT(pos >= 0);
5855 ASSERT(len >= 0);
5856 if (pos > special_length || len > special_length - pos) {
5857 return Top::Throw(Heap::illegal_argument_symbol());
5858 }
5859 increment = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005860 } else if (elt->IsString()) {
5861 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005862 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005863 increment = element_length;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005864 if (ascii && !element->HasOnlyAsciiChars()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005865 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005866 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005867 } else {
5868 return Top::Throw(Heap::illegal_argument_symbol());
5869 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005870 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005871 Top::context()->mark_out_of_memory();
5872 return Failure::OutOfMemoryException();
5873 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005874 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005875 }
5876
5877 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005878 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005879
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005880 if (ascii) {
5881 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005882 if (object->IsFailure()) return object;
5883 SeqAsciiString* answer = SeqAsciiString::cast(object);
5884 StringBuilderConcatHelper(special,
5885 answer->GetChars(),
5886 fixed_array,
5887 array_length);
5888 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005889 } else {
5890 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005891 if (object->IsFailure()) return object;
5892 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
5893 StringBuilderConcatHelper(special,
5894 answer->GetChars(),
5895 fixed_array,
5896 array_length);
5897 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005898 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005899}
5900
5901
5902static Object* Runtime_NumberOr(Arguments args) {
5903 NoHandleAllocation ha;
5904 ASSERT(args.length() == 2);
5905
5906 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5907 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5908 return Heap::NumberFromInt32(x | y);
5909}
5910
5911
5912static Object* Runtime_NumberAnd(Arguments args) {
5913 NoHandleAllocation ha;
5914 ASSERT(args.length() == 2);
5915
5916 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5917 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5918 return Heap::NumberFromInt32(x & y);
5919}
5920
5921
5922static Object* Runtime_NumberXor(Arguments args) {
5923 NoHandleAllocation ha;
5924 ASSERT(args.length() == 2);
5925
5926 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5927 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5928 return Heap::NumberFromInt32(x ^ y);
5929}
5930
5931
5932static Object* Runtime_NumberNot(Arguments args) {
5933 NoHandleAllocation ha;
5934 ASSERT(args.length() == 1);
5935
5936 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5937 return Heap::NumberFromInt32(~x);
5938}
5939
5940
5941static Object* Runtime_NumberShl(Arguments args) {
5942 NoHandleAllocation ha;
5943 ASSERT(args.length() == 2);
5944
5945 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5946 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5947 return Heap::NumberFromInt32(x << (y & 0x1f));
5948}
5949
5950
5951static Object* Runtime_NumberShr(Arguments args) {
5952 NoHandleAllocation ha;
5953 ASSERT(args.length() == 2);
5954
5955 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
5956 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5957 return Heap::NumberFromUint32(x >> (y & 0x1f));
5958}
5959
5960
5961static Object* Runtime_NumberSar(Arguments args) {
5962 NoHandleAllocation ha;
5963 ASSERT(args.length() == 2);
5964
5965 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5966 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5967 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
5968}
5969
5970
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005971static Object* Runtime_NumberEquals(Arguments args) {
5972 NoHandleAllocation ha;
5973 ASSERT(args.length() == 2);
5974
5975 CONVERT_DOUBLE_CHECKED(x, args[0]);
5976 CONVERT_DOUBLE_CHECKED(y, args[1]);
5977 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
5978 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
5979 if (x == y) return Smi::FromInt(EQUAL);
5980 Object* result;
5981 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
5982 result = Smi::FromInt(EQUAL);
5983 } else {
5984 result = Smi::FromInt(NOT_EQUAL);
5985 }
5986 return result;
5987}
5988
5989
5990static Object* Runtime_StringEquals(Arguments args) {
5991 NoHandleAllocation ha;
5992 ASSERT(args.length() == 2);
5993
5994 CONVERT_CHECKED(String, x, args[0]);
5995 CONVERT_CHECKED(String, y, args[1]);
5996
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005997 bool not_equal = !x->Equals(y);
5998 // This is slightly convoluted because the value that signifies
5999 // equality is 0 and inequality is 1 so we have to negate the result
6000 // from String::Equals.
6001 ASSERT(not_equal == 0 || not_equal == 1);
6002 STATIC_CHECK(EQUAL == 0);
6003 STATIC_CHECK(NOT_EQUAL == 1);
6004 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006005}
6006
6007
6008static Object* Runtime_NumberCompare(Arguments args) {
6009 NoHandleAllocation ha;
6010 ASSERT(args.length() == 3);
6011
6012 CONVERT_DOUBLE_CHECKED(x, args[0]);
6013 CONVERT_DOUBLE_CHECKED(y, args[1]);
6014 if (isnan(x) || isnan(y)) return args[2];
6015 if (x == y) return Smi::FromInt(EQUAL);
6016 if (isless(x, y)) return Smi::FromInt(LESS);
6017 return Smi::FromInt(GREATER);
6018}
6019
6020
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006021// Compare two Smis as if they were converted to strings and then
6022// compared lexicographically.
6023static Object* Runtime_SmiLexicographicCompare(Arguments args) {
6024 NoHandleAllocation ha;
6025 ASSERT(args.length() == 2);
6026
6027 // Arrays for the individual characters of the two Smis. Smis are
6028 // 31 bit integers and 10 decimal digits are therefore enough.
6029 static int x_elms[10];
6030 static int y_elms[10];
6031
6032 // Extract the integer values from the Smis.
6033 CONVERT_CHECKED(Smi, x, args[0]);
6034 CONVERT_CHECKED(Smi, y, args[1]);
6035 int x_value = x->value();
6036 int y_value = y->value();
6037
6038 // If the integers are equal so are the string representations.
6039 if (x_value == y_value) return Smi::FromInt(EQUAL);
6040
6041 // If one of the integers are zero the normal integer order is the
6042 // same as the lexicographic order of the string representations.
6043 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
6044
ager@chromium.org32912102009-01-16 10:38:43 +00006045 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006046 // smallest because the char code of '-' is less than the char code
6047 // of any digit. Otherwise, we make both values positive.
6048 if (x_value < 0 || y_value < 0) {
6049 if (y_value >= 0) return Smi::FromInt(LESS);
6050 if (x_value >= 0) return Smi::FromInt(GREATER);
6051 x_value = -x_value;
6052 y_value = -y_value;
6053 }
6054
6055 // Convert the integers to arrays of their decimal digits.
6056 int x_index = 0;
6057 int y_index = 0;
6058 while (x_value > 0) {
6059 x_elms[x_index++] = x_value % 10;
6060 x_value /= 10;
6061 }
6062 while (y_value > 0) {
6063 y_elms[y_index++] = y_value % 10;
6064 y_value /= 10;
6065 }
6066
6067 // Loop through the arrays of decimal digits finding the first place
6068 // where they differ.
6069 while (--x_index >= 0 && --y_index >= 0) {
6070 int diff = x_elms[x_index] - y_elms[y_index];
6071 if (diff != 0) return Smi::FromInt(diff);
6072 }
6073
6074 // If one array is a suffix of the other array, the longest array is
6075 // the representation of the largest of the Smis in the
6076 // lexicographic ordering.
6077 return Smi::FromInt(x_index - y_index);
6078}
6079
6080
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006081static Object* StringInputBufferCompare(String* x, String* y) {
6082 static StringInputBuffer bufx;
6083 static StringInputBuffer bufy;
6084 bufx.Reset(x);
6085 bufy.Reset(y);
6086 while (bufx.has_more() && bufy.has_more()) {
6087 int d = bufx.GetNext() - bufy.GetNext();
6088 if (d < 0) return Smi::FromInt(LESS);
6089 else if (d > 0) return Smi::FromInt(GREATER);
6090 }
6091
6092 // x is (non-trivial) prefix of y:
6093 if (bufy.has_more()) return Smi::FromInt(LESS);
6094 // y is prefix of x:
6095 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
6096}
6097
6098
6099static Object* FlatStringCompare(String* x, String* y) {
6100 ASSERT(x->IsFlat());
6101 ASSERT(y->IsFlat());
6102 Object* equal_prefix_result = Smi::FromInt(EQUAL);
6103 int prefix_length = x->length();
6104 if (y->length() < prefix_length) {
6105 prefix_length = y->length();
6106 equal_prefix_result = Smi::FromInt(GREATER);
6107 } else if (y->length() > prefix_length) {
6108 equal_prefix_result = Smi::FromInt(LESS);
6109 }
6110 int r;
6111 if (x->IsAsciiRepresentation()) {
6112 Vector<const char> x_chars = x->ToAsciiVector();
6113 if (y->IsAsciiRepresentation()) {
6114 Vector<const char> y_chars = y->ToAsciiVector();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006115 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006116 } else {
6117 Vector<const uc16> y_chars = y->ToUC16Vector();
6118 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
6119 }
6120 } else {
6121 Vector<const uc16> x_chars = x->ToUC16Vector();
6122 if (y->IsAsciiRepresentation()) {
6123 Vector<const char> y_chars = y->ToAsciiVector();
6124 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
6125 } else {
6126 Vector<const uc16> y_chars = y->ToUC16Vector();
6127 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
6128 }
6129 }
6130 Object* result;
6131 if (r == 0) {
6132 result = equal_prefix_result;
6133 } else {
6134 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
6135 }
6136 ASSERT(result == StringInputBufferCompare(x, y));
6137 return result;
6138}
6139
6140
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006141static Object* Runtime_StringCompare(Arguments args) {
6142 NoHandleAllocation ha;
6143 ASSERT(args.length() == 2);
6144
6145 CONVERT_CHECKED(String, x, args[0]);
6146 CONVERT_CHECKED(String, y, args[1]);
6147
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006148 Counters::string_compare_runtime.Increment();
6149
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006150 // A few fast case tests before we flatten.
6151 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006152 if (y->length() == 0) {
6153 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006154 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006155 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006156 return Smi::FromInt(LESS);
6157 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006158
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006159 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006160 if (d < 0) return Smi::FromInt(LESS);
6161 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006162
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006163 Object* obj = Heap::PrepareForCompare(x);
6164 if (obj->IsFailure()) return obj;
6165 obj = Heap::PrepareForCompare(y);
6166 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006167
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006168 return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
6169 : StringInputBufferCompare(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006170}
6171
6172
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006173static Object* Runtime_Math_acos(Arguments args) {
6174 NoHandleAllocation ha;
6175 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006176 Counters::math_acos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006177
6178 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006179 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006180}
6181
6182
6183static Object* Runtime_Math_asin(Arguments args) {
6184 NoHandleAllocation ha;
6185 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006186 Counters::math_asin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006187
6188 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006189 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006190}
6191
6192
6193static Object* Runtime_Math_atan(Arguments args) {
6194 NoHandleAllocation ha;
6195 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006196 Counters::math_atan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006197
6198 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006199 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006200}
6201
6202
6203static Object* Runtime_Math_atan2(Arguments args) {
6204 NoHandleAllocation ha;
6205 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006206 Counters::math_atan2.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006207
6208 CONVERT_DOUBLE_CHECKED(x, args[0]);
6209 CONVERT_DOUBLE_CHECKED(y, args[1]);
6210 double result;
6211 if (isinf(x) && isinf(y)) {
6212 // Make sure that the result in case of two infinite arguments
6213 // is a multiple of Pi / 4. The sign of the result is determined
6214 // by the first argument (x) and the sign of the second argument
6215 // determines the multiplier: one or three.
6216 static double kPiDividedBy4 = 0.78539816339744830962;
6217 int multiplier = (x < 0) ? -1 : 1;
6218 if (y < 0) multiplier *= 3;
6219 result = multiplier * kPiDividedBy4;
6220 } else {
6221 result = atan2(x, y);
6222 }
6223 return Heap::AllocateHeapNumber(result);
6224}
6225
6226
6227static Object* Runtime_Math_ceil(Arguments args) {
6228 NoHandleAllocation ha;
6229 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006230 Counters::math_ceil.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006231
6232 CONVERT_DOUBLE_CHECKED(x, args[0]);
6233 return Heap::NumberFromDouble(ceiling(x));
6234}
6235
6236
6237static Object* Runtime_Math_cos(Arguments args) {
6238 NoHandleAllocation ha;
6239 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006240 Counters::math_cos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006241
6242 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006243 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006244}
6245
6246
6247static Object* Runtime_Math_exp(Arguments args) {
6248 NoHandleAllocation ha;
6249 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006250 Counters::math_exp.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006251
6252 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006253 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006254}
6255
6256
6257static Object* Runtime_Math_floor(Arguments args) {
6258 NoHandleAllocation ha;
6259 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006260 Counters::math_floor.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006261
6262 CONVERT_DOUBLE_CHECKED(x, args[0]);
6263 return Heap::NumberFromDouble(floor(x));
6264}
6265
6266
6267static Object* Runtime_Math_log(Arguments args) {
6268 NoHandleAllocation ha;
6269 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006270 Counters::math_log.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006271
6272 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006273 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006274}
6275
6276
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006277// Helper function to compute x^y, where y is known to be an
6278// integer. Uses binary decomposition to limit the number of
6279// multiplications; see the discussion in "Hacker's Delight" by Henry
6280// S. Warren, Jr., figure 11-6, page 213.
6281static double powi(double x, int y) {
6282 ASSERT(y != kMinInt);
6283 unsigned n = (y < 0) ? -y : y;
6284 double m = x;
6285 double p = 1;
6286 while (true) {
6287 if ((n & 1) != 0) p *= m;
6288 n >>= 1;
6289 if (n == 0) {
6290 if (y < 0) {
6291 // Unfortunately, we have to be careful when p has reached
6292 // infinity in the computation, because sometimes the higher
6293 // internal precision in the pow() implementation would have
6294 // given us a finite p. This happens very rarely.
6295 double result = 1.0 / p;
6296 return (result == 0 && isinf(p))
6297 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
6298 : result;
6299 } else {
6300 return p;
6301 }
6302 }
6303 m *= m;
6304 }
6305}
6306
6307
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006308static Object* Runtime_Math_pow(Arguments args) {
6309 NoHandleAllocation ha;
6310 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006311 Counters::math_pow.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006312
6313 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006314
6315 // If the second argument is a smi, it is much faster to call the
6316 // custom powi() function than the generic pow().
6317 if (args[1]->IsSmi()) {
6318 int y = Smi::cast(args[1])->value();
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00006319 return Heap::NumberFromDouble(powi(x, y));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006320 }
6321
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006322 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00006323
6324 if (!isinf(x)) {
6325 if (y == 0.5) {
6326 // It's not uncommon to use Math.pow(x, 0.5) to compute the
6327 // square root of a number. To speed up such computations, we
6328 // explictly check for this case and use the sqrt() function
6329 // which is faster than pow().
6330 return Heap::AllocateHeapNumber(sqrt(x));
6331 } else if (y == -0.5) {
6332 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
6333 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
6334 }
6335 }
6336
6337 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006338 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006339 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6340 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006341 } else {
6342 return Heap::AllocateHeapNumber(pow(x, y));
6343 }
6344}
6345
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006346// Fast version of Math.pow if we know that y is not an integer and
6347// y is not -0.5 or 0.5. Used as slowcase from codegen.
6348static Object* Runtime_Math_pow_cfunction(Arguments args) {
6349 NoHandleAllocation ha;
6350 ASSERT(args.length() == 2);
6351 CONVERT_DOUBLE_CHECKED(x, args[0]);
6352 CONVERT_DOUBLE_CHECKED(y, args[1]);
6353 if (y == 0) {
6354 return Smi::FromInt(1);
6355 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6356 return Heap::nan_value();
6357 } else {
6358 return Heap::AllocateHeapNumber(pow(x, y));
6359 }
6360}
6361
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006362
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006363static Object* Runtime_RoundNumber(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006364 NoHandleAllocation ha;
6365 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006366 Counters::math_round.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006367
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006368 if (!args[0]->IsHeapNumber()) {
6369 // Must be smi. Return the argument unchanged for all the other types
6370 // to make fuzz-natives test happy.
6371 return args[0];
6372 }
6373
6374 HeapNumber* number = reinterpret_cast<HeapNumber*>(args[0]);
6375
6376 double value = number->value();
6377 int exponent = number->get_exponent();
6378 int sign = number->get_sign();
6379
6380 // We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
6381 // should be rounded to 2^30, which is not smi.
6382 if (!sign && exponent <= kSmiValueSize - 3) {
6383 return Smi::FromInt(static_cast<int>(value + 0.5));
6384 }
6385
6386 // If the magnitude is big enough, there's no place for fraction part. If we
6387 // try to add 0.5 to this number, 1.0 will be added instead.
6388 if (exponent >= 52) {
6389 return number;
6390 }
6391
6392 if (sign && value >= -0.5) return Heap::minus_zero_value();
6393
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006394 // Do not call NumberFromDouble() to avoid extra checks.
6395 return Heap::AllocateHeapNumber(floor(value + 0.5));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006396}
6397
6398
6399static Object* Runtime_Math_sin(Arguments args) {
6400 NoHandleAllocation ha;
6401 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006402 Counters::math_sin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006403
6404 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006405 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006406}
6407
6408
6409static Object* Runtime_Math_sqrt(Arguments args) {
6410 NoHandleAllocation ha;
6411 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006412 Counters::math_sqrt.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006413
6414 CONVERT_DOUBLE_CHECKED(x, args[0]);
6415 return Heap::AllocateHeapNumber(sqrt(x));
6416}
6417
6418
6419static Object* Runtime_Math_tan(Arguments args) {
6420 NoHandleAllocation ha;
6421 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006422 Counters::math_tan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006423
6424 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006425 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006426}
6427
6428
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006429static int MakeDay(int year, int month, int day) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006430 static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
6431 181, 212, 243, 273, 304, 334};
6432 static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
6433 182, 213, 244, 274, 305, 335};
6434
6435 year += month / 12;
6436 month %= 12;
6437 if (month < 0) {
6438 year--;
6439 month += 12;
6440 }
6441
6442 ASSERT(month >= 0);
6443 ASSERT(month < 12);
6444
6445 // year_delta is an arbitrary number such that:
6446 // a) year_delta = -1 (mod 400)
6447 // b) year + year_delta > 0 for years in the range defined by
6448 // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
6449 // Jan 1 1970. This is required so that we don't run into integer
6450 // division of negative numbers.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006451 // c) there shouldn't be an overflow for 32-bit integers in the following
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006452 // operations.
6453 static const int year_delta = 399999;
6454 static const int base_day = 365 * (1970 + year_delta) +
6455 (1970 + year_delta) / 4 -
6456 (1970 + year_delta) / 100 +
6457 (1970 + year_delta) / 400;
6458
6459 int year1 = year + year_delta;
6460 int day_from_year = 365 * year1 +
6461 year1 / 4 -
6462 year1 / 100 +
6463 year1 / 400 -
6464 base_day;
6465
6466 if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006467 return day_from_year + day_from_month[month] + day - 1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006468 }
6469
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006470 return day_from_year + day_from_month_leap[month] + day - 1;
6471}
6472
6473
6474static Object* Runtime_DateMakeDay(Arguments args) {
6475 NoHandleAllocation ha;
6476 ASSERT(args.length() == 3);
6477
6478 CONVERT_SMI_CHECKED(year, args[0]);
6479 CONVERT_SMI_CHECKED(month, args[1]);
6480 CONVERT_SMI_CHECKED(date, args[2]);
6481
6482 return Smi::FromInt(MakeDay(year, month, date));
6483}
6484
6485
6486static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
6487static const int kDaysIn4Years = 4 * 365 + 1;
6488static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
6489static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
6490static const int kDays1970to2000 = 30 * 365 + 7;
6491static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
6492 kDays1970to2000;
6493static const int kYearsOffset = 400000;
6494
6495static const char kDayInYear[] = {
6496 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6497 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6498 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6499 22, 23, 24, 25, 26, 27, 28,
6500 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6501 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6502 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6503 22, 23, 24, 25, 26, 27, 28, 29, 30,
6504 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6505 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6506 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6507 22, 23, 24, 25, 26, 27, 28, 29, 30,
6508 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6509 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6510 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6511 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6512 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6513 22, 23, 24, 25, 26, 27, 28, 29, 30,
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, 29, 30,
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
6521 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6522 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6523 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6524 22, 23, 24, 25, 26, 27, 28,
6525 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6526 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6527 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6528 22, 23, 24, 25, 26, 27, 28, 29, 30,
6529 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6530 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6531 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6532 22, 23, 24, 25, 26, 27, 28, 29, 30,
6533 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6534 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6535 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6536 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6537 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6538 22, 23, 24, 25, 26, 27, 28, 29, 30,
6539 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6540 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6541 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6542 22, 23, 24, 25, 26, 27, 28, 29, 30,
6543 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6544 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6545
6546 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6547 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6548 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6549 22, 23, 24, 25, 26, 27, 28, 29,
6550 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6551 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6552 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6553 22, 23, 24, 25, 26, 27, 28, 29, 30,
6554 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6555 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6556 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6557 22, 23, 24, 25, 26, 27, 28, 29, 30,
6558 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6559 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6560 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6561 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6562 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6563 22, 23, 24, 25, 26, 27, 28, 29, 30,
6564 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6565 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6566 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6567 22, 23, 24, 25, 26, 27, 28, 29, 30,
6568 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6569 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6570
6571 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6572 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6573 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6574 22, 23, 24, 25, 26, 27, 28,
6575 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6576 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6577 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6578 22, 23, 24, 25, 26, 27, 28, 29, 30,
6579 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6580 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6581 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6582 22, 23, 24, 25, 26, 27, 28, 29, 30,
6583 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6584 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6585 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6586 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6587 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6588 22, 23, 24, 25, 26, 27, 28, 29, 30,
6589 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6590 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6591 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6592 22, 23, 24, 25, 26, 27, 28, 29, 30,
6593 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6594 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
6595
6596static const char kMonthInYear[] = {
6597 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,
6598 0, 0, 0, 0, 0, 0,
6599 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,
6600 1, 1, 1,
6601 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,
6602 2, 2, 2, 2, 2, 2,
6603 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,
6604 3, 3, 3, 3, 3,
6605 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,
6606 4, 4, 4, 4, 4, 4,
6607 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,
6608 5, 5, 5, 5, 5,
6609 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,
6610 6, 6, 6, 6, 6, 6,
6611 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,
6612 7, 7, 7, 7, 7, 7,
6613 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,
6614 8, 8, 8, 8, 8,
6615 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,
6616 9, 9, 9, 9, 9, 9,
6617 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6618 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6619 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6620 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6621
6622 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,
6623 0, 0, 0, 0, 0, 0,
6624 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,
6625 1, 1, 1,
6626 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,
6627 2, 2, 2, 2, 2, 2,
6628 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,
6629 3, 3, 3, 3, 3,
6630 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,
6631 4, 4, 4, 4, 4, 4,
6632 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,
6633 5, 5, 5, 5, 5,
6634 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,
6635 6, 6, 6, 6, 6, 6,
6636 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,
6637 7, 7, 7, 7, 7, 7,
6638 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,
6639 8, 8, 8, 8, 8,
6640 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,
6641 9, 9, 9, 9, 9, 9,
6642 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6643 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6644 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6645 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6646
6647 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,
6648 0, 0, 0, 0, 0, 0,
6649 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,
6650 1, 1, 1, 1,
6651 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,
6652 2, 2, 2, 2, 2, 2,
6653 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,
6654 3, 3, 3, 3, 3,
6655 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,
6656 4, 4, 4, 4, 4, 4,
6657 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,
6658 5, 5, 5, 5, 5,
6659 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,
6660 6, 6, 6, 6, 6, 6,
6661 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,
6662 7, 7, 7, 7, 7, 7,
6663 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,
6664 8, 8, 8, 8, 8,
6665 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,
6666 9, 9, 9, 9, 9, 9,
6667 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6668 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6669 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6670 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6671
6672 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,
6673 0, 0, 0, 0, 0, 0,
6674 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,
6675 1, 1, 1,
6676 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,
6677 2, 2, 2, 2, 2, 2,
6678 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,
6679 3, 3, 3, 3, 3,
6680 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,
6681 4, 4, 4, 4, 4, 4,
6682 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,
6683 5, 5, 5, 5, 5,
6684 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,
6685 6, 6, 6, 6, 6, 6,
6686 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,
6687 7, 7, 7, 7, 7, 7,
6688 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,
6689 8, 8, 8, 8, 8,
6690 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,
6691 9, 9, 9, 9, 9, 9,
6692 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6693 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6694 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6695 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
6696
6697
6698// This function works for dates from 1970 to 2099.
6699static inline void DateYMDFromTimeAfter1970(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006700 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006701#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006702 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006703#endif
6704
6705 year = 1970 + (4 * date + 2) / kDaysIn4Years;
6706 date %= kDaysIn4Years;
6707
6708 month = kMonthInYear[date];
6709 day = kDayInYear[date];
6710
6711 ASSERT(MakeDay(year, month, day) == save_date);
6712}
6713
6714
6715static inline void DateYMDFromTimeSlow(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006716 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006717#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006718 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006719#endif
6720
6721 date += kDaysOffset;
6722 year = 400 * (date / kDaysIn400Years) - kYearsOffset;
6723 date %= kDaysIn400Years;
6724
6725 ASSERT(MakeDay(year, 0, 1) + date == save_date);
6726
6727 date--;
6728 int yd1 = date / kDaysIn100Years;
6729 date %= kDaysIn100Years;
6730 year += 100 * yd1;
6731
6732 date++;
6733 int yd2 = date / kDaysIn4Years;
6734 date %= kDaysIn4Years;
6735 year += 4 * yd2;
6736
6737 date--;
6738 int yd3 = date / 365;
6739 date %= 365;
6740 year += yd3;
6741
6742 bool is_leap = (!yd1 || yd2) && !yd3;
6743
6744 ASSERT(date >= -1);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006745 ASSERT(is_leap || (date >= 0));
6746 ASSERT((date < 365) || (is_leap && (date < 366)));
6747 ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0))));
6748 ASSERT(is_leap || ((MakeDay(year, 0, 1) + date) == save_date));
6749 ASSERT(!is_leap || ((MakeDay(year, 0, 1) + date + 1) == save_date));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006750
6751 if (is_leap) {
6752 day = kDayInYear[2*365 + 1 + date];
6753 month = kMonthInYear[2*365 + 1 + date];
6754 } else {
6755 day = kDayInYear[date];
6756 month = kMonthInYear[date];
6757 }
6758
6759 ASSERT(MakeDay(year, month, day) == save_date);
6760}
6761
6762
6763static inline void DateYMDFromTime(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006764 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006765 if (date >= 0 && date < 32 * kDaysIn4Years) {
6766 DateYMDFromTimeAfter1970(date, year, month, day);
6767 } else {
6768 DateYMDFromTimeSlow(date, year, month, day);
6769 }
6770}
6771
6772
6773static Object* Runtime_DateYMDFromTime(Arguments args) {
6774 NoHandleAllocation ha;
6775 ASSERT(args.length() == 2);
6776
6777 CONVERT_DOUBLE_CHECKED(t, args[0]);
6778 CONVERT_CHECKED(JSArray, res_array, args[1]);
6779
6780 int year, month, day;
6781 DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
6782
ricow@chromium.org0b9f8502010-08-18 07:45:01 +00006783 RUNTIME_ASSERT(res_array->elements()->map() == Heap::fixed_array_map());
6784 FixedArray* elms = FixedArray::cast(res_array->elements());
6785 RUNTIME_ASSERT(elms->length() == 3);
6786
6787 elms->set(0, Smi::FromInt(year));
6788 elms->set(1, Smi::FromInt(month));
6789 elms->set(2, Smi::FromInt(day));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006790
6791 return Heap::undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006792}
6793
6794
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006795static Object* Runtime_NewArgumentsFast(Arguments args) {
6796 NoHandleAllocation ha;
6797 ASSERT(args.length() == 3);
6798
6799 JSFunction* callee = JSFunction::cast(args[0]);
6800 Object** parameters = reinterpret_cast<Object**>(args[1]);
6801 const int length = Smi::cast(args[2])->value();
6802
6803 Object* result = Heap::AllocateArgumentsObject(callee, length);
6804 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006805 // Allocate the elements if needed.
6806 if (length > 0) {
6807 // Allocate the fixed array.
6808 Object* obj = Heap::AllocateRawFixedArray(length);
6809 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006810
6811 AssertNoAllocation no_gc;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006812 FixedArray* array = reinterpret_cast<FixedArray*>(obj);
6813 array->set_map(Heap::fixed_array_map());
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006814 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006815
6816 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006817 for (int i = 0; i < length; i++) {
6818 array->set(i, *--parameters, mode);
6819 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006820 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006821 }
6822 return result;
6823}
6824
6825
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006826static Object* Runtime_NewClosure(Arguments args) {
6827 HandleScope scope;
6828 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00006829 CONVERT_ARG_CHECKED(Context, context, 0);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006830 CONVERT_ARG_CHECKED(SharedFunctionInfo, shared, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006831
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00006832 PretenureFlag pretenure = (context->global_context() == *context)
6833 ? TENURED // Allocate global closures in old space.
6834 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006835 Handle<JSFunction> result =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006836 Factory::NewFunctionFromSharedFunctionInfo(shared, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006837 return *result;
6838}
6839
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006840static Object* Runtime_NewObjectFromBound(Arguments args) {
6841 HandleScope scope;
6842 ASSERT(args.length() == 2);
6843 CONVERT_ARG_CHECKED(JSFunction, function, 0);
6844 CONVERT_ARG_CHECKED(JSArray, params, 1);
6845
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006846 RUNTIME_ASSERT(params->HasFastElements());
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006847 FixedArray* fixed = FixedArray::cast(params->elements());
6848
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006849 int fixed_length = Smi::cast(params->length())->value();
6850 SmartPointer<Object**> param_data(NewArray<Object**>(fixed_length));
6851 for (int i = 0; i < fixed_length; i++) {
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006852 Handle<Object> val = Handle<Object>(fixed->get(i));
6853 param_data[i] = val.location();
6854 }
6855
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006856 bool exception = false;
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006857 Handle<Object> result = Execution::New(
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006858 function, fixed_length, *param_data, &exception);
6859 if (exception) {
6860 return Failure::Exception();
6861 }
6862 ASSERT(!result.is_null());
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006863 return *result;
6864}
6865
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006866
ager@chromium.org5c838252010-02-19 08:53:10 +00006867static Code* ComputeConstructStub(Handle<JSFunction> function) {
6868 Handle<Object> prototype = Factory::null_value();
6869 if (function->has_instance_prototype()) {
6870 prototype = Handle<Object>(function->instance_prototype());
6871 }
6872 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006873 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00006874 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006875 if (code->IsFailure()) {
6876 return Builtins::builtin(Builtins::JSConstructStubGeneric);
6877 }
6878 return Code::cast(code);
6879 }
6880
ager@chromium.org5c838252010-02-19 08:53:10 +00006881 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006882}
6883
6884
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006885static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006886 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006887 ASSERT(args.length() == 1);
6888
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006889 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006890
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006891 // If the constructor isn't a proper function we throw a type error.
6892 if (!constructor->IsJSFunction()) {
6893 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6894 Handle<Object> type_error =
6895 Factory::NewTypeError("not_constructor", arguments);
6896 return Top::Throw(*type_error);
6897 }
6898
6899 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006900
6901 // If function should not have prototype, construction is not allowed. In this
6902 // case generated code bailouts here, since function has no initial_map.
6903 if (!function->should_have_prototype()) {
6904 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6905 Handle<Object> type_error =
6906 Factory::NewTypeError("not_constructor", arguments);
6907 return Top::Throw(*type_error);
6908 }
6909
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006910#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006911 // Handle stepping into constructors if step into is active.
6912 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006913 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006914 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006915#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006916
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006917 if (function->has_initial_map()) {
6918 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006919 // The 'Function' function ignores the receiver object when
6920 // called using 'new' and creates a new JSFunction object that
6921 // is returned. The receiver object is only used for error
6922 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006923 // JSFunction. Factory::NewJSObject() should not be used to
6924 // allocate JSFunctions since it does not properly initialize
6925 // the shared part of the function. Since the receiver is
6926 // ignored anyway, we use the global object as the receiver
6927 // instead of a new JSFunction object. This way, errors are
6928 // reported the same way whether or not 'Function' is called
6929 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006930 return Top::context()->global();
6931 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006932 }
6933
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006934 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006935 Handle<SharedFunctionInfo> shared(function->shared());
6936 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006937
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006938 bool first_allocation = !function->has_initial_map();
6939 Handle<JSObject> result = Factory::NewJSObject(function);
6940 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006941 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00006942 ComputeConstructStub(Handle<JSFunction>(function)));
6943 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006944 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006945
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006946 Counters::constructed_objects.Increment();
6947 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006948
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006949 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006950}
6951
6952
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006953static Object* Runtime_LazyCompile(Arguments args) {
6954 HandleScope scope;
6955 ASSERT(args.length() == 1);
6956
6957 Handle<JSFunction> function = args.at<JSFunction>(0);
6958#ifdef DEBUG
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00006959 if (FLAG_trace_lazy && !function->shared()->is_compiled()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006960 PrintF("[lazy: ");
6961 function->shared()->name()->Print();
6962 PrintF("]\n");
6963 }
6964#endif
6965
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006966 // Compile the target function. Here we compile using CompileLazyInLoop in
6967 // order to get the optimized version. This helps code like delta-blue
6968 // that calls performance-critical routines through constructors. A
6969 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
6970 // direct call. Since the in-loop tracking takes place through CallICs
6971 // this means that things called through constructors are never known to
6972 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006973 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006974 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006975 return Failure::Exception();
6976 }
6977
6978 return function->code();
6979}
6980
6981
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006982static Object* Runtime_GetFunctionDelegate(Arguments args) {
6983 HandleScope scope;
6984 ASSERT(args.length() == 1);
6985 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6986 return *Execution::GetFunctionDelegate(args.at<Object>(0));
6987}
6988
6989
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00006990static Object* Runtime_GetConstructorDelegate(Arguments args) {
6991 HandleScope scope;
6992 ASSERT(args.length() == 1);
6993 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6994 return *Execution::GetConstructorDelegate(args.at<Object>(0));
6995}
6996
6997
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006998static Object* Runtime_NewContext(Arguments args) {
6999 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00007000 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007001
kasper.lund7276f142008-07-30 08:49:36 +00007002 CONVERT_CHECKED(JSFunction, function, args[0]);
ager@chromium.orgb5737492010-07-15 09:29:43 +00007003 int length = function->shared()->scope_info()->NumberOfContextSlots();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007004 Object* result = Heap::AllocateFunctionContext(length, function);
7005 if (result->IsFailure()) return result;
7006
7007 Top::set_context(Context::cast(result));
7008
kasper.lund7276f142008-07-30 08:49:36 +00007009 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007010}
7011
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007012static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007013 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007014 Object* js_object = object;
7015 if (!js_object->IsJSObject()) {
7016 js_object = js_object->ToObject();
7017 if (js_object->IsFailure()) {
7018 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007019 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007020 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007021 Handle<Object> result =
7022 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
7023 return Top::Throw(*result);
7024 }
7025 }
7026
7027 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007028 Heap::AllocateWithContext(Top::context(),
7029 JSObject::cast(js_object),
7030 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007031 if (result->IsFailure()) return result;
7032
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007033 Context* context = Context::cast(result);
7034 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007035
kasper.lund7276f142008-07-30 08:49:36 +00007036 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007037}
7038
7039
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007040static Object* Runtime_PushContext(Arguments args) {
7041 NoHandleAllocation ha;
7042 ASSERT(args.length() == 1);
7043 return PushContextHelper(args[0], false);
7044}
7045
7046
7047static Object* Runtime_PushCatchContext(Arguments args) {
7048 NoHandleAllocation ha;
7049 ASSERT(args.length() == 1);
7050 return PushContextHelper(args[0], true);
7051}
7052
7053
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007054static Object* Runtime_LookupContext(Arguments args) {
7055 HandleScope scope;
7056 ASSERT(args.length() == 2);
7057
7058 CONVERT_ARG_CHECKED(Context, context, 0);
7059 CONVERT_ARG_CHECKED(String, name, 1);
7060
7061 int index;
7062 PropertyAttributes attributes;
7063 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007064 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007065 context->Lookup(name, flags, &index, &attributes);
7066
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007067 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007068 ASSERT(holder->IsJSObject());
7069 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007070 }
7071
7072 // No intermediate context found. Use global object by default.
7073 return Top::context()->global();
7074}
7075
7076
ager@chromium.orga1645e22009-09-09 19:27:10 +00007077// A mechanism to return a pair of Object pointers in registers (if possible).
7078// How this is achieved is calling convention-dependent.
7079// All currently supported x86 compiles uses calling conventions that are cdecl
7080// variants where a 64-bit value is returned in two 32-bit registers
7081// (edx:eax on ia32, r1:r0 on ARM).
7082// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
7083// In Win64 calling convention, a struct of two pointers is returned in memory,
7084// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007085#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007086struct ObjectPair {
7087 Object* x;
7088 Object* y;
7089};
ager@chromium.orga1645e22009-09-09 19:27:10 +00007090
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007091static inline ObjectPair MakePair(Object* x, Object* y) {
7092 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00007093 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
7094 // In Win64 they are assigned to a hidden first argument.
7095 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007096}
7097#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007098typedef uint64_t ObjectPair;
7099static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007100 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007101 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007102}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007103#endif
7104
7105
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007106static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007107 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
7108 USE(attributes);
7109 return x->IsTheHole() ? Heap::undefined_value() : x;
7110}
7111
7112
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007113static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
7114 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007115 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007116 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007117 JSFunction* context_extension_function =
7118 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007119 // If the holder isn't a context extension object, we just return it
7120 // as the receiver. This allows arguments objects to be used as
7121 // receivers, but only if they are put in the context scope chain
7122 // explicitly via a with-statement.
7123 Object* constructor = holder->map()->constructor();
7124 if (constructor != context_extension_function) return holder;
7125 // Fall back to using the global object as the receiver if the
7126 // property turns out to be a local variable allocated in a context
7127 // extension object - introduced via eval.
7128 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007129}
7130
7131
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007132static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007133 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00007134 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007135
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007136 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00007137 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007138 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007139 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007140 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007141
7142 int index;
7143 PropertyAttributes attributes;
7144 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007145 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007146 context->Lookup(name, flags, &index, &attributes);
7147
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007148 // If the index is non-negative, the slot has been found in a local
7149 // variable or a parameter. Read it from the context object or the
7150 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007151 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007152 // If the "property" we were looking for is a local variable or an
7153 // argument in a context, the receiver is the global object; see
7154 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
7155 JSObject* receiver = Top::context()->global()->global_receiver();
7156 Object* value = (holder->IsContext())
7157 ? Context::cast(*holder)->get(index)
7158 : JSObject::cast(*holder)->GetElement(index);
7159 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007160 }
7161
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007162 // If the holder is found, we read the property from it.
7163 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007164 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007165 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007166 JSObject* receiver;
7167 if (object->IsGlobalObject()) {
7168 receiver = GlobalObject::cast(object)->global_receiver();
7169 } else if (context->is_exception_holder(*holder)) {
7170 receiver = Top::context()->global()->global_receiver();
7171 } else {
7172 receiver = ComputeReceiverForNonGlobal(object);
7173 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007174 // No need to unhole the value here. This is taken care of by the
7175 // GetProperty function.
7176 Object* value = object->GetProperty(*name);
7177 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007178 }
7179
7180 if (throw_error) {
7181 // The property doesn't exist - throw exception.
7182 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007183 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007184 return MakePair(Top::Throw(*reference_error), NULL);
7185 } else {
7186 // The property doesn't exist - return undefined
7187 return MakePair(Heap::undefined_value(), Heap::undefined_value());
7188 }
7189}
7190
7191
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007192static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007193 return LoadContextSlotHelper(args, true);
7194}
7195
7196
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007197static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007198 return LoadContextSlotHelper(args, false);
7199}
7200
7201
7202static Object* Runtime_StoreContextSlot(Arguments args) {
7203 HandleScope scope;
7204 ASSERT(args.length() == 3);
7205
7206 Handle<Object> value(args[0]);
7207 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007208 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007209
7210 int index;
7211 PropertyAttributes attributes;
7212 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007213 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007214 context->Lookup(name, flags, &index, &attributes);
7215
7216 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007217 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007218 // Ignore if read_only variable.
7219 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007220 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007221 }
7222 } else {
7223 ASSERT((attributes & READ_ONLY) == 0);
7224 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007225 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007226 USE(result);
7227 ASSERT(!result->IsFailure());
7228 }
7229 return *value;
7230 }
7231
7232 // Slow case: The property is not in a FixedArray context.
7233 // It is either in an JSObject extension context or it was not found.
7234 Handle<JSObject> context_ext;
7235
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007236 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007237 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007238 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007239 } else {
7240 // The property was not found. It needs to be stored in the global context.
7241 ASSERT(attributes == ABSENT);
7242 attributes = NONE;
7243 context_ext = Handle<JSObject>(Top::context()->global());
7244 }
7245
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007246 // Set the property, but ignore if read_only variable on the context
7247 // extension object itself.
7248 if ((attributes & READ_ONLY) == 0 ||
7249 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007250 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
7251 if (set.is_null()) {
7252 // Failure::Exception is converted to a null handle in the
7253 // handle-based methods such as SetProperty. We therefore need
7254 // to convert null handles back to exceptions.
7255 ASSERT(Top::has_pending_exception());
7256 return Failure::Exception();
7257 }
7258 }
7259 return *value;
7260}
7261
7262
7263static Object* Runtime_Throw(Arguments args) {
7264 HandleScope scope;
7265 ASSERT(args.length() == 1);
7266
7267 return Top::Throw(args[0]);
7268}
7269
7270
7271static Object* Runtime_ReThrow(Arguments args) {
7272 HandleScope scope;
7273 ASSERT(args.length() == 1);
7274
7275 return Top::ReThrow(args[0]);
7276}
7277
7278
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007279static Object* Runtime_PromoteScheduledException(Arguments args) {
7280 ASSERT_EQ(0, args.length());
7281 return Top::PromoteScheduledException();
7282}
7283
7284
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007285static Object* Runtime_ThrowReferenceError(Arguments args) {
7286 HandleScope scope;
7287 ASSERT(args.length() == 1);
7288
7289 Handle<Object> name(args[0]);
7290 Handle<Object> reference_error =
7291 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
7292 return Top::Throw(*reference_error);
7293}
7294
7295
7296static Object* Runtime_StackOverflow(Arguments args) {
7297 NoHandleAllocation na;
7298 return Top::StackOverflow();
7299}
7300
7301
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007302static Object* Runtime_StackGuard(Arguments args) {
7303 ASSERT(args.length() == 1);
7304
7305 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007306 if (StackGuard::IsStackOverflow()) {
7307 return Runtime_StackOverflow(args);
7308 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007309
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007310 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007311}
7312
7313
7314// NOTE: These PrintXXX functions are defined for all builds (not just
7315// DEBUG builds) because we may want to be able to trace function
7316// calls in all modes.
7317static void PrintString(String* str) {
7318 // not uncommon to have empty strings
7319 if (str->length() > 0) {
7320 SmartPointer<char> s =
7321 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
7322 PrintF("%s", *s);
7323 }
7324}
7325
7326
7327static void PrintObject(Object* obj) {
7328 if (obj->IsSmi()) {
7329 PrintF("%d", Smi::cast(obj)->value());
7330 } else if (obj->IsString() || obj->IsSymbol()) {
7331 PrintString(String::cast(obj));
7332 } else if (obj->IsNumber()) {
7333 PrintF("%g", obj->Number());
7334 } else if (obj->IsFailure()) {
7335 PrintF("<failure>");
7336 } else if (obj->IsUndefined()) {
7337 PrintF("<undefined>");
7338 } else if (obj->IsNull()) {
7339 PrintF("<null>");
7340 } else if (obj->IsTrue()) {
7341 PrintF("<true>");
7342 } else if (obj->IsFalse()) {
7343 PrintF("<false>");
7344 } else {
7345 PrintF("%p", obj);
7346 }
7347}
7348
7349
7350static int StackSize() {
7351 int n = 0;
7352 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
7353 return n;
7354}
7355
7356
7357static void PrintTransition(Object* result) {
7358 // indentation
7359 { const int nmax = 80;
7360 int n = StackSize();
7361 if (n <= nmax)
7362 PrintF("%4d:%*s", n, n, "");
7363 else
7364 PrintF("%4d:%*s", n, nmax, "...");
7365 }
7366
7367 if (result == NULL) {
7368 // constructor calls
7369 JavaScriptFrameIterator it;
7370 JavaScriptFrame* frame = it.frame();
7371 if (frame->IsConstructor()) PrintF("new ");
7372 // function name
7373 Object* fun = frame->function();
7374 if (fun->IsJSFunction()) {
7375 PrintObject(JSFunction::cast(fun)->shared()->name());
7376 } else {
7377 PrintObject(fun);
7378 }
7379 // function arguments
7380 // (we are intentionally only printing the actually
7381 // supplied parameters, not all parameters required)
7382 PrintF("(this=");
7383 PrintObject(frame->receiver());
7384 const int length = frame->GetProvidedParametersCount();
7385 for (int i = 0; i < length; i++) {
7386 PrintF(", ");
7387 PrintObject(frame->GetParameter(i));
7388 }
7389 PrintF(") {\n");
7390
7391 } else {
7392 // function result
7393 PrintF("} -> ");
7394 PrintObject(result);
7395 PrintF("\n");
7396 }
7397}
7398
7399
7400static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007401 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007402 NoHandleAllocation ha;
7403 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007404 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007405}
7406
7407
7408static Object* Runtime_TraceExit(Arguments args) {
7409 NoHandleAllocation ha;
7410 PrintTransition(args[0]);
7411 return args[0]; // return TOS
7412}
7413
7414
7415static Object* Runtime_DebugPrint(Arguments args) {
7416 NoHandleAllocation ha;
7417 ASSERT(args.length() == 1);
7418
7419#ifdef DEBUG
7420 if (args[0]->IsString()) {
7421 // If we have a string, assume it's a code "marker"
7422 // and print some interesting cpu debugging info.
7423 JavaScriptFrameIterator it;
7424 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007425 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
7426 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007427 } else {
7428 PrintF("DebugPrint: ");
7429 }
7430 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007431 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007432 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007433 HeapObject::cast(args[0])->map()->Print();
7434 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007435#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007436 // ShortPrint is available in release mode. Print is not.
7437 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007438#endif
7439 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00007440 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007441
7442 return args[0]; // return TOS
7443}
7444
7445
7446static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007447 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007448 NoHandleAllocation ha;
7449 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007450 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007451}
7452
7453
mads.s.ager31e71382008-08-13 09:32:07 +00007454static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007455 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007456 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007457
7458 // According to ECMA-262, section 15.9.1, page 117, the precision of
7459 // the number in a Date object representing a particular instant in
7460 // time is milliseconds. Therefore, we floor the result of getting
7461 // the OS time.
7462 double millis = floor(OS::TimeCurrentMillis());
7463 return Heap::NumberFromDouble(millis);
7464}
7465
7466
7467static Object* Runtime_DateParseString(Arguments args) {
7468 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007469 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007470
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007471 CONVERT_ARG_CHECKED(String, str, 0);
7472 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007473
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007474 CONVERT_ARG_CHECKED(JSArray, output, 1);
7475 RUNTIME_ASSERT(output->HasFastElements());
7476
7477 AssertNoAllocation no_allocation;
7478
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007479 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007480 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
7481 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007482 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007483 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007484 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007485 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007486 result = DateParser::Parse(str->ToUC16Vector(), output_array);
7487 }
7488
7489 if (result) {
7490 return *output;
7491 } else {
7492 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007493 }
7494}
7495
7496
7497static Object* Runtime_DateLocalTimezone(Arguments args) {
7498 NoHandleAllocation ha;
7499 ASSERT(args.length() == 1);
7500
7501 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007502 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007503 return Heap::AllocateStringFromUtf8(CStrVector(zone));
7504}
7505
7506
7507static Object* Runtime_DateLocalTimeOffset(Arguments args) {
7508 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007509 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007510
7511 return Heap::NumberFromDouble(OS::LocalTimeOffset());
7512}
7513
7514
7515static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
7516 NoHandleAllocation ha;
7517 ASSERT(args.length() == 1);
7518
7519 CONVERT_DOUBLE_CHECKED(x, args[0]);
7520 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
7521}
7522
7523
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007524static Object* Runtime_GlobalReceiver(Arguments args) {
7525 ASSERT(args.length() == 1);
7526 Object* global = args[0];
7527 if (!global->IsJSGlobalObject()) return Heap::null_value();
7528 return JSGlobalObject::cast(global)->global_receiver();
7529}
7530
7531
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007532static Object* Runtime_CompileString(Arguments args) {
7533 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007534 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007535 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007536 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007537
ager@chromium.org381abbb2009-02-25 13:23:22 +00007538 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007539 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007540 Compiler::ValidationState validate = (is_json->IsTrue())
7541 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007542 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
7543 context,
7544 true,
7545 validate);
7546 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007547 Handle<JSFunction> fun =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007548 Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007549 return *fun;
7550}
7551
7552
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007553static ObjectPair CompileGlobalEval(Handle<String> source,
7554 Handle<Object> receiver) {
7555 // Deal with a normal eval call with a string argument. Compile it
7556 // and return the compiled function bound in the local context.
7557 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
7558 source,
7559 Handle<Context>(Top::context()),
7560 Top::context()->IsGlobalContext(),
7561 Compiler::DONT_VALIDATE_JSON);
7562 if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
7563 Handle<JSFunction> compiled = Factory::NewFunctionFromSharedFunctionInfo(
7564 shared,
7565 Handle<Context>(Top::context()),
7566 NOT_TENURED);
7567 return MakePair(*compiled, *receiver);
7568}
7569
7570
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007571static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
7572 ASSERT(args.length() == 3);
7573 if (!args[0]->IsJSFunction()) {
7574 return MakePair(Top::ThrowIllegalOperation(), NULL);
7575 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007576
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007577 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007578 Handle<JSFunction> callee = args.at<JSFunction>(0);
7579 Handle<Object> receiver; // Will be overwritten.
7580
7581 // Compute the calling context.
7582 Handle<Context> context = Handle<Context>(Top::context());
7583#ifdef DEBUG
7584 // Make sure Top::context() agrees with the old code that traversed
7585 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007586 StackFrameLocator locator;
7587 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007588 ASSERT(Context::cast(frame->context()) == *context);
7589#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007590
7591 // Find where the 'eval' symbol is bound. It is unaliased only if
7592 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007593 int index = -1;
7594 PropertyAttributes attributes = ABSENT;
7595 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007596 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
7597 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007598 // Stop search when eval is found or when the global context is
7599 // reached.
7600 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007601 if (context->is_function_context()) {
7602 context = Handle<Context>(Context::cast(context->closure()->context()));
7603 } else {
7604 context = Handle<Context>(context->previous());
7605 }
7606 }
7607
iposva@chromium.org245aa852009-02-10 00:49:54 +00007608 // If eval could not be resolved, it has been deleted and we need to
7609 // throw a reference error.
7610 if (attributes == ABSENT) {
7611 Handle<Object> name = Factory::eval_symbol();
7612 Handle<Object> reference_error =
7613 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007614 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007615 }
7616
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007617 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007618 // 'eval' is not bound in the global context. Just call the function
7619 // with the given arguments. This is not necessarily the global eval.
7620 if (receiver->IsContext()) {
7621 context = Handle<Context>::cast(receiver);
7622 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007623 } else if (receiver->IsJSContextExtensionObject()) {
7624 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007625 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007626 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007627 }
7628
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007629 // 'eval' is bound in the global context, but it may have been overwritten.
7630 // Compare it to the builtin 'GlobalEval' function to make sure.
7631 if (*callee != Top::global_context()->global_eval_fun() ||
7632 !args[1]->IsString()) {
7633 return MakePair(*callee, Top::context()->global()->global_receiver());
7634 }
7635
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007636 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
7637}
7638
7639
7640static ObjectPair Runtime_ResolvePossiblyDirectEvalNoLookup(Arguments args) {
7641 ASSERT(args.length() == 3);
7642 if (!args[0]->IsJSFunction()) {
7643 return MakePair(Top::ThrowIllegalOperation(), NULL);
7644 }
7645
7646 HandleScope scope;
7647 Handle<JSFunction> callee = args.at<JSFunction>(0);
7648
7649 // 'eval' is bound in the global context, but it may have been overwritten.
7650 // Compare it to the builtin 'GlobalEval' function to make sure.
7651 if (*callee != Top::global_context()->global_eval_fun() ||
7652 !args[1]->IsString()) {
7653 return MakePair(*callee, Top::context()->global()->global_receiver());
7654 }
7655
7656 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007657}
7658
7659
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007660static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
7661 // This utility adjusts the property attributes for newly created Function
7662 // object ("new Function(...)") by changing the map.
7663 // All it does is changing the prototype property to enumerable
7664 // as specified in ECMA262, 15.3.5.2.
7665 HandleScope scope;
7666 ASSERT(args.length() == 1);
7667 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7668 ASSERT(func->map()->instance_type() ==
7669 Top::function_instance_map()->instance_type());
7670 ASSERT(func->map()->instance_size() ==
7671 Top::function_instance_map()->instance_size());
7672 func->set_map(*Top::function_instance_map());
7673 return *func;
7674}
7675
7676
lrn@chromium.orgc4e51ac2010-08-09 09:47:21 +00007677static Object* Runtime_AllocateInNewSpace(Arguments args) {
7678 // Allocate a block of memory in NewSpace (filled with a filler).
7679 // Use as fallback for allocation in generated code when NewSpace
7680 // is full.
7681 ASSERT(args.length() == 1);
7682 CONVERT_ARG_CHECKED(Smi, size_smi, 0);
7683 int size = size_smi->value();
7684 RUNTIME_ASSERT(IsAligned(size, kPointerSize));
7685 RUNTIME_ASSERT(size > 0);
7686 static const int kMinFreeNewSpaceAfterGC =
7687 Heap::InitialSemiSpaceSize() * 3/4;
7688 RUNTIME_ASSERT(size <= kMinFreeNewSpaceAfterGC);
7689 Object* allocation = Heap::new_space()->AllocateRaw(size);
7690 if (!allocation->IsFailure()) {
7691 Heap::CreateFillerObjectAt(HeapObject::cast(allocation)->address(), size);
7692 }
7693 return allocation;
7694}
7695
7696
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007697// Push an array unto an array of arrays if it is not already in the
7698// array. Returns true if the element was pushed on the stack and
7699// false otherwise.
7700static Object* Runtime_PushIfAbsent(Arguments args) {
7701 ASSERT(args.length() == 2);
7702 CONVERT_CHECKED(JSArray, array, args[0]);
7703 CONVERT_CHECKED(JSArray, element, args[1]);
7704 RUNTIME_ASSERT(array->HasFastElements());
7705 int length = Smi::cast(array->length())->value();
7706 FixedArray* elements = FixedArray::cast(array->elements());
7707 for (int i = 0; i < length; i++) {
7708 if (elements->get(i) == element) return Heap::false_value();
7709 }
7710 Object* obj = array->SetFastElement(length, element);
7711 if (obj->IsFailure()) return obj;
7712 return Heap::true_value();
7713}
7714
7715
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007716/**
7717 * A simple visitor visits every element of Array's.
7718 * The backend storage can be a fixed array for fast elements case,
7719 * or a dictionary for sparse array. Since Dictionary is a subtype
7720 * of FixedArray, the class can be used by both fast and slow cases.
7721 * The second parameter of the constructor, fast_elements, specifies
7722 * whether the storage is a FixedArray or Dictionary.
7723 *
7724 * An index limit is used to deal with the situation that a result array
7725 * length overflows 32-bit non-negative integer.
7726 */
7727class ArrayConcatVisitor {
7728 public:
7729 ArrayConcatVisitor(Handle<FixedArray> storage,
7730 uint32_t index_limit,
7731 bool fast_elements) :
7732 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007733 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007734
7735 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007736 if (i >= index_limit_ - index_offset_) return;
7737 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007738
7739 if (fast_elements_) {
7740 ASSERT(index < static_cast<uint32_t>(storage_->length()));
7741 storage_->set(index, *elm);
7742
7743 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007744 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
7745 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007746 Factory::DictionaryAtNumberPut(dict, index, elm);
7747 if (!result.is_identical_to(dict))
7748 storage_ = result;
7749 }
7750 }
7751
7752 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007753 if (index_limit_ - index_offset_ < delta) {
7754 index_offset_ = index_limit_;
7755 } else {
7756 index_offset_ += delta;
7757 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007758 }
7759
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007760 Handle<FixedArray> storage() { return storage_; }
7761
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007762 private:
7763 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007764 // Limit on the accepted indices. Elements with indices larger than the
7765 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007766 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007767 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007768 uint32_t index_offset_;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007769 const bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007770};
7771
7772
ager@chromium.org3811b432009-10-28 14:53:37 +00007773template<class ExternalArrayClass, class ElementType>
7774static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
7775 bool elements_are_ints,
7776 bool elements_are_guaranteed_smis,
7777 uint32_t range,
7778 ArrayConcatVisitor* visitor) {
7779 Handle<ExternalArrayClass> array(
7780 ExternalArrayClass::cast(receiver->elements()));
7781 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
7782
7783 if (visitor != NULL) {
7784 if (elements_are_ints) {
7785 if (elements_are_guaranteed_smis) {
7786 for (uint32_t j = 0; j < len; j++) {
7787 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
7788 visitor->visit(j, e);
7789 }
7790 } else {
7791 for (uint32_t j = 0; j < len; j++) {
7792 int64_t val = static_cast<int64_t>(array->get(j));
7793 if (Smi::IsValid(static_cast<intptr_t>(val))) {
7794 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
7795 visitor->visit(j, e);
7796 } else {
7797 Handle<Object> e(
7798 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
7799 visitor->visit(j, e);
7800 }
7801 }
7802 }
7803 } else {
7804 for (uint32_t j = 0; j < len; j++) {
7805 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
7806 visitor->visit(j, e);
7807 }
7808 }
7809 }
7810
7811 return len;
7812}
7813
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007814/**
7815 * A helper function that visits elements of a JSObject. Only elements
7816 * whose index between 0 and range (exclusive) are visited.
7817 *
7818 * If the third parameter, visitor, is not NULL, the visitor is called
7819 * with parameters, 'visitor_index_offset + element index' and the element.
7820 *
7821 * It returns the number of visisted elements.
7822 */
7823static uint32_t IterateElements(Handle<JSObject> receiver,
7824 uint32_t range,
7825 ArrayConcatVisitor* visitor) {
7826 uint32_t num_of_elements = 0;
7827
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007828 switch (receiver->GetElementsKind()) {
7829 case JSObject::FAST_ELEMENTS: {
7830 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
7831 uint32_t len = elements->length();
7832 if (range < len) {
7833 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007834 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007835
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007836 for (uint32_t j = 0; j < len; j++) {
7837 Handle<Object> e(elements->get(j));
7838 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007839 num_of_elements++;
7840 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007841 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007842 }
7843 }
7844 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007845 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007846 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007847 case JSObject::PIXEL_ELEMENTS: {
7848 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
7849 uint32_t len = pixels->length();
7850 if (range < len) {
7851 len = range;
7852 }
7853
7854 for (uint32_t j = 0; j < len; j++) {
7855 num_of_elements++;
7856 if (visitor != NULL) {
7857 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
7858 visitor->visit(j, e);
7859 }
7860 }
7861 break;
7862 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007863 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
7864 num_of_elements =
7865 IterateExternalArrayElements<ExternalByteArray, int8_t>(
7866 receiver, true, true, range, visitor);
7867 break;
7868 }
7869 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7870 num_of_elements =
7871 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
7872 receiver, true, true, range, visitor);
7873 break;
7874 }
7875 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
7876 num_of_elements =
7877 IterateExternalArrayElements<ExternalShortArray, int16_t>(
7878 receiver, true, true, range, visitor);
7879 break;
7880 }
7881 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7882 num_of_elements =
7883 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
7884 receiver, true, true, range, visitor);
7885 break;
7886 }
7887 case JSObject::EXTERNAL_INT_ELEMENTS: {
7888 num_of_elements =
7889 IterateExternalArrayElements<ExternalIntArray, int32_t>(
7890 receiver, true, false, range, visitor);
7891 break;
7892 }
7893 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7894 num_of_elements =
7895 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
7896 receiver, true, false, range, visitor);
7897 break;
7898 }
7899 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
7900 num_of_elements =
7901 IterateExternalArrayElements<ExternalFloatArray, float>(
7902 receiver, false, false, range, visitor);
7903 break;
7904 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007905 case JSObject::DICTIONARY_ELEMENTS: {
7906 Handle<NumberDictionary> dict(receiver->element_dictionary());
7907 uint32_t capacity = dict->Capacity();
7908 for (uint32_t j = 0; j < capacity; j++) {
7909 Handle<Object> k(dict->KeyAt(j));
7910 if (dict->IsKey(*k)) {
7911 ASSERT(k->IsNumber());
7912 uint32_t index = static_cast<uint32_t>(k->Number());
7913 if (index < range) {
7914 num_of_elements++;
7915 if (visitor) {
7916 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
7917 }
7918 }
7919 }
7920 }
7921 break;
7922 }
7923 default:
7924 UNREACHABLE();
7925 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007926 }
7927
7928 return num_of_elements;
7929}
7930
7931
7932/**
7933 * A helper function that visits elements of an Array object, and elements
7934 * on its prototypes.
7935 *
7936 * Elements on prototypes are visited first, and only elements whose indices
7937 * less than Array length are visited.
7938 *
7939 * If a ArrayConcatVisitor object is given, the visitor is called with
7940 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007941 *
7942 * The returned number of elements is an upper bound on the actual number
7943 * of elements added. If the same element occurs in more than one object
7944 * in the array's prototype chain, it will be counted more than once, but
7945 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007946 */
7947static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
7948 ArrayConcatVisitor* visitor) {
7949 uint32_t range = static_cast<uint32_t>(array->length()->Number());
7950 Handle<Object> obj = array;
7951
7952 static const int kEstimatedPrototypes = 3;
7953 List< Handle<JSObject> > objects(kEstimatedPrototypes);
7954
7955 // Visit prototype first. If an element on the prototype is shadowed by
7956 // the inheritor using the same index, the ArrayConcatVisitor visits
7957 // the prototype element before the shadowing element.
7958 // The visitor can simply overwrite the old value by new value using
7959 // the same index. This follows Array::concat semantics.
7960 while (!obj->IsNull()) {
7961 objects.Add(Handle<JSObject>::cast(obj));
7962 obj = Handle<Object>(obj->GetPrototype());
7963 }
7964
7965 uint32_t nof_elements = 0;
7966 for (int i = objects.length() - 1; i >= 0; i--) {
7967 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007968 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007969 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007970
7971 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
7972 nof_elements = JSObject::kMaxElementCount;
7973 } else {
7974 nof_elements += encountered_elements;
7975 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007976 }
7977
7978 return nof_elements;
7979}
7980
7981
7982/**
7983 * A helper function of Runtime_ArrayConcat.
7984 *
7985 * The first argument is an Array of arrays and objects. It is the
7986 * same as the arguments array of Array::concat JS function.
7987 *
7988 * If an argument is an Array object, the function visits array
7989 * elements. If an argument is not an Array object, the function
7990 * visits the object as if it is an one-element array.
7991 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007992 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007993 * non-negative number is used as new length. For example, if one
7994 * array length is 2^32 - 1, second array length is 1, the
7995 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007996 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
7997 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007998 */
7999static uint32_t IterateArguments(Handle<JSArray> arguments,
8000 ArrayConcatVisitor* visitor) {
8001 uint32_t visited_elements = 0;
8002 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
8003
8004 for (uint32_t i = 0; i < num_of_args; i++) {
8005 Handle<Object> obj(arguments->GetElement(i));
8006 if (obj->IsJSArray()) {
8007 Handle<JSArray> array = Handle<JSArray>::cast(obj);
8008 uint32_t len = static_cast<uint32_t>(array->length()->Number());
8009 uint32_t nof_elements =
8010 IterateArrayAndPrototypeElements(array, visitor);
8011 // Total elements of array and its prototype chain can be more than
8012 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008013 // the array length number of elements. We use the length as an estimate
8014 // for the actual number of elements added.
8015 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
8016 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
8017 visited_elements = JSArray::kMaxElementCount;
8018 } else {
8019 visited_elements += added_elements;
8020 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008021 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008022 } else {
8023 if (visitor) {
8024 visitor->visit(0, obj);
8025 visitor->increase_index_offset(1);
8026 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008027 if (visited_elements < JSArray::kMaxElementCount) {
8028 visited_elements++;
8029 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008030 }
8031 }
8032 return visited_elements;
8033}
8034
8035
8036/**
8037 * Array::concat implementation.
8038 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008039 * TODO(lrn): Fix non-compliance for very large concatenations and update to
8040 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008041 */
8042static Object* Runtime_ArrayConcat(Arguments args) {
8043 ASSERT(args.length() == 1);
8044 HandleScope handle_scope;
8045
8046 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
8047 Handle<JSArray> arguments(arg_arrays);
8048
8049 // Pass 1: estimate the number of elements of the result
8050 // (it could be more than real numbers if prototype has elements).
8051 uint32_t result_length = 0;
8052 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
8053
8054 { AssertNoAllocation nogc;
8055 for (uint32_t i = 0; i < num_of_args; i++) {
8056 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008057 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008058 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008059 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008060 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
8061 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008062 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008063 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008064 if (JSObject::kMaxElementCount - result_length < length_estimate) {
8065 result_length = JSObject::kMaxElementCount;
8066 break;
8067 }
8068 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008069 }
8070 }
8071
8072 // Allocate an empty array, will set length and content later.
8073 Handle<JSArray> result = Factory::NewJSArray(0);
8074
8075 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
8076 // If estimated number of elements is more than half of length, a
8077 // fixed array (fast case) is more time and space-efficient than a
8078 // dictionary.
8079 bool fast_case = (estimate_nof_elements * 2) >= result_length;
8080
8081 Handle<FixedArray> storage;
8082 if (fast_case) {
8083 // The backing storage array must have non-existing elements to
8084 // preserve holes across concat operations.
8085 storage = Factory::NewFixedArrayWithHoles(result_length);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00008086 result->set_map(*Factory::GetFastElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008087 } else {
8088 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
8089 uint32_t at_least_space_for = estimate_nof_elements +
8090 (estimate_nof_elements >> 2);
8091 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008092 Factory::NewNumberDictionary(at_least_space_for));
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00008093 result->set_map(*Factory::GetSlowElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008094 }
8095
8096 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
8097
8098 ArrayConcatVisitor visitor(storage, result_length, fast_case);
8099
8100 IterateArguments(arguments, &visitor);
8101
8102 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00008103 // Please note the storage might have changed in the visitor.
8104 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008105
8106 return *result;
8107}
8108
8109
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008110// This will not allocate (flatten the string), but it may run
8111// very slowly for very deeply nested ConsStrings. For debugging use only.
8112static Object* Runtime_GlobalPrint(Arguments args) {
8113 NoHandleAllocation ha;
8114 ASSERT(args.length() == 1);
8115
8116 CONVERT_CHECKED(String, string, args[0]);
8117 StringInputBuffer buffer(string);
8118 while (buffer.has_more()) {
8119 uint16_t character = buffer.GetNext();
8120 PrintF("%c", character);
8121 }
8122 return string;
8123}
8124
ager@chromium.org5ec48922009-05-05 07:25:34 +00008125// Moves all own elements of an object, that are below a limit, to positions
8126// starting at zero. All undefined values are placed after non-undefined values,
8127// and are followed by non-existing element. Does not change the length
8128// property.
8129// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008130static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00008131 ASSERT(args.length() == 2);
8132 CONVERT_CHECKED(JSObject, object, args[0]);
8133 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
8134 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008135}
8136
8137
8138// Move contents of argument 0 (an array) to argument 1 (an array)
8139static Object* Runtime_MoveArrayContents(Arguments args) {
8140 ASSERT(args.length() == 2);
8141 CONVERT_CHECKED(JSArray, from, args[0]);
8142 CONVERT_CHECKED(JSArray, to, args[1]);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00008143 HeapObject* new_elements = from->elements();
8144 Object* new_map;
ricow@chromium.org0b9f8502010-08-18 07:45:01 +00008145 if (new_elements->map() == Heap::fixed_array_map() ||
8146 new_elements->map() == Heap::fixed_cow_array_map()) {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00008147 new_map = to->map()->GetFastElementsMap();
8148 } else {
8149 new_map = to->map()->GetSlowElementsMap();
8150 }
8151 if (new_map->IsFailure()) return new_map;
8152 to->set_map(Map::cast(new_map));
8153 to->set_elements(new_elements);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008154 to->set_length(from->length());
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00008155 Object* obj = from->ResetElements();
8156 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008157 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008158 return to;
8159}
8160
8161
8162// How many elements does this array have?
8163static Object* Runtime_EstimateNumberOfElements(Arguments args) {
8164 ASSERT(args.length() == 1);
8165 CONVERT_CHECKED(JSArray, array, args[0]);
8166 HeapObject* elements = array->elements();
8167 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008168 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008169 } else {
8170 return array->length();
8171 }
8172}
8173
8174
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008175static Object* Runtime_SwapElements(Arguments args) {
8176 HandleScope handle_scope;
8177
8178 ASSERT_EQ(3, args.length());
8179
ager@chromium.orgac091b72010-05-05 07:34:42 +00008180 CONVERT_ARG_CHECKED(JSObject, object, 0);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008181 Handle<Object> key1 = args.at<Object>(1);
8182 Handle<Object> key2 = args.at<Object>(2);
8183
8184 uint32_t index1, index2;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008185 if (!key1->ToArrayIndex(&index1)
8186 || !key2->ToArrayIndex(&index2)) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00008187 return Top::ThrowIllegalOperation();
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008188 }
8189
ager@chromium.orgac091b72010-05-05 07:34:42 +00008190 Handle<JSObject> jsobject = Handle<JSObject>::cast(object);
8191 Handle<Object> tmp1 = GetElement(jsobject, index1);
8192 Handle<Object> tmp2 = GetElement(jsobject, index2);
8193
8194 SetElement(jsobject, index1, tmp2);
8195 SetElement(jsobject, index2, tmp1);
8196
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008197 return Heap::undefined_value();
8198}
8199
8200
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008201// Returns an array that tells you where in the [0, length) interval an array
8202// might have elements. Can either return keys or intervals. Keys can have
8203// gaps in (undefined). Intervals can also span over some undefined keys.
8204static Object* Runtime_GetArrayKeys(Arguments args) {
8205 ASSERT(args.length() == 2);
8206 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00008207 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008208 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008209 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008210 // Create an array and get all the keys into it, then remove all the
8211 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008212 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008213 int keys_length = keys->length();
8214 for (int i = 0; i < keys_length; i++) {
8215 Object* key = keys->get(i);
8216 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008217 if (!key->ToArrayIndex(&index) || index >= length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008218 // Zap invalid keys.
8219 keys->set_undefined(i);
8220 }
8221 }
8222 return *Factory::NewJSArrayWithElements(keys);
8223 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008224 ASSERT(array->HasFastElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008225 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
8226 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008227 single_interval->set(0, Smi::FromInt(-1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008228 uint32_t actual_length =
8229 static_cast<uint32_t>(FixedArray::cast(array->elements())->length());
ager@chromium.org5ec48922009-05-05 07:25:34 +00008230 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008231 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00008232 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008233 single_interval->set(1, *length_object);
8234 return *Factory::NewJSArrayWithElements(single_interval);
8235 }
8236}
8237
8238
8239// DefineAccessor takes an optional final argument which is the
8240// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
8241// to the way accessors are implemented, it is set for both the getter
8242// and setter on the first call to DefineAccessor and ignored on
8243// subsequent calls.
8244static Object* Runtime_DefineAccessor(Arguments args) {
8245 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
8246 // Compute attributes.
8247 PropertyAttributes attributes = NONE;
8248 if (args.length() == 5) {
8249 CONVERT_CHECKED(Smi, attrs, args[4]);
8250 int value = attrs->value();
8251 // Only attribute bits should be set.
8252 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
8253 attributes = static_cast<PropertyAttributes>(value);
8254 }
8255
8256 CONVERT_CHECKED(JSObject, obj, args[0]);
8257 CONVERT_CHECKED(String, name, args[1]);
8258 CONVERT_CHECKED(Smi, flag, args[2]);
8259 CONVERT_CHECKED(JSFunction, fun, args[3]);
8260 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
8261}
8262
8263
8264static Object* Runtime_LookupAccessor(Arguments args) {
8265 ASSERT(args.length() == 3);
8266 CONVERT_CHECKED(JSObject, obj, args[0]);
8267 CONVERT_CHECKED(String, name, args[1]);
8268 CONVERT_CHECKED(Smi, flag, args[2]);
8269 return obj->LookupAccessor(name, flag->value() == 0);
8270}
8271
8272
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008273#ifdef ENABLE_DEBUGGER_SUPPORT
8274static Object* Runtime_DebugBreak(Arguments args) {
8275 ASSERT(args.length() == 0);
8276 return Execution::DebugBreakHelper();
8277}
8278
8279
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008280// Helper functions for wrapping and unwrapping stack frame ids.
8281static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008282 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008283 return Smi::FromInt(id >> 2);
8284}
8285
8286
8287static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
8288 return static_cast<StackFrame::Id>(wrapped->value() << 2);
8289}
8290
8291
8292// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00008293// args[0]: debug event listener function to set or null or undefined for
8294// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008295// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00008296static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008297 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00008298 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
8299 args[0]->IsUndefined() ||
8300 args[0]->IsNull());
8301 Handle<Object> callback = args.at<Object>(0);
8302 Handle<Object> data = args.at<Object>(1);
8303 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008304
8305 return Heap::undefined_value();
8306}
8307
8308
8309static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00008310 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008311 StackGuard::DebugBreak();
8312 return Heap::undefined_value();
8313}
8314
8315
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008316static Object* DebugLookupResultValue(Object* receiver, String* name,
8317 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00008318 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008319 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008320 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008321 case NORMAL:
8322 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008323 if (value->IsTheHole()) {
8324 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008325 }
8326 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008327 case FIELD:
8328 value =
8329 JSObject::cast(
8330 result->holder())->FastPropertyAt(result->GetFieldIndex());
8331 if (value->IsTheHole()) {
8332 return Heap::undefined_value();
8333 }
8334 return value;
8335 case CONSTANT_FUNCTION:
8336 return result->GetConstantFunction();
8337 case CALLBACKS: {
8338 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008339 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008340 value = receiver->GetPropertyWithCallback(
8341 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00008342 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008343 value = Top::pending_exception();
8344 Top::clear_pending_exception();
8345 if (caught_exception != NULL) {
8346 *caught_exception = true;
8347 }
8348 }
8349 return value;
8350 } else {
8351 return Heap::undefined_value();
8352 }
8353 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008354 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008355 case MAP_TRANSITION:
8356 case CONSTANT_TRANSITION:
8357 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008358 return Heap::undefined_value();
8359 default:
8360 UNREACHABLE();
8361 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008362 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008363 return Heap::undefined_value();
8364}
8365
8366
ager@chromium.org32912102009-01-16 10:38:43 +00008367// Get debugger related details for an object property.
8368// args[0]: object holding property
8369// args[1]: name of the property
8370//
8371// The array returned contains the following information:
8372// 0: Property value
8373// 1: Property details
8374// 2: Property value is exception
8375// 3: Getter function if defined
8376// 4: Setter function if defined
8377// Items 2-4 are only filled if the property has either a getter or a setter
8378// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00008379static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008380 HandleScope scope;
8381
8382 ASSERT(args.length() == 2);
8383
8384 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8385 CONVERT_ARG_CHECKED(String, name, 1);
8386
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008387 // Make sure to set the current context to the context before the debugger was
8388 // entered (if the debugger is entered). The reason for switching context here
8389 // is that for some property lookups (accessors and interceptors) callbacks
8390 // into the embedding application can occour, and the embedding application
8391 // could have the assumption that its own global context is the current
8392 // context and not some internal debugger context.
8393 SaveContext save;
8394 if (Debug::InDebugger()) {
8395 Top::set_context(*Debug::debugger_entry()->GetContext());
8396 }
8397
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008398 // Skip the global proxy as it has no properties and always delegates to the
8399 // real global object.
8400 if (obj->IsJSGlobalProxy()) {
8401 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
8402 }
8403
8404
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008405 // Check if the name is trivially convertible to an index and get the element
8406 // if so.
8407 uint32_t index;
8408 if (name->AsArrayIndex(&index)) {
8409 Handle<FixedArray> details = Factory::NewFixedArray(2);
8410 details->set(0, Runtime::GetElementOrCharAt(obj, index));
8411 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
8412 return *Factory::NewJSArrayWithElements(details);
8413 }
8414
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008415 // Find the number of objects making up this.
8416 int length = LocalPrototypeChainLength(*obj);
8417
8418 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008419 Handle<JSObject> jsproto = obj;
8420 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008421 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008422 jsproto->LocalLookup(*name, &result);
8423 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008424 // LookupResult is not GC safe as it holds raw object pointers.
8425 // GC can happen later in this code so put the required fields into
8426 // local variables using handles when required for later use.
8427 PropertyType result_type = result.type();
8428 Handle<Object> result_callback_obj;
8429 if (result_type == CALLBACKS) {
8430 result_callback_obj = Handle<Object>(result.GetCallbackObject());
8431 }
8432 Smi* property_details = result.GetPropertyDetails().AsSmi();
8433 // DebugLookupResultValue can cause GC so details from LookupResult needs
8434 // to be copied to handles before this.
8435 bool caught_exception = false;
8436 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
8437 &caught_exception);
8438 if (raw_value->IsFailure()) return raw_value;
8439 Handle<Object> value(raw_value);
8440
8441 // If the callback object is a fixed array then it contains JavaScript
8442 // getter and/or setter.
8443 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
8444 result_callback_obj->IsFixedArray();
8445 Handle<FixedArray> details =
8446 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
8447 details->set(0, *value);
8448 details->set(1, property_details);
8449 if (hasJavaScriptAccessors) {
8450 details->set(2,
8451 caught_exception ? Heap::true_value()
8452 : Heap::false_value());
8453 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
8454 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
8455 }
8456
8457 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008458 }
8459 if (i < length - 1) {
8460 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
8461 }
8462 }
8463
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008464 return Heap::undefined_value();
8465}
8466
8467
8468static Object* Runtime_DebugGetProperty(Arguments args) {
8469 HandleScope scope;
8470
8471 ASSERT(args.length() == 2);
8472
8473 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8474 CONVERT_ARG_CHECKED(String, name, 1);
8475
8476 LookupResult result;
8477 obj->Lookup(*name, &result);
8478 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008479 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008480 }
8481 return Heap::undefined_value();
8482}
8483
8484
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008485// Return the property type calculated from the property details.
8486// args[0]: smi with property details.
8487static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
8488 ASSERT(args.length() == 1);
8489 CONVERT_CHECKED(Smi, details, args[0]);
8490 PropertyType type = PropertyDetails(details).type();
8491 return Smi::FromInt(static_cast<int>(type));
8492}
8493
8494
8495// Return the property attribute calculated from the property details.
8496// args[0]: smi with property details.
8497static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
8498 ASSERT(args.length() == 1);
8499 CONVERT_CHECKED(Smi, details, args[0]);
8500 PropertyAttributes attributes = PropertyDetails(details).attributes();
8501 return Smi::FromInt(static_cast<int>(attributes));
8502}
8503
8504
8505// Return the property insertion index calculated from the property details.
8506// args[0]: smi with property details.
8507static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
8508 ASSERT(args.length() == 1);
8509 CONVERT_CHECKED(Smi, details, args[0]);
8510 int index = PropertyDetails(details).index();
8511 return Smi::FromInt(index);
8512}
8513
8514
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008515// Return property value from named interceptor.
8516// args[0]: object
8517// args[1]: property name
8518static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
8519 HandleScope scope;
8520 ASSERT(args.length() == 2);
8521 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8522 RUNTIME_ASSERT(obj->HasNamedInterceptor());
8523 CONVERT_ARG_CHECKED(String, name, 1);
8524
8525 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008526 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008527}
8528
8529
8530// Return element value from indexed interceptor.
8531// args[0]: object
8532// args[1]: index
8533static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
8534 HandleScope scope;
8535 ASSERT(args.length() == 2);
8536 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8537 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
8538 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
8539
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008540 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008541}
8542
8543
8544static Object* Runtime_CheckExecutionState(Arguments args) {
8545 ASSERT(args.length() >= 1);
8546 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00008547 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008548 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008549 return Top::Throw(Heap::illegal_execution_state_symbol());
8550 }
8551
8552 return Heap::true_value();
8553}
8554
8555
8556static Object* Runtime_GetFrameCount(Arguments args) {
8557 HandleScope scope;
8558 ASSERT(args.length() == 1);
8559
8560 // Check arguments.
8561 Object* result = Runtime_CheckExecutionState(args);
8562 if (result->IsFailure()) return result;
8563
8564 // Count all frames which are relevant to debugging stack trace.
8565 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008566 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008567 if (id == StackFrame::NO_ID) {
8568 // If there is no JavaScript stack frame count is 0.
8569 return Smi::FromInt(0);
8570 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008571 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
8572 return Smi::FromInt(n);
8573}
8574
8575
8576static const int kFrameDetailsFrameIdIndex = 0;
8577static const int kFrameDetailsReceiverIndex = 1;
8578static const int kFrameDetailsFunctionIndex = 2;
8579static const int kFrameDetailsArgumentCountIndex = 3;
8580static const int kFrameDetailsLocalCountIndex = 4;
8581static const int kFrameDetailsSourcePositionIndex = 5;
8582static const int kFrameDetailsConstructCallIndex = 6;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008583static const int kFrameDetailsAtReturnIndex = 7;
8584static const int kFrameDetailsDebuggerFrameIndex = 8;
8585static const int kFrameDetailsFirstDynamicIndex = 9;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008586
8587// Return an array with frame details
8588// args[0]: number: break id
8589// args[1]: number: frame index
8590//
8591// The array returned contains the following information:
8592// 0: Frame id
8593// 1: Receiver
8594// 2: Function
8595// 3: Argument count
8596// 4: Local count
8597// 5: Source position
8598// 6: Constructor call
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008599// 7: Is at return
8600// 8: Debugger frame
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008601// Arguments name, value
8602// Locals name, value
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008603// Return value if any
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008604static Object* Runtime_GetFrameDetails(Arguments args) {
8605 HandleScope scope;
8606 ASSERT(args.length() == 2);
8607
8608 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008609 Object* check = Runtime_CheckExecutionState(args);
8610 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008611 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8612
8613 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008614 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008615 if (id == StackFrame::NO_ID) {
8616 // If there are no JavaScript stack frames return undefined.
8617 return Heap::undefined_value();
8618 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008619 int count = 0;
8620 JavaScriptFrameIterator it(id);
8621 for (; !it.done(); it.Advance()) {
8622 if (count == index) break;
8623 count++;
8624 }
8625 if (it.done()) return Heap::undefined_value();
8626
8627 // Traverse the saved contexts chain to find the active context for the
8628 // selected frame.
8629 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008630 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008631 save = save->prev();
8632 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008633 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008634
8635 // Get the frame id.
8636 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
8637
8638 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008639 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008640
8641 // Check for constructor frame.
8642 bool constructor = it.frame()->IsConstructor();
8643
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008644 // Get scope info and read from it for local variable information.
8645 Handle<JSFunction> function(JSFunction::cast(it.frame()->function()));
ager@chromium.orgb5737492010-07-15 09:29:43 +00008646 Handle<SerializedScopeInfo> scope_info(function->shared()->scope_info());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008647 ScopeInfo<> info(*scope_info);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008648
8649 // Get the context.
8650 Handle<Context> context(Context::cast(it.frame()->context()));
8651
8652 // Get the locals names and values into a temporary array.
8653 //
8654 // TODO(1240907): Hide compiler-introduced stack variables
8655 // (e.g. .result)? For users of the debugger, they will probably be
8656 // confusing.
8657 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
8658 for (int i = 0; i < info.NumberOfLocals(); i++) {
8659 // Name of the local.
8660 locals->set(i * 2, *info.LocalName(i));
8661
8662 // Fetch the value of the local - either from the stack or from a
8663 // heap-allocated context.
8664 if (i < info.number_of_stack_slots()) {
8665 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
8666 } else {
8667 Handle<String> name = info.LocalName(i);
8668 // Traverse the context chain to the function context as all local
8669 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008670 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008671 context = Handle<Context>(context->previous());
8672 }
8673 ASSERT(context->is_function_context());
8674 locals->set(i * 2 + 1,
ager@chromium.orgb5737492010-07-15 09:29:43 +00008675 context->get(scope_info->ContextSlotIndex(*name, NULL)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008676 }
8677 }
8678
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008679 // Check whether this frame is positioned at return.
8680 int at_return = (index == 0) ? Debug::IsBreakAtReturn(it.frame()) : false;
8681
8682 // If positioned just before return find the value to be returned and add it
8683 // to the frame information.
8684 Handle<Object> return_value = Factory::undefined_value();
8685 if (at_return) {
8686 StackFrameIterator it2;
8687 Address internal_frame_sp = NULL;
8688 while (!it2.done()) {
8689 if (it2.frame()->is_internal()) {
8690 internal_frame_sp = it2.frame()->sp();
8691 } else {
8692 if (it2.frame()->is_java_script()) {
8693 if (it2.frame()->id() == it.frame()->id()) {
8694 // The internal frame just before the JavaScript frame contains the
8695 // value to return on top. A debug break at return will create an
8696 // internal frame to store the return value (eax/rax/r0) before
8697 // entering the debug break exit frame.
8698 if (internal_frame_sp != NULL) {
8699 return_value =
8700 Handle<Object>(Memory::Object_at(internal_frame_sp));
8701 break;
8702 }
8703 }
8704 }
8705
8706 // Indicate that the previous frame was not an internal frame.
8707 internal_frame_sp = NULL;
8708 }
8709 it2.Advance();
8710 }
8711 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008712
8713 // Now advance to the arguments adapter frame (if any). It contains all
8714 // the provided parameters whereas the function frame always have the number
8715 // of arguments matching the functions parameters. The rest of the
8716 // information (except for what is collected above) is the same.
8717 it.AdvanceToArgumentsFrame();
8718
8719 // Find the number of arguments to fill. At least fill the number of
8720 // parameters for the function and fill more if more parameters are provided.
8721 int argument_count = info.number_of_parameters();
8722 if (argument_count < it.frame()->GetProvidedParametersCount()) {
8723 argument_count = it.frame()->GetProvidedParametersCount();
8724 }
8725
8726 // Calculate the size of the result.
8727 int details_size = kFrameDetailsFirstDynamicIndex +
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008728 2 * (argument_count + info.NumberOfLocals()) +
8729 (at_return ? 1 : 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008730 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8731
8732 // Add the frame id.
8733 details->set(kFrameDetailsFrameIdIndex, *frame_id);
8734
8735 // Add the function (same as in function frame).
8736 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
8737
8738 // Add the arguments count.
8739 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
8740
8741 // Add the locals count
8742 details->set(kFrameDetailsLocalCountIndex,
8743 Smi::FromInt(info.NumberOfLocals()));
8744
8745 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00008746 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008747 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
8748 } else {
8749 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
8750 }
8751
8752 // Add the constructor information.
8753 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
8754
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008755 // Add the at return information.
8756 details->set(kFrameDetailsAtReturnIndex, Heap::ToBoolean(at_return));
8757
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008758 // Add information on whether this frame is invoked in the debugger context.
8759 details->set(kFrameDetailsDebuggerFrameIndex,
8760 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
8761
8762 // Fill the dynamic part.
8763 int details_index = kFrameDetailsFirstDynamicIndex;
8764
8765 // Add arguments name and value.
8766 for (int i = 0; i < argument_count; i++) {
8767 // Name of the argument.
8768 if (i < info.number_of_parameters()) {
8769 details->set(details_index++, *info.parameter_name(i));
8770 } else {
8771 details->set(details_index++, Heap::undefined_value());
8772 }
8773
8774 // Parameter value.
8775 if (i < it.frame()->GetProvidedParametersCount()) {
8776 details->set(details_index++, it.frame()->GetParameter(i));
8777 } else {
8778 details->set(details_index++, Heap::undefined_value());
8779 }
8780 }
8781
8782 // Add locals name and value from the temporary copy from the function frame.
8783 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
8784 details->set(details_index++, locals->get(i));
8785 }
8786
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008787 // Add the value being returned.
8788 if (at_return) {
8789 details->set(details_index++, *return_value);
8790 }
8791
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008792 // Add the receiver (same as in function frame).
8793 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
8794 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
8795 Handle<Object> receiver(it.frame()->receiver());
8796 if (!receiver->IsJSObject()) {
8797 // If the receiver is NOT a JSObject we have hit an optimization
8798 // where a value object is not converted into a wrapped JS objects.
8799 // To hide this optimization from the debugger, we wrap the receiver
8800 // by creating correct wrapper object based on the calling frame's
8801 // global context.
8802 it.Advance();
8803 Handle<Context> calling_frames_global_context(
8804 Context::cast(Context::cast(it.frame()->context())->global_context()));
8805 receiver = Factory::ToObject(receiver, calling_frames_global_context);
8806 }
8807 details->set(kFrameDetailsReceiverIndex, *receiver);
8808
8809 ASSERT_EQ(details_size, details_index);
8810 return *Factory::NewJSArrayWithElements(details);
8811}
8812
8813
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008814// Copy all the context locals into an object used to materialize a scope.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008815static void CopyContextLocalsToScopeObject(
8816 Handle<SerializedScopeInfo> serialized_scope_info,
8817 ScopeInfo<>& scope_info,
8818 Handle<Context> context,
8819 Handle<JSObject> scope_object) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008820 // Fill all context locals to the context extension.
8821 for (int i = Context::MIN_CONTEXT_SLOTS;
8822 i < scope_info.number_of_context_slots();
8823 i++) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00008824 int context_index = serialized_scope_info->ContextSlotIndex(
8825 *scope_info.context_slot_name(i), NULL);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008826
8827 // Don't include the arguments shadow (.arguments) context variable.
8828 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
8829 SetProperty(scope_object,
8830 scope_info.context_slot_name(i),
8831 Handle<Object>(context->get(context_index)), NONE);
8832 }
8833 }
8834}
8835
8836
8837// Create a plain JSObject which materializes the local scope for the specified
8838// frame.
8839static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
8840 Handle<JSFunction> function(JSFunction::cast(frame->function()));
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008841 Handle<SharedFunctionInfo> shared(function->shared());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008842 Handle<SerializedScopeInfo> serialized_scope_info(shared->scope_info());
8843 ScopeInfo<> scope_info(*serialized_scope_info);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008844
8845 // Allocate and initialize a JSObject with all the arguments, stack locals
8846 // heap locals and extension properties of the debugged function.
8847 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
8848
8849 // First fill all parameters.
8850 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8851 SetProperty(local_scope,
8852 scope_info.parameter_name(i),
8853 Handle<Object>(frame->GetParameter(i)), NONE);
8854 }
8855
8856 // Second fill all stack locals.
8857 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
8858 SetProperty(local_scope,
8859 scope_info.stack_slot_name(i),
8860 Handle<Object>(frame->GetExpression(i)), NONE);
8861 }
8862
8863 // Third fill all context locals.
8864 Handle<Context> frame_context(Context::cast(frame->context()));
8865 Handle<Context> function_context(frame_context->fcontext());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008866 CopyContextLocalsToScopeObject(serialized_scope_info, scope_info,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008867 function_context, local_scope);
8868
8869 // Finally copy any properties from the function context extension. This will
8870 // be variables introduced by eval.
8871 if (function_context->closure() == *function) {
8872 if (function_context->has_extension() &&
8873 !function_context->IsGlobalContext()) {
8874 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008875 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008876 for (int i = 0; i < keys->length(); i++) {
8877 // Names of variables introduced by eval are strings.
8878 ASSERT(keys->get(i)->IsString());
8879 Handle<String> key(String::cast(keys->get(i)));
8880 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
8881 }
8882 }
8883 }
8884 return local_scope;
8885}
8886
8887
8888// Create a plain JSObject which materializes the closure content for the
8889// context.
8890static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
8891 ASSERT(context->is_function_context());
8892
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008893 Handle<SharedFunctionInfo> shared(context->closure()->shared());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008894 Handle<SerializedScopeInfo> serialized_scope_info(shared->scope_info());
8895 ScopeInfo<> scope_info(*serialized_scope_info);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008896
8897 // Allocate and initialize a JSObject with all the content of theis function
8898 // closure.
8899 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
8900
8901 // Check whether the arguments shadow object exists.
8902 int arguments_shadow_index =
ager@chromium.orgb5737492010-07-15 09:29:43 +00008903 shared->scope_info()->ContextSlotIndex(Heap::arguments_shadow_symbol(),
8904 NULL);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008905 if (arguments_shadow_index >= 0) {
8906 // In this case all the arguments are available in the arguments shadow
8907 // object.
8908 Handle<JSObject> arguments_shadow(
8909 JSObject::cast(context->get(arguments_shadow_index)));
8910 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8911 SetProperty(closure_scope,
8912 scope_info.parameter_name(i),
8913 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
8914 }
8915 }
8916
8917 // Fill all context locals to the context extension.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008918 CopyContextLocalsToScopeObject(serialized_scope_info, scope_info,
8919 context, closure_scope);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008920
8921 // Finally copy any properties from the function context extension. This will
8922 // be variables introduced by eval.
8923 if (context->has_extension()) {
8924 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008925 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008926 for (int i = 0; i < keys->length(); i++) {
8927 // Names of variables introduced by eval are strings.
8928 ASSERT(keys->get(i)->IsString());
8929 Handle<String> key(String::cast(keys->get(i)));
8930 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
8931 }
8932 }
8933
8934 return closure_scope;
8935}
8936
8937
8938// Iterate over the actual scopes visible from a stack frame. All scopes are
8939// backed by an actual context except the local scope, which is inserted
8940// "artifically" in the context chain.
8941class ScopeIterator {
8942 public:
8943 enum ScopeType {
8944 ScopeTypeGlobal = 0,
8945 ScopeTypeLocal,
8946 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00008947 ScopeTypeClosure,
8948 // Every catch block contains an implicit with block (its parameter is
8949 // a JSContextExtensionObject) that extends current scope with a variable
8950 // holding exception object. Such with blocks are treated as scopes of their
8951 // own type.
8952 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008953 };
8954
8955 explicit ScopeIterator(JavaScriptFrame* frame)
8956 : frame_(frame),
8957 function_(JSFunction::cast(frame->function())),
8958 context_(Context::cast(frame->context())),
8959 local_done_(false),
8960 at_local_(false) {
8961
8962 // Check whether the first scope is actually a local scope.
8963 if (context_->IsGlobalContext()) {
8964 // If there is a stack slot for .result then this local scope has been
8965 // created for evaluating top level code and it is not a real local scope.
8966 // Checking for the existence of .result seems fragile, but the scope info
8967 // saved with the code object does not otherwise have that information.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008968 int index = function_->shared()->scope_info()->
8969 StackSlotIndex(Heap::result_symbol());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008970 at_local_ = index < 0;
8971 } else if (context_->is_function_context()) {
8972 at_local_ = true;
8973 }
8974 }
8975
8976 // More scopes?
8977 bool Done() { return context_.is_null(); }
8978
8979 // Move to the next scope.
8980 void Next() {
8981 // If at a local scope mark the local scope as passed.
8982 if (at_local_) {
8983 at_local_ = false;
8984 local_done_ = true;
8985
8986 // If the current context is not associated with the local scope the
8987 // current context is the next real scope, so don't move to the next
8988 // context in this case.
8989 if (context_->closure() != *function_) {
8990 return;
8991 }
8992 }
8993
8994 // The global scope is always the last in the chain.
8995 if (context_->IsGlobalContext()) {
8996 context_ = Handle<Context>();
8997 return;
8998 }
8999
9000 // Move to the next context.
9001 if (context_->is_function_context()) {
9002 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
9003 } else {
9004 context_ = Handle<Context>(context_->previous());
9005 }
9006
9007 // If passing the local scope indicate that the current scope is now the
9008 // local scope.
9009 if (!local_done_ &&
9010 (context_->IsGlobalContext() || (context_->is_function_context()))) {
9011 at_local_ = true;
9012 }
9013 }
9014
9015 // Return the type of the current scope.
9016 int Type() {
9017 if (at_local_) {
9018 return ScopeTypeLocal;
9019 }
9020 if (context_->IsGlobalContext()) {
9021 ASSERT(context_->global()->IsGlobalObject());
9022 return ScopeTypeGlobal;
9023 }
9024 if (context_->is_function_context()) {
9025 return ScopeTypeClosure;
9026 }
9027 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00009028 // Current scope is either an explicit with statement or a with statement
9029 // implicitely generated for a catch block.
9030 // If the extension object here is a JSContextExtensionObject then
9031 // current with statement is one frome a catch block otherwise it's a
9032 // regular with statement.
9033 if (context_->extension()->IsJSContextExtensionObject()) {
9034 return ScopeTypeCatch;
9035 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009036 return ScopeTypeWith;
9037 }
9038
9039 // Return the JavaScript object with the content of the current scope.
9040 Handle<JSObject> ScopeObject() {
9041 switch (Type()) {
9042 case ScopeIterator::ScopeTypeGlobal:
9043 return Handle<JSObject>(CurrentContext()->global());
9044 break;
9045 case ScopeIterator::ScopeTypeLocal:
9046 // Materialize the content of the local scope into a JSObject.
9047 return MaterializeLocalScope(frame_);
9048 break;
9049 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00009050 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009051 // Return the with object.
9052 return Handle<JSObject>(CurrentContext()->extension());
9053 break;
9054 case ScopeIterator::ScopeTypeClosure:
9055 // Materialize the content of the closure scope into a JSObject.
9056 return MaterializeClosure(CurrentContext());
9057 break;
9058 }
9059 UNREACHABLE();
9060 return Handle<JSObject>();
9061 }
9062
9063 // Return the context for this scope. For the local context there might not
9064 // be an actual context.
9065 Handle<Context> CurrentContext() {
9066 if (at_local_ && context_->closure() != *function_) {
9067 return Handle<Context>();
9068 }
9069 return context_;
9070 }
9071
9072#ifdef DEBUG
9073 // Debug print of the content of the current scope.
9074 void DebugPrint() {
9075 switch (Type()) {
9076 case ScopeIterator::ScopeTypeGlobal:
9077 PrintF("Global:\n");
9078 CurrentContext()->Print();
9079 break;
9080
9081 case ScopeIterator::ScopeTypeLocal: {
9082 PrintF("Local:\n");
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009083 ScopeInfo<> scope_info(function_->shared()->scope_info());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009084 scope_info.Print();
9085 if (!CurrentContext().is_null()) {
9086 CurrentContext()->Print();
9087 if (CurrentContext()->has_extension()) {
9088 Handle<JSObject> extension =
9089 Handle<JSObject>(CurrentContext()->extension());
9090 if (extension->IsJSContextExtensionObject()) {
9091 extension->Print();
9092 }
9093 }
9094 }
9095 break;
9096 }
9097
9098 case ScopeIterator::ScopeTypeWith: {
9099 PrintF("With:\n");
9100 Handle<JSObject> extension =
9101 Handle<JSObject>(CurrentContext()->extension());
9102 extension->Print();
9103 break;
9104 }
9105
ager@chromium.orga1645e22009-09-09 19:27:10 +00009106 case ScopeIterator::ScopeTypeCatch: {
9107 PrintF("Catch:\n");
9108 Handle<JSObject> extension =
9109 Handle<JSObject>(CurrentContext()->extension());
9110 extension->Print();
9111 break;
9112 }
9113
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009114 case ScopeIterator::ScopeTypeClosure: {
9115 PrintF("Closure:\n");
9116 CurrentContext()->Print();
9117 if (CurrentContext()->has_extension()) {
9118 Handle<JSObject> extension =
9119 Handle<JSObject>(CurrentContext()->extension());
9120 if (extension->IsJSContextExtensionObject()) {
9121 extension->Print();
9122 }
9123 }
9124 break;
9125 }
9126
9127 default:
9128 UNREACHABLE();
9129 }
9130 PrintF("\n");
9131 }
9132#endif
9133
9134 private:
9135 JavaScriptFrame* frame_;
9136 Handle<JSFunction> function_;
9137 Handle<Context> context_;
9138 bool local_done_;
9139 bool at_local_;
9140
9141 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
9142};
9143
9144
9145static Object* Runtime_GetScopeCount(Arguments args) {
9146 HandleScope scope;
9147 ASSERT(args.length() == 2);
9148
9149 // Check arguments.
9150 Object* check = Runtime_CheckExecutionState(args);
9151 if (check->IsFailure()) return check;
9152 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9153
9154 // Get the frame where the debugging is performed.
9155 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9156 JavaScriptFrameIterator it(id);
9157 JavaScriptFrame* frame = it.frame();
9158
9159 // Count the visible scopes.
9160 int n = 0;
9161 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
9162 n++;
9163 }
9164
9165 return Smi::FromInt(n);
9166}
9167
9168
9169static const int kScopeDetailsTypeIndex = 0;
9170static const int kScopeDetailsObjectIndex = 1;
9171static const int kScopeDetailsSize = 2;
9172
9173// Return an array with scope details
9174// args[0]: number: break id
9175// args[1]: number: frame index
9176// args[2]: number: scope index
9177//
9178// The array returned contains the following information:
9179// 0: Scope type
9180// 1: Scope object
9181static Object* Runtime_GetScopeDetails(Arguments args) {
9182 HandleScope scope;
9183 ASSERT(args.length() == 3);
9184
9185 // Check arguments.
9186 Object* check = Runtime_CheckExecutionState(args);
9187 if (check->IsFailure()) return check;
9188 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9189 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
9190
9191 // Get the frame where the debugging is performed.
9192 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9193 JavaScriptFrameIterator frame_it(id);
9194 JavaScriptFrame* frame = frame_it.frame();
9195
9196 // Find the requested scope.
9197 int n = 0;
9198 ScopeIterator it(frame);
9199 for (; !it.Done() && n < index; it.Next()) {
9200 n++;
9201 }
9202 if (it.Done()) {
9203 return Heap::undefined_value();
9204 }
9205
9206 // Calculate the size of the result.
9207 int details_size = kScopeDetailsSize;
9208 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
9209
9210 // Fill in scope details.
9211 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
9212 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
9213
9214 return *Factory::NewJSArrayWithElements(details);
9215}
9216
9217
9218static Object* Runtime_DebugPrintScopes(Arguments args) {
9219 HandleScope scope;
9220 ASSERT(args.length() == 0);
9221
9222#ifdef DEBUG
9223 // Print the scopes for the top frame.
9224 StackFrameLocator locator;
9225 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
9226 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
9227 it.DebugPrint();
9228 }
9229#endif
9230 return Heap::undefined_value();
9231}
9232
9233
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009234static Object* Runtime_GetCFrames(Arguments args) {
9235 HandleScope scope;
9236 ASSERT(args.length() == 1);
9237 Object* result = Runtime_CheckExecutionState(args);
9238 if (result->IsFailure()) return result;
9239
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00009240#if V8_HOST_ARCH_64_BIT
9241 UNIMPLEMENTED();
9242 return Heap::undefined_value();
9243#else
9244
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009245 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009246 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
9247 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009248 if (frames_count == OS::kStackWalkError) {
9249 return Heap::undefined_value();
9250 }
9251
9252 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
9253 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
9254 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
9255 for (int i = 0; i < frames_count; i++) {
9256 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
9257 frame_value->SetProperty(
9258 *address_str,
9259 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
9260 NONE);
9261
9262 // Get the stack walk text for this frame.
9263 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009264 int frame_text_length = StrLength(frames[i].text);
9265 if (frame_text_length > 0) {
9266 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009267 frame_text = Factory::NewStringFromAscii(str);
9268 }
9269
9270 if (!frame_text.is_null()) {
9271 frame_value->SetProperty(*text_str, *frame_text, NONE);
9272 }
9273
9274 frames_array->set(i, *frame_value);
9275 }
9276 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00009277#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009278}
9279
9280
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00009281static Object* Runtime_GetThreadCount(Arguments args) {
9282 HandleScope scope;
9283 ASSERT(args.length() == 1);
9284
9285 // Check arguments.
9286 Object* result = Runtime_CheckExecutionState(args);
9287 if (result->IsFailure()) return result;
9288
9289 // Count all archived V8 threads.
9290 int n = 0;
9291 for (ThreadState* thread = ThreadState::FirstInUse();
9292 thread != NULL;
9293 thread = thread->Next()) {
9294 n++;
9295 }
9296
9297 // Total number of threads is current thread and archived threads.
9298 return Smi::FromInt(n + 1);
9299}
9300
9301
9302static const int kThreadDetailsCurrentThreadIndex = 0;
9303static const int kThreadDetailsThreadIdIndex = 1;
9304static const int kThreadDetailsSize = 2;
9305
9306// Return an array with thread details
9307// args[0]: number: break id
9308// args[1]: number: thread index
9309//
9310// The array returned contains the following information:
9311// 0: Is current thread?
9312// 1: Thread id
9313static Object* Runtime_GetThreadDetails(Arguments args) {
9314 HandleScope scope;
9315 ASSERT(args.length() == 2);
9316
9317 // Check arguments.
9318 Object* check = Runtime_CheckExecutionState(args);
9319 if (check->IsFailure()) return check;
9320 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
9321
9322 // Allocate array for result.
9323 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
9324
9325 // Thread index 0 is current thread.
9326 if (index == 0) {
9327 // Fill the details.
9328 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
9329 details->set(kThreadDetailsThreadIdIndex,
9330 Smi::FromInt(ThreadManager::CurrentId()));
9331 } else {
9332 // Find the thread with the requested index.
9333 int n = 1;
9334 ThreadState* thread = ThreadState::FirstInUse();
9335 while (index != n && thread != NULL) {
9336 thread = thread->Next();
9337 n++;
9338 }
9339 if (thread == NULL) {
9340 return Heap::undefined_value();
9341 }
9342
9343 // Fill the details.
9344 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
9345 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
9346 }
9347
9348 // Convert to JS array and return.
9349 return *Factory::NewJSArrayWithElements(details);
9350}
9351
9352
whesse@chromium.orge90029b2010-08-02 11:52:17 +00009353// Sets the disable break state
9354// args[0]: disable break state
9355static Object* Runtime_SetDisableBreak(Arguments args) {
9356 HandleScope scope;
9357 ASSERT(args.length() == 1);
9358 CONVERT_BOOLEAN_CHECKED(disable_break, args[0]);
9359 Debug::set_disable_break(disable_break);
9360 return Heap::undefined_value();
9361}
9362
9363
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009364static Object* Runtime_GetBreakLocations(Arguments args) {
9365 HandleScope scope;
9366 ASSERT(args.length() == 1);
9367
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009368 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9369 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009370 // Find the number of break points
9371 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
9372 if (break_locations->IsUndefined()) return Heap::undefined_value();
9373 // Return array as JS array
9374 return *Factory::NewJSArrayWithElements(
9375 Handle<FixedArray>::cast(break_locations));
9376}
9377
9378
9379// Set a break point in a function
9380// args[0]: function
9381// args[1]: number: break source position (within the function source)
9382// args[2]: number: break point object
9383static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
9384 HandleScope scope;
9385 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009386 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9387 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009388 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9389 RUNTIME_ASSERT(source_position >= 0);
9390 Handle<Object> break_point_object_arg = args.at<Object>(2);
9391
9392 // Set break point.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009393 Debug::SetBreakPoint(shared, break_point_object_arg, &source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009394
lrn@chromium.org32d961d2010-06-30 09:09:34 +00009395 return Smi::FromInt(source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009396}
9397
9398
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009399Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
9400 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009401 // Iterate the heap looking for SharedFunctionInfo generated from the
9402 // script. The inner most SharedFunctionInfo containing the source position
9403 // for the requested break point is found.
9404 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
9405 // which is found is not compiled it is compiled and the heap is iterated
9406 // again as the compilation might create inner functions from the newly
9407 // compiled function and the actual requested break point might be in one of
9408 // these functions.
9409 bool done = false;
9410 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00009411 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009412 Handle<SharedFunctionInfo> target;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009413 while (!done) {
9414 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009415 for (HeapObject* obj = iterator.next();
9416 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009417 if (obj->IsSharedFunctionInfo()) {
9418 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
9419 if (shared->script() == *script) {
9420 // If the SharedFunctionInfo found has the requested script data and
9421 // contains the source position it is a candidate.
9422 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00009423 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009424 start_position = shared->start_position();
9425 }
9426 if (start_position <= position &&
9427 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00009428 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009429 // candidate this is the new candidate.
9430 if (target.is_null()) {
9431 target_start_position = start_position;
9432 target = shared;
9433 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00009434 if (target_start_position == start_position &&
9435 shared->end_position() == target->end_position()) {
9436 // If a top-level function contain only one function
9437 // declartion the source for the top-level and the function is
9438 // the same. In that case prefer the non top-level function.
9439 if (!shared->is_toplevel()) {
9440 target_start_position = start_position;
9441 target = shared;
9442 }
9443 } else if (target_start_position <= start_position &&
9444 shared->end_position() <= target->end_position()) {
9445 // This containment check includes equality as a function inside
9446 // a top-level function can share either start or end position
9447 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009448 target_start_position = start_position;
9449 target = shared;
9450 }
9451 }
9452 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009453 }
9454 }
9455 }
9456
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009457 if (target.is_null()) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009458 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009459 }
9460
9461 // If the candidate found is compiled we are done. NOTE: when lazy
9462 // compilation of inner functions is introduced some additional checking
9463 // needs to be done here to compile inner functions.
9464 done = target->is_compiled();
9465 if (!done) {
9466 // If the candidate is not compiled compile it to reveal any inner
9467 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009468 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009469 }
9470 }
9471
9472 return *target;
9473}
9474
9475
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009476// Changes the state of a break point in a script and returns source position
9477// where break point was set. NOTE: Regarding performance see the NOTE for
9478// GetScriptFromScriptData.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009479// args[0]: script to set break point in
9480// args[1]: number: break source position (within the script source)
9481// args[2]: number: break point object
9482static Object* Runtime_SetScriptBreakPoint(Arguments args) {
9483 HandleScope scope;
9484 ASSERT(args.length() == 3);
9485 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
9486 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9487 RUNTIME_ASSERT(source_position >= 0);
9488 Handle<Object> break_point_object_arg = args.at<Object>(2);
9489
9490 // Get the script from the script wrapper.
9491 RUNTIME_ASSERT(wrapper->value()->IsScript());
9492 Handle<Script> script(Script::cast(wrapper->value()));
9493
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009494 Object* result = Runtime::FindSharedFunctionInfoInScript(
9495 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009496 if (!result->IsUndefined()) {
9497 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
9498 // Find position within function. The script position might be before the
9499 // source position of the first function.
9500 int position;
9501 if (shared->start_position() > source_position) {
9502 position = 0;
9503 } else {
9504 position = source_position - shared->start_position();
9505 }
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009506 Debug::SetBreakPoint(shared, break_point_object_arg, &position);
9507 position += shared->start_position();
9508 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009509 }
9510 return Heap::undefined_value();
9511}
9512
9513
9514// Clear a break point
9515// args[0]: number: break point object
9516static Object* Runtime_ClearBreakPoint(Arguments args) {
9517 HandleScope scope;
9518 ASSERT(args.length() == 1);
9519 Handle<Object> break_point_object_arg = args.at<Object>(0);
9520
9521 // Clear break point.
9522 Debug::ClearBreakPoint(break_point_object_arg);
9523
9524 return Heap::undefined_value();
9525}
9526
9527
9528// Change the state of break on exceptions
9529// args[0]: boolean indicating uncaught exceptions
9530// args[1]: boolean indicating on/off
9531static Object* Runtime_ChangeBreakOnException(Arguments args) {
9532 HandleScope scope;
9533 ASSERT(args.length() == 2);
9534 ASSERT(args[0]->IsNumber());
9535 ASSERT(args[1]->IsBoolean());
9536
9537 // Update break point state
9538 ExceptionBreakType type =
9539 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
9540 bool enable = args[1]->ToBoolean()->IsTrue();
9541 Debug::ChangeBreakOnException(type, enable);
9542 return Heap::undefined_value();
9543}
9544
9545
9546// Prepare for stepping
9547// args[0]: break id for checking execution state
9548// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00009549// args[2]: number of times to perform the step, for step out it is the number
9550// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009551static Object* Runtime_PrepareStep(Arguments args) {
9552 HandleScope scope;
9553 ASSERT(args.length() == 3);
9554 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009555 Object* check = Runtime_CheckExecutionState(args);
9556 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009557 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
9558 return Top::Throw(Heap::illegal_argument_symbol());
9559 }
9560
9561 // Get the step action and check validity.
9562 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
9563 if (step_action != StepIn &&
9564 step_action != StepNext &&
9565 step_action != StepOut &&
9566 step_action != StepInMin &&
9567 step_action != StepMin) {
9568 return Top::Throw(Heap::illegal_argument_symbol());
9569 }
9570
9571 // Get the number of steps.
9572 int step_count = NumberToInt32(args[2]);
9573 if (step_count < 1) {
9574 return Top::Throw(Heap::illegal_argument_symbol());
9575 }
9576
ager@chromium.orga1645e22009-09-09 19:27:10 +00009577 // Clear all current stepping setup.
9578 Debug::ClearStepping();
9579
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009580 // Prepare step.
9581 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
9582 return Heap::undefined_value();
9583}
9584
9585
9586// Clear all stepping set by PrepareStep.
9587static Object* Runtime_ClearStepping(Arguments args) {
9588 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009589 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009590 Debug::ClearStepping();
9591 return Heap::undefined_value();
9592}
9593
9594
9595// Creates a copy of the with context chain. The copy of the context chain is
9596// is linked to the function context supplied.
9597static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
9598 Handle<Context> function_context) {
9599 // At the bottom of the chain. Return the function context to link to.
9600 if (context_chain->is_function_context()) {
9601 return function_context;
9602 }
9603
9604 // Recursively copy the with contexts.
9605 Handle<Context> previous(context_chain->previous());
9606 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
9607 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009608 CopyWithContextChain(function_context, previous),
9609 extension,
9610 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009611}
9612
9613
9614// Helper function to find or create the arguments object for
9615// Runtime_DebugEvaluate.
9616static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
9617 Handle<JSFunction> function,
ager@chromium.orgb5737492010-07-15 09:29:43 +00009618 Handle<SerializedScopeInfo> scope_info,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009619 const ScopeInfo<>* sinfo,
9620 Handle<Context> function_context) {
9621 // Try to find the value of 'arguments' to pass as parameter. If it is not
9622 // found (that is the debugged function does not reference 'arguments' and
9623 // does not support eval) then create an 'arguments' object.
9624 int index;
9625 if (sinfo->number_of_stack_slots() > 0) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00009626 index = scope_info->StackSlotIndex(Heap::arguments_symbol());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009627 if (index != -1) {
9628 return Handle<Object>(frame->GetExpression(index));
9629 }
9630 }
9631
9632 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00009633 index = scope_info->ContextSlotIndex(Heap::arguments_symbol(), NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009634 if (index != -1) {
9635 return Handle<Object>(function_context->get(index));
9636 }
9637 }
9638
9639 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009640 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
9641 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009642
9643 AssertNoAllocation no_gc;
9644 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009645 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009646 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009647 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009648 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009649 return arguments;
9650}
9651
9652
9653// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00009654// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009655// extension part has all the parameters and locals of the function on the
9656// stack frame. A function which calls eval with the code to evaluate is then
9657// compiled in this context and called in this context. As this context
9658// replaces the context of the function on the stack frame a new (empty)
9659// function is created as well to be used as the closure for the context.
9660// This function and the context acts as replacements for the function on the
9661// stack frame presenting the same view of the values of parameters and
9662// local variables as if the piece of JavaScript was evaluated at the point
9663// where the function on the stack frame is currently stopped.
9664static Object* Runtime_DebugEvaluate(Arguments args) {
9665 HandleScope scope;
9666
9667 // Check the execution state and decode arguments frame and source to be
9668 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009669 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009670 Object* check_result = Runtime_CheckExecutionState(args);
9671 if (check_result->IsFailure()) return check_result;
9672 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9673 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009674 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
9675
9676 // Handle the processing of break.
9677 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009678
9679 // Get the frame where the debugging is performed.
9680 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9681 JavaScriptFrameIterator it(id);
9682 JavaScriptFrame* frame = it.frame();
9683 Handle<JSFunction> function(JSFunction::cast(frame->function()));
ager@chromium.orgb5737492010-07-15 09:29:43 +00009684 Handle<SerializedScopeInfo> scope_info(function->shared()->scope_info());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009685 ScopeInfo<> sinfo(*scope_info);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009686
9687 // Traverse the saved contexts chain to find the active context for the
9688 // selected frame.
9689 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009690 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009691 save = save->prev();
9692 }
9693 ASSERT(save != NULL);
9694 SaveContext savex;
9695 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009696
9697 // Create the (empty) function replacing the function on the stack frame for
9698 // the purpose of evaluating in the context created below. It is important
9699 // that this function does not describe any parameters and local variables
9700 // in the context. If it does then this will cause problems with the lookup
9701 // in Context::Lookup, where context slots for parameters and local variables
9702 // are looked at before the extension object.
9703 Handle<JSFunction> go_between =
9704 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
9705 go_between->set_context(function->context());
9706#ifdef DEBUG
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009707 ScopeInfo<> go_between_sinfo(go_between->shared()->scope_info());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009708 ASSERT(go_between_sinfo.number_of_parameters() == 0);
9709 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
9710#endif
9711
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009712 // Materialize the content of the local scope into a JSObject.
9713 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009714
9715 // Allocate a new context for the debug evaluation and set the extension
9716 // object build.
9717 Handle<Context> context =
9718 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009719 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009720 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009721 Handle<Context> frame_context(Context::cast(frame->context()));
9722 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009723 context = CopyWithContextChain(frame_context, context);
9724
9725 // Wrap the evaluation statement in a new function compiled in the newly
9726 // created context. The function has one parameter which has to be called
9727 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00009728 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009729 // function(arguments,__source__) {return eval(__source__);}
9730 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00009731 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009732 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009733 Handle<String> function_source =
9734 Factory::NewStringFromAscii(Vector<const char>(source_str,
9735 source_str_length));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009736 Handle<SharedFunctionInfo> shared =
ager@chromium.org381abbb2009-02-25 13:23:22 +00009737 Compiler::CompileEval(function_source,
9738 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00009739 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00009740 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009741 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009742 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009743 Factory::NewFunctionFromSharedFunctionInfo(shared, context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009744
9745 // Invoke the result of the compilation to get the evaluation function.
9746 bool has_pending_exception;
9747 Handle<Object> receiver(frame->receiver());
9748 Handle<Object> evaluation_function =
9749 Execution::Call(compiled_function, receiver, 0, NULL,
9750 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009751 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009752
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009753 Handle<Object> arguments = GetArgumentsObject(frame, function, scope_info,
9754 &sinfo, function_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009755
9756 // Invoke the evaluation function and return the result.
9757 const int argc = 2;
9758 Object** argv[argc] = { arguments.location(),
9759 Handle<Object>::cast(source).location() };
9760 Handle<Object> result =
9761 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
9762 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009763 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009764
9765 // Skip the global proxy as it has no properties and always delegates to the
9766 // real global object.
9767 if (result->IsJSGlobalProxy()) {
9768 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
9769 }
9770
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009771 return *result;
9772}
9773
9774
9775static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
9776 HandleScope scope;
9777
9778 // Check the execution state and decode arguments frame and source to be
9779 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009780 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009781 Object* check_result = Runtime_CheckExecutionState(args);
9782 if (check_result->IsFailure()) return check_result;
9783 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009784 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
9785
9786 // Handle the processing of break.
9787 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009788
9789 // Enter the top context from before the debugger was invoked.
9790 SaveContext save;
9791 SaveContext* top = &save;
9792 while (top != NULL && *top->context() == *Debug::debug_context()) {
9793 top = top->prev();
9794 }
9795 if (top != NULL) {
9796 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009797 }
9798
9799 // Get the global context now set to the top context from before the
9800 // debugger was invoked.
9801 Handle<Context> context = Top::global_context();
9802
9803 // Compile the source to be evaluated.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009804 Handle<SharedFunctionInfo> shared =
9805 Compiler::CompileEval(source,
9806 context,
9807 true,
9808 Compiler::DONT_VALIDATE_JSON);
9809 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009810 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009811 Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
9812 context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009813
9814 // Invoke the result of the compilation to get the evaluation function.
9815 bool has_pending_exception;
9816 Handle<Object> receiver = Top::global();
9817 Handle<Object> result =
9818 Execution::Call(compiled_function, receiver, 0, NULL,
9819 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009820 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009821 return *result;
9822}
9823
9824
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009825static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
9826 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009827 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009828
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009829 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009830 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009831
9832 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009833 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00009834 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
9835 // Get the script wrapper in a local handle before calling GetScriptWrapper,
9836 // because using
9837 // instances->set(i, *GetScriptWrapper(script))
9838 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
9839 // already have deferenced the instances handle.
9840 Handle<JSValue> wrapper = GetScriptWrapper(script);
9841 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009842 }
9843
9844 // Return result as a JS array.
9845 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
9846 Handle<JSArray>::cast(result)->SetContent(*instances);
9847 return *result;
9848}
9849
9850
9851// Helper function used by Runtime_DebugReferencedBy below.
9852static int DebugReferencedBy(JSObject* target,
9853 Object* instance_filter, int max_references,
9854 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009855 JSFunction* arguments_function) {
9856 NoHandleAllocation ha;
9857 AssertNoAllocation no_alloc;
9858
9859 // Iterate the heap.
9860 int count = 0;
9861 JSObject* last = NULL;
9862 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009863 HeapObject* heap_obj = NULL;
9864 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009865 (max_references == 0 || count < max_references)) {
9866 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009867 if (heap_obj->IsJSObject()) {
9868 // Skip context extension objects and argument arrays as these are
9869 // checked in the context of functions using them.
9870 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00009871 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009872 obj->map()->constructor() == arguments_function) {
9873 continue;
9874 }
9875
9876 // Check if the JS object has a reference to the object looked for.
9877 if (obj->ReferencesObject(target)) {
9878 // Check instance filter if supplied. This is normally used to avoid
9879 // references from mirror objects (see Runtime_IsInPrototypeChain).
9880 if (!instance_filter->IsUndefined()) {
9881 Object* V = obj;
9882 while (true) {
9883 Object* prototype = V->GetPrototype();
9884 if (prototype->IsNull()) {
9885 break;
9886 }
9887 if (instance_filter == prototype) {
9888 obj = NULL; // Don't add this object.
9889 break;
9890 }
9891 V = prototype;
9892 }
9893 }
9894
9895 if (obj != NULL) {
9896 // Valid reference found add to instance array if supplied an update
9897 // count.
9898 if (instances != NULL && count < instances_size) {
9899 instances->set(count, obj);
9900 }
9901 last = obj;
9902 count++;
9903 }
9904 }
9905 }
9906 }
9907
9908 // Check for circular reference only. This can happen when the object is only
9909 // referenced from mirrors and has a circular reference in which case the
9910 // object is not really alive and would have been garbage collected if not
9911 // referenced from the mirror.
9912 if (count == 1 && last == target) {
9913 count = 0;
9914 }
9915
9916 // Return the number of referencing objects found.
9917 return count;
9918}
9919
9920
9921// Scan the heap for objects with direct references to an object
9922// args[0]: the object to find references to
9923// args[1]: constructor function for instances to exclude (Mirror)
9924// args[2]: the the maximum number of objects to return
9925static Object* Runtime_DebugReferencedBy(Arguments args) {
9926 ASSERT(args.length() == 3);
9927
9928 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009929 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009930
9931 // Check parameters.
9932 CONVERT_CHECKED(JSObject, target, args[0]);
9933 Object* instance_filter = args[1];
9934 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
9935 instance_filter->IsJSObject());
9936 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
9937 RUNTIME_ASSERT(max_references >= 0);
9938
9939 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009940 JSObject* arguments_boilerplate =
9941 Top::context()->global_context()->arguments_boilerplate();
9942 JSFunction* arguments_function =
9943 JSFunction::cast(arguments_boilerplate->map()->constructor());
9944
9945 // Get the number of referencing objects.
9946 int count;
9947 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009948 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009949
9950 // Allocate an array to hold the result.
9951 Object* object = Heap::AllocateFixedArray(count);
9952 if (object->IsFailure()) return object;
9953 FixedArray* instances = FixedArray::cast(object);
9954
9955 // Fill the referencing objects.
9956 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009957 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009958
9959 // Return result as JS array.
9960 Object* result =
9961 Heap::AllocateJSObject(
9962 Top::context()->global_context()->array_function());
9963 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9964 return result;
9965}
9966
9967
9968// Helper function used by Runtime_DebugConstructedBy below.
9969static int DebugConstructedBy(JSFunction* constructor, int max_references,
9970 FixedArray* instances, int instances_size) {
9971 AssertNoAllocation no_alloc;
9972
9973 // Iterate the heap.
9974 int count = 0;
9975 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009976 HeapObject* heap_obj = NULL;
9977 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009978 (max_references == 0 || count < max_references)) {
9979 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009980 if (heap_obj->IsJSObject()) {
9981 JSObject* obj = JSObject::cast(heap_obj);
9982 if (obj->map()->constructor() == constructor) {
9983 // Valid reference found add to instance array if supplied an update
9984 // count.
9985 if (instances != NULL && count < instances_size) {
9986 instances->set(count, obj);
9987 }
9988 count++;
9989 }
9990 }
9991 }
9992
9993 // Return the number of referencing objects found.
9994 return count;
9995}
9996
9997
9998// Scan the heap for objects constructed by a specific function.
9999// args[0]: the constructor to find instances of
10000// args[1]: the the maximum number of objects to return
10001static Object* Runtime_DebugConstructedBy(Arguments args) {
10002 ASSERT(args.length() == 2);
10003
10004 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +000010005 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010006
10007 // Check parameters.
10008 CONVERT_CHECKED(JSFunction, constructor, args[0]);
10009 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
10010 RUNTIME_ASSERT(max_references >= 0);
10011
10012 // Get the number of referencing objects.
10013 int count;
10014 count = DebugConstructedBy(constructor, max_references, NULL, 0);
10015
10016 // Allocate an array to hold the result.
10017 Object* object = Heap::AllocateFixedArray(count);
10018 if (object->IsFailure()) return object;
10019 FixedArray* instances = FixedArray::cast(object);
10020
10021 // Fill the referencing objects.
10022 count = DebugConstructedBy(constructor, max_references, instances, count);
10023
10024 // Return result as JS array.
10025 Object* result =
10026 Heap::AllocateJSObject(
10027 Top::context()->global_context()->array_function());
10028 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
10029 return result;
10030}
10031
10032
ager@chromium.orgddb913d2009-01-27 10:01:48 +000010033// Find the effective prototype object as returned by __proto__.
10034// args[0]: the object to find the prototype for.
10035static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010036 ASSERT(args.length() == 1);
10037
10038 CONVERT_CHECKED(JSObject, obj, args[0]);
10039
ager@chromium.orgddb913d2009-01-27 10:01:48 +000010040 // Use the __proto__ accessor.
10041 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010042}
10043
10044
10045static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +000010046 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010047 CPU::DebugBreak();
10048 return Heap::undefined_value();
10049}
10050
10051
ager@chromium.org18ad94b2009-09-02 08:22:29 +000010052static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010053#ifdef DEBUG
10054 HandleScope scope;
10055 ASSERT(args.length() == 1);
10056 // Get the function and make sure it is compiled.
10057 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010058 Handle<SharedFunctionInfo> shared(func->shared());
10059 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010060 return Failure::Exception();
10061 }
10062 func->code()->PrintLn();
10063#endif // DEBUG
10064 return Heap::undefined_value();
10065}
ager@chromium.org9085a012009-05-11 19:22:57 +000010066
10067
ager@chromium.org18ad94b2009-09-02 08:22:29 +000010068static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
10069#ifdef DEBUG
10070 HandleScope scope;
10071 ASSERT(args.length() == 1);
10072 // Get the function and make sure it is compiled.
10073 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010074 Handle<SharedFunctionInfo> shared(func->shared());
10075 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +000010076 return Failure::Exception();
10077 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010078 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +000010079#endif // DEBUG
10080 return Heap::undefined_value();
10081}
10082
10083
ager@chromium.org9085a012009-05-11 19:22:57 +000010084static Object* Runtime_FunctionGetInferredName(Arguments args) {
10085 NoHandleAllocation ha;
10086 ASSERT(args.length() == 1);
10087
10088 CONVERT_CHECKED(JSFunction, f, args[0]);
10089 return f->shared()->inferred_name();
10090}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010091
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010092
10093static int FindSharedFunctionInfosForScript(Script* script,
10094 FixedArray* buffer) {
10095 AssertNoAllocation no_allocations;
10096
10097 int counter = 0;
10098 int buffer_size = buffer->length();
10099 HeapIterator iterator;
10100 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
10101 ASSERT(obj != NULL);
10102 if (!obj->IsSharedFunctionInfo()) {
10103 continue;
10104 }
10105 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
10106 if (shared->script() != script) {
10107 continue;
10108 }
10109 if (counter < buffer_size) {
10110 buffer->set(counter, shared);
10111 }
10112 counter++;
10113 }
10114 return counter;
10115}
10116
10117// For a script finds all SharedFunctionInfo's in the heap that points
10118// to this script. Returns JSArray of SharedFunctionInfo wrapped
10119// in OpaqueReferences.
10120static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
10121 Arguments args) {
10122 ASSERT(args.length() == 1);
10123 HandleScope scope;
10124 CONVERT_CHECKED(JSValue, script_value, args[0]);
10125
10126 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
10127
10128 const int kBufferSize = 32;
10129
10130 Handle<FixedArray> array;
10131 array = Factory::NewFixedArray(kBufferSize);
10132 int number = FindSharedFunctionInfosForScript(*script, *array);
10133 if (number > kBufferSize) {
10134 array = Factory::NewFixedArray(number);
10135 FindSharedFunctionInfosForScript(*script, *array);
10136 }
10137
10138 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
10139 result->set_length(Smi::FromInt(number));
10140
10141 LiveEdit::WrapSharedFunctionInfos(result);
10142
10143 return *result;
10144}
10145
10146// For a script calculates compilation information about all its functions.
10147// The script source is explicitly specified by the second argument.
10148// The source of the actual script is not used, however it is important that
10149// all generated code keeps references to this particular instance of script.
10150// Returns a JSArray of compilation infos. The array is ordered so that
10151// each function with all its descendant is always stored in a continues range
10152// with the function itself going first. The root function is a script function.
10153static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
10154 ASSERT(args.length() == 2);
10155 HandleScope scope;
10156 CONVERT_CHECKED(JSValue, script, args[0]);
10157 CONVERT_ARG_CHECKED(String, source, 1);
10158 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
10159
10160 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
10161
10162 if (Top::has_pending_exception()) {
10163 return Failure::Exception();
10164 }
10165
10166 return result;
10167}
10168
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010169// Changes the source of the script to a new_source.
10170// If old_script_name is provided (i.e. is a String), also creates a copy of
10171// the script with its original source and sends notification to debugger.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010172static Object* Runtime_LiveEditReplaceScript(Arguments args) {
10173 ASSERT(args.length() == 3);
10174 HandleScope scope;
10175 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
10176 CONVERT_ARG_CHECKED(String, new_source, 1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010177 Handle<Object> old_script_name(args[2]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010178
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010179 CONVERT_CHECKED(Script, original_script_pointer,
10180 original_script_value->value());
10181 Handle<Script> original_script(original_script_pointer);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010182
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010183 Object* old_script = LiveEdit::ChangeScriptSource(original_script,
10184 new_source,
10185 old_script_name);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010186
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010187 if (old_script->IsScript()) {
10188 Handle<Script> script_handle(Script::cast(old_script));
10189 return *(GetScriptWrapper(script_handle));
10190 } else {
10191 return Heap::null_value();
10192 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010193}
10194
10195// Replaces code of SharedFunctionInfo with a new one.
10196static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
10197 ASSERT(args.length() == 2);
10198 HandleScope scope;
10199 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
10200 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
10201
ager@chromium.orgac091b72010-05-05 07:34:42 +000010202 return LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010203}
10204
10205// Connects SharedFunctionInfo to another script.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010206static Object* Runtime_LiveEditFunctionSetScript(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010207 ASSERT(args.length() == 2);
10208 HandleScope scope;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010209 Handle<Object> function_object(args[0]);
10210 Handle<Object> script_object(args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010211
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010212 if (function_object->IsJSValue()) {
10213 Handle<JSValue> function_wrapper = Handle<JSValue>::cast(function_object);
10214 if (script_object->IsJSValue()) {
10215 CONVERT_CHECKED(Script, script, JSValue::cast(*script_object)->value());
10216 script_object = Handle<Object>(script);
10217 }
10218
10219 LiveEdit::SetFunctionScript(function_wrapper, script_object);
10220 } else {
10221 // Just ignore this. We may not have a SharedFunctionInfo for some functions
10222 // and we check it in this function.
10223 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010224
10225 return Heap::undefined_value();
10226}
10227
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010228
10229// In a code of a parent function replaces original function as embedded object
10230// with a substitution one.
10231static Object* Runtime_LiveEditReplaceRefToNestedFunction(Arguments args) {
10232 ASSERT(args.length() == 3);
10233 HandleScope scope;
10234
10235 CONVERT_ARG_CHECKED(JSValue, parent_wrapper, 0);
10236 CONVERT_ARG_CHECKED(JSValue, orig_wrapper, 1);
10237 CONVERT_ARG_CHECKED(JSValue, subst_wrapper, 2);
10238
10239 LiveEdit::ReplaceRefToNestedFunction(parent_wrapper, orig_wrapper,
10240 subst_wrapper);
10241
10242 return Heap::undefined_value();
10243}
10244
10245
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010246// Updates positions of a shared function info (first parameter) according
10247// to script source change. Text change is described in second parameter as
10248// array of groups of 3 numbers:
10249// (change_begin, change_end, change_end_new_position).
10250// Each group describes a change in text; groups are sorted by change_begin.
10251static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
10252 ASSERT(args.length() == 2);
10253 HandleScope scope;
10254 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
10255 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
10256
ager@chromium.orgac091b72010-05-05 07:34:42 +000010257 return LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010258}
10259
10260
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010261// For array of SharedFunctionInfo's (each wrapped in JSValue)
10262// checks that none of them have activations on stacks (of any thread).
10263// Returns array of the same length with corresponding results of
10264// LiveEdit::FunctionPatchabilityStatus type.
ager@chromium.org357bf652010-04-12 11:30:10 +000010265static Object* Runtime_LiveEditCheckAndDropActivations(Arguments args) {
10266 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010267 HandleScope scope;
10268 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +000010269 CONVERT_BOOLEAN_CHECKED(do_drop, args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010270
ager@chromium.org357bf652010-04-12 11:30:10 +000010271 return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010272}
10273
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010274// Compares 2 strings line-by-line and returns diff in form of JSArray of
fschneider@chromium.org013f3e12010-04-26 13:27:52 +000010275// triplets (pos1, pos1_end, pos2_end) describing list of diff chunks.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010276static Object* Runtime_LiveEditCompareStringsLinewise(Arguments args) {
10277 ASSERT(args.length() == 2);
10278 HandleScope scope;
10279 CONVERT_ARG_CHECKED(String, s1, 0);
10280 CONVERT_ARG_CHECKED(String, s2, 1);
10281
10282 return *LiveEdit::CompareStringsLinewise(s1, s2);
10283}
10284
10285
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010286
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010287// A testing entry. Returns statement position which is the closest to
10288// source_position.
10289static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
10290 ASSERT(args.length() == 2);
10291 HandleScope scope;
10292 CONVERT_ARG_CHECKED(JSFunction, function, 0);
10293 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
10294
10295 Handle<Code> code(function->code());
10296
10297 RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
10298 int closest_pc = 0;
10299 int distance = kMaxInt;
10300 while (!it.done()) {
10301 int statement_position = static_cast<int>(it.rinfo()->data());
10302 // Check if this break point is closer that what was previously found.
10303 if (source_position <= statement_position &&
10304 statement_position - source_position < distance) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +000010305 closest_pc =
10306 static_cast<int>(it.rinfo()->pc() - code->instruction_start());
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010307 distance = statement_position - source_position;
10308 // Check whether we can't get any closer.
10309 if (distance == 0) break;
10310 }
10311 it.next();
10312 }
10313
10314 return Smi::FromInt(closest_pc);
10315}
10316
10317
ager@chromium.org357bf652010-04-12 11:30:10 +000010318// Calls specified function with or without entering the debugger.
10319// This is used in unit tests to run code as if debugger is entered or simply
10320// to have a stack with C++ frame in the middle.
10321static Object* Runtime_ExecuteInDebugContext(Arguments args) {
10322 ASSERT(args.length() == 2);
10323 HandleScope scope;
10324 CONVERT_ARG_CHECKED(JSFunction, function, 0);
10325 CONVERT_BOOLEAN_CHECKED(without_debugger, args[1]);
10326
10327 Handle<Object> result;
10328 bool pending_exception;
10329 {
10330 if (without_debugger) {
10331 result = Execution::Call(function, Top::global(), 0, NULL,
10332 &pending_exception);
10333 } else {
10334 EnterDebugger enter_debugger;
10335 result = Execution::Call(function, Top::global(), 0, NULL,
10336 &pending_exception);
10337 }
10338 }
10339 if (!pending_exception) {
10340 return *result;
10341 } else {
10342 return Failure::Exception();
10343 }
10344}
10345
10346
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010347#endif // ENABLE_DEBUGGER_SUPPORT
10348
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010349#ifdef ENABLE_LOGGING_AND_PROFILING
10350
10351static Object* Runtime_ProfilerResume(Arguments args) {
10352 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010353 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010354
10355 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010356 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10357 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010358 return Heap::undefined_value();
10359}
10360
10361
10362static Object* Runtime_ProfilerPause(Arguments args) {
10363 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010364 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010365
10366 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010367 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10368 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010369 return Heap::undefined_value();
10370}
10371
10372#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010373
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010374// Finds the script object from the script data. NOTE: This operation uses
10375// heap traversal to find the function generated for the source position
10376// for the requested break point. For lazily compiled functions several heap
10377// traversals might be required rendering this operation as a rather slow
10378// operation. However for setting break points which is normally done through
10379// some kind of user interaction the performance is not crucial.
10380static Handle<Object> Runtime_GetScriptFromScriptName(
10381 Handle<String> script_name) {
10382 // Scan the heap for Script objects to find the script with the requested
10383 // script data.
10384 Handle<Script> script;
10385 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010386 HeapObject* obj = NULL;
10387 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010388 // If a script is found check if it has the script data requested.
10389 if (obj->IsScript()) {
10390 if (Script::cast(obj)->name()->IsString()) {
10391 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
10392 script = Handle<Script>(Script::cast(obj));
10393 }
10394 }
10395 }
10396 }
10397
10398 // If no script with the requested script data is found return undefined.
10399 if (script.is_null()) return Factory::undefined_value();
10400
10401 // Return the script found.
10402 return GetScriptWrapper(script);
10403}
10404
10405
10406// Get the script object from script data. NOTE: Regarding performance
10407// see the NOTE for GetScriptFromScriptData.
10408// args[0]: script data for the script to find the source for
10409static Object* Runtime_GetScript(Arguments args) {
10410 HandleScope scope;
10411
10412 ASSERT(args.length() == 1);
10413
10414 CONVERT_CHECKED(String, script_name, args[0]);
10415
10416 // Find the requested script.
10417 Handle<Object> result =
10418 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
10419 return *result;
10420}
10421
10422
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010423// Determines whether the given stack frame should be displayed in
10424// a stack trace. The caller is the error constructor that asked
10425// for the stack trace to be collected. The first time a construct
10426// call to this function is encountered it is skipped. The seen_caller
10427// in/out parameter is used to remember if the caller has been seen
10428// yet.
10429static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
10430 bool* seen_caller) {
10431 // Only display JS frames.
10432 if (!raw_frame->is_java_script())
10433 return false;
10434 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
10435 Object* raw_fun = frame->function();
10436 // Not sure when this can happen but skip it just in case.
10437 if (!raw_fun->IsJSFunction())
10438 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010439 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010440 *seen_caller = true;
10441 return false;
10442 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010443 // Skip all frames until we've seen the caller. Also, skip the most
10444 // obvious builtin calls. Some builtin calls (such as Number.ADD
10445 // which is invoked using 'call') are very difficult to recognize
10446 // so we're leaving them in for now.
10447 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010448}
10449
10450
10451// Collect the raw data for a stack trace. Returns an array of three
10452// element segments each containing a receiver, function and native
10453// code offset.
10454static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010455 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010456 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010457 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
10458
10459 HandleScope scope;
10460
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000010461 limit = Max(limit, 0); // Ensure that limit is not negative.
10462 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010463 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010464
10465 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010466 // If the caller parameter is a function we skip frames until we're
10467 // under it before starting to collect.
10468 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010469 int cursor = 0;
10470 int frames_seen = 0;
10471 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010472 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010473 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010474 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010475 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010476 Object* recv = frame->receiver();
10477 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010478 Address pc = frame->pc();
10479 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010480 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010481 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010482 if (cursor + 2 < elements->length()) {
10483 elements->set(cursor++, recv);
10484 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010485 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010486 } else {
10487 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010488 Handle<Object> recv_handle(recv);
10489 Handle<Object> fun_handle(fun);
10490 SetElement(result, cursor++, recv_handle);
10491 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010492 SetElement(result, cursor++, Handle<Smi>(offset));
10493 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010494 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010495 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010496 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010497
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010498 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010499 return *result;
10500}
10501
10502
ager@chromium.org3811b432009-10-28 14:53:37 +000010503// Returns V8 version as a string.
10504static Object* Runtime_GetV8Version(Arguments args) {
10505 ASSERT_EQ(args.length(), 0);
10506
10507 NoHandleAllocation ha;
10508
10509 const char* version_string = v8::V8::GetVersion();
10510
10511 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
10512}
10513
10514
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010515static Object* Runtime_Abort(Arguments args) {
10516 ASSERT(args.length() == 2);
10517 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
10518 Smi::cast(args[1])->value());
10519 Top::PrintStack();
10520 OS::Abort();
10521 UNREACHABLE();
10522 return NULL;
10523}
10524
10525
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010526static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
10527 ASSERT(args.length() == 0);
10528 HandleScope::DeleteExtensions();
10529 return Heap::undefined_value();
10530}
10531
10532
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010533static Object* CacheMiss(FixedArray* cache_obj, int index, Object* key_obj) {
10534 ASSERT(index % 2 == 0); // index of the key
10535 ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
10536 ASSERT(index < cache_obj->length());
10537
10538 HandleScope scope;
10539
10540 Handle<FixedArray> cache(cache_obj);
10541 Handle<Object> key(key_obj);
10542 Handle<JSFunction> factory(JSFunction::cast(
10543 cache->get(JSFunctionResultCache::kFactoryIndex)));
10544 // TODO(antonm): consider passing a receiver when constructing a cache.
10545 Handle<Object> receiver(Top::global_context()->global());
10546
10547 Handle<Object> value;
10548 {
10549 // This handle is nor shared, nor used later, so it's safe.
10550 Object** argv[] = { key.location() };
10551 bool pending_exception = false;
10552 value = Execution::Call(factory,
10553 receiver,
10554 1,
10555 argv,
10556 &pending_exception);
10557 if (pending_exception) return Failure::Exception();
10558 }
10559
10560 cache->set(index, *key);
10561 cache->set(index + 1, *value);
10562 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(index));
10563
10564 return *value;
10565}
10566
10567
10568static Object* Runtime_GetFromCache(Arguments args) {
10569 // This is only called from codegen, so checks might be more lax.
10570 CONVERT_CHECKED(FixedArray, cache, args[0]);
10571 Object* key = args[1];
10572
10573 const int finger_index =
10574 Smi::cast(cache->get(JSFunctionResultCache::kFingerIndex))->value();
10575
10576 Object* o = cache->get(finger_index);
10577 if (o == key) {
10578 // The fastest case: hit the same place again.
10579 return cache->get(finger_index + 1);
10580 }
10581
10582 for (int i = finger_index - 2;
10583 i >= JSFunctionResultCache::kEntriesIndex;
10584 i -= 2) {
10585 o = cache->get(i);
10586 if (o == key) {
10587 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10588 return cache->get(i + 1);
10589 }
10590 }
10591
10592 const int size =
10593 Smi::cast(cache->get(JSFunctionResultCache::kCacheSizeIndex))->value();
10594 ASSERT(size <= cache->length());
10595
10596 for (int i = size - 2; i > finger_index; i -= 2) {
10597 o = cache->get(i);
10598 if (o == key) {
10599 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10600 return cache->get(i + 1);
10601 }
10602 }
10603
10604 // Cache miss. If we have spare room, put new data into it, otherwise
10605 // evict post finger entry which must be least recently used.
10606 if (size < cache->length()) {
10607 cache->set(JSFunctionResultCache::kCacheSizeIndex, Smi::FromInt(size + 2));
10608 return CacheMiss(cache, size, key);
10609 } else {
antonm@chromium.org397e23c2010-04-21 12:00:05 +000010610 int target_index = finger_index + JSFunctionResultCache::kEntrySize;
10611 if (target_index == cache->length()) {
10612 target_index = JSFunctionResultCache::kEntriesIndex;
10613 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010614 return CacheMiss(cache, target_index, key);
10615 }
10616}
10617
kasper.lund44510672008-07-25 07:37:58 +000010618#ifdef DEBUG
10619// ListNatives is ONLY used by the fuzz-natives.js in debug mode
10620// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010621static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +000010622 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010623 HandleScope scope;
10624 Handle<JSArray> result = Factory::NewJSArray(0);
10625 int index = 0;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010626 bool inline_runtime_functions = false;
ager@chromium.orga1645e22009-09-09 19:27:10 +000010627#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010628 { \
10629 HandleScope inner; \
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010630 Handle<String> name; \
10631 /* Inline runtime functions have an underscore in front of the name. */ \
10632 if (inline_runtime_functions) { \
10633 name = Factory::NewStringFromAscii( \
10634 Vector<const char>("_" #Name, StrLength("_" #Name))); \
10635 } else { \
10636 name = Factory::NewStringFromAscii( \
10637 Vector<const char>(#Name, StrLength(#Name))); \
10638 } \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010639 Handle<JSArray> pair = Factory::NewJSArray(0); \
10640 SetElement(pair, 0, name); \
10641 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
10642 SetElement(result, index++, pair); \
10643 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010644 inline_runtime_functions = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010645 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010646 inline_runtime_functions = true;
10647 INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010648#undef ADD_ENTRY
10649 return *result;
10650}
kasper.lund44510672008-07-25 07:37:58 +000010651#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010652
10653
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010654static Object* Runtime_Log(Arguments args) {
10655 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +000010656 CONVERT_CHECKED(String, format, args[0]);
10657 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010658 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010659 Logger::LogRuntime(chars, elms);
10660 return Heap::undefined_value();
10661}
10662
10663
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010664static Object* Runtime_IS_VAR(Arguments args) {
10665 UNREACHABLE(); // implemented as macro in the parser
10666 return NULL;
10667}
10668
10669
10670// ----------------------------------------------------------------------------
10671// Implementation of Runtime
10672
ager@chromium.orga1645e22009-09-09 19:27:10 +000010673#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010674 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +000010675 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010676
10677static Runtime::Function Runtime_functions[] = {
10678 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010679 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010680};
10681
10682#undef F
10683
10684
10685Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
10686 ASSERT(0 <= fid && fid < kNofFunctions);
10687 return &Runtime_functions[fid];
10688}
10689
10690
ricow@chromium.org65fae842010-08-25 15:26:24 +000010691Runtime::Function* Runtime::FunctionForName(Vector<const char> name) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010692 for (Function* f = Runtime_functions; f->name != NULL; f++) {
ricow@chromium.org65fae842010-08-25 15:26:24 +000010693 if (strncmp(f->name, name.start(), name.length()) == 0
10694 && f->name[name.length()] == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010695 return f;
10696 }
10697 }
10698 return NULL;
10699}
10700
10701
10702void Runtime::PerformGC(Object* result) {
10703 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010704 if (failure->IsRetryAfterGC()) {
10705 // Try to do a garbage collection; ignore it if it fails. The C
10706 // entry stub will throw an out-of-memory exception in that case.
10707 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
10708 } else {
10709 // Handle last resort GC and make sure to allow future allocations
10710 // to grow the heap without causing GCs (if possible).
10711 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +000010712 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010713 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010714}
10715
10716
10717} } // namespace v8::internal