blob: 43a67342350a821a3d6ca3371a2a3153d545b922 [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
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +0000101MUST_USE_RESULT static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000102 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 {
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +0000983 // The holder is an arguments object.
984 Handle<JSObject> arguments(Handle<JSObject>::cast(holder));
985 SetElement(arguments, index, initial_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000986 }
987 } else {
988 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000989 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000990 SetProperty(context_ext, name, initial_value, mode);
991 }
992 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000993
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000994 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000995 // The property is not in the function context. It needs to be
996 // "declared" in the function context's extension context, or in the
997 // global context.
998 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000999 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001000 // The function context's extension context exists - use it.
1001 context_ext = Handle<JSObject>(context->extension());
1002 } else {
1003 // The function context's extension context does not exists - allocate
1004 // it.
1005 context_ext = Factory::NewJSObject(Top::context_extension_function());
1006 // And store it in the extension slot.
1007 context->set_extension(*context_ext);
1008 }
1009 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001010
ager@chromium.org7c537e22008-10-16 08:43:32 +00001011 // Declare the property by setting it to the initial value if provided,
1012 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
1013 // constant declarations).
1014 ASSERT(!context_ext->HasLocalProperty(*name));
1015 Handle<Object> value(Heap::undefined_value());
1016 if (*initial_value != NULL) value = initial_value;
1017 SetProperty(context_ext, name, value, mode);
1018 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
1019 }
1020
1021 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001022}
1023
1024
1025static Object* Runtime_InitializeVarGlobal(Arguments args) {
1026 NoHandleAllocation nha;
1027
1028 // Determine if we need to assign to the variable if it already
1029 // exists (based on the number of arguments).
1030 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
1031 bool assign = args.length() == 2;
1032
1033 CONVERT_ARG_CHECKED(String, name, 0);
1034 GlobalObject* global = Top::context()->global();
1035
1036 // According to ECMA-262, section 12.2, page 62, the property must
1037 // not be deletable.
1038 PropertyAttributes attributes = DONT_DELETE;
1039
1040 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001041 // there, there is a property with this name in the prototype chain.
1042 // We follow Safari and Firefox behavior and only set the property
1043 // locally if there is an explicit initialization value that we have
1044 // to assign to the property. When adding the property we take
1045 // special precautions to always add it as a local property even in
1046 // case of callbacks in the prototype chain (this rules out using
1047 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
1048 // this.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001049 // Note that objects can have hidden prototypes, so we need to traverse
1050 // the whole chain of hidden prototypes to do a 'local' lookup.
1051 JSObject* real_holder = global;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001052 LookupResult lookup;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001053 while (true) {
1054 real_holder->LocalLookup(*name, &lookup);
1055 if (lookup.IsProperty()) {
1056 // Determine if this is a redeclaration of something read-only.
1057 if (lookup.IsReadOnly()) {
1058 // If we found readonly property on one of hidden prototypes,
1059 // just shadow it.
1060 if (real_holder != Top::context()->global()) break;
1061 return ThrowRedeclarationError("const", name);
1062 }
1063
1064 // Determine if this is a redeclaration of an intercepted read-only
1065 // property and figure out if the property exists at all.
1066 bool found = true;
1067 PropertyType type = lookup.type();
1068 if (type == INTERCEPTOR) {
1069 HandleScope handle_scope;
1070 Handle<JSObject> holder(real_holder);
1071 PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
1072 real_holder = *holder;
1073 if (intercepted == ABSENT) {
1074 // The interceptor claims the property isn't there. We need to
1075 // make sure to introduce it.
1076 found = false;
1077 } else if ((intercepted & READ_ONLY) != 0) {
1078 // The property is present, but read-only. Since we're trying to
1079 // overwrite it with a variable declaration we must throw a
1080 // re-declaration error. However if we found readonly property
1081 // on one of hidden prototypes, just shadow it.
1082 if (real_holder != Top::context()->global()) break;
1083 return ThrowRedeclarationError("const", name);
1084 }
1085 }
1086
1087 if (found && !assign) {
1088 // The global property is there and we're not assigning any value
1089 // to it. Just return.
1090 return Heap::undefined_value();
1091 }
1092
1093 // Assign the value (or undefined) to the property.
1094 Object* value = (assign) ? args[1] : Heap::undefined_value();
1095 return real_holder->SetProperty(&lookup, *name, value, attributes);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001096 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001097
1098 Object* proto = real_holder->GetPrototype();
1099 if (!proto->IsJSObject())
1100 break;
1101
1102 if (!JSObject::cast(proto)->map()->is_hidden_prototype())
1103 break;
1104
1105 real_holder = JSObject::cast(proto);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001106 }
1107
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001108 global = Top::context()->global();
1109 if (assign) {
1110 return global->IgnoreAttributesAndSetLocalProperty(*name,
1111 args[1],
1112 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001113 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001114 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001115}
1116
1117
1118static Object* Runtime_InitializeConstGlobal(Arguments args) {
1119 // All constants are declared with an initial value. The name
1120 // of the constant is the first argument and the initial value
1121 // is the second.
1122 RUNTIME_ASSERT(args.length() == 2);
1123 CONVERT_ARG_CHECKED(String, name, 0);
1124 Handle<Object> value = args.at<Object>(1);
1125
1126 // Get the current global object from top.
1127 GlobalObject* global = Top::context()->global();
1128
1129 // According to ECMA-262, section 12.2, page 62, the property must
1130 // not be deletable. Since it's a const, it must be READ_ONLY too.
1131 PropertyAttributes attributes =
1132 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
1133
1134 // Lookup the property locally in the global object. If it isn't
1135 // there, we add the property and take special precautions to always
1136 // add it as a local property even in case of callbacks in the
1137 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001138 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001139 LookupResult lookup;
1140 global->LocalLookup(*name, &lookup);
1141 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001142 return global->IgnoreAttributesAndSetLocalProperty(*name,
1143 *value,
1144 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001145 }
1146
1147 // Determine if this is a redeclaration of something not
1148 // read-only. In case the result is hidden behind an interceptor we
1149 // need to ask it for the property attributes.
1150 if (!lookup.IsReadOnly()) {
1151 if (lookup.type() != INTERCEPTOR) {
1152 return ThrowRedeclarationError("var", name);
1153 }
1154
1155 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
1156
1157 // Throw re-declaration error if the intercepted property is present
1158 // but not read-only.
1159 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
1160 return ThrowRedeclarationError("var", name);
1161 }
1162
1163 // Restore global object from context (in case of GC) and continue
1164 // with setting the value because the property is either absent or
1165 // read-only. We also have to do redo the lookup.
1166 global = Top::context()->global();
1167
1168 // BUG 1213579: Handle the case where we have to set a read-only
1169 // property through an interceptor and only do it if it's
1170 // uninitialized, e.g. the hole. Nirk...
1171 global->SetProperty(*name, *value, attributes);
1172 return *value;
1173 }
1174
1175 // Set the value, but only we're assigning the initial value to a
1176 // constant. For now, we determine this by checking if the
1177 // current value is the hole.
1178 PropertyType type = lookup.type();
1179 if (type == FIELD) {
1180 FixedArray* properties = global->properties();
1181 int index = lookup.GetFieldIndex();
1182 if (properties->get(index)->IsTheHole()) {
1183 properties->set(index, *value);
1184 }
1185 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001186 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
1187 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001188 }
1189 } else {
1190 // Ignore re-initialization of constants that have already been
1191 // assigned a function value.
1192 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
1193 }
1194
1195 // Use the set value as the result of the operation.
1196 return *value;
1197}
1198
1199
1200static Object* Runtime_InitializeConstContextSlot(Arguments args) {
1201 HandleScope scope;
1202 ASSERT(args.length() == 3);
1203
1204 Handle<Object> value(args[0]);
1205 ASSERT(!value->IsTheHole());
1206 CONVERT_ARG_CHECKED(Context, context, 1);
1207 Handle<String> name(String::cast(args[2]));
1208
1209 // Initializations are always done in the function context.
1210 context = Handle<Context>(context->fcontext());
1211
1212 int index;
1213 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001214 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001215 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001216 context->Lookup(name, flags, &index, &attributes);
1217
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001218 // In most situations, the property introduced by the const
1219 // declaration should be present in the context extension object.
1220 // However, because declaration and initialization are separate, the
1221 // property might have been deleted (if it was introduced by eval)
1222 // before we reach the initialization point.
1223 //
1224 // Example:
1225 //
1226 // function f() { eval("delete x; const x;"); }
1227 //
1228 // In that case, the initialization behaves like a normal assignment
1229 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001230 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001231 // Property was found in a context.
1232 if (holder->IsContext()) {
1233 // The holder cannot be the function context. If it is, there
1234 // should have been a const redeclaration error when declaring
1235 // the const property.
1236 ASSERT(!holder.is_identical_to(context));
1237 if ((attributes & READ_ONLY) == 0) {
1238 Handle<Context>::cast(holder)->set(index, *value);
1239 }
1240 } else {
1241 // The holder is an arguments object.
1242 ASSERT((attributes & READ_ONLY) == 0);
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00001243 Handle<JSObject> arguments(Handle<JSObject>::cast(holder));
1244 SetElement(arguments, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001245 }
1246 return *value;
1247 }
1248
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001249 // The property could not be found, we introduce it in the global
1250 // context.
1251 if (attributes == ABSENT) {
1252 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1253 SetProperty(global, name, value, NONE);
1254 return *value;
1255 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001256
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001257 // The property was present in a context extension object.
1258 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001259
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001260 if (*context_ext == context->extension()) {
1261 // This is the property that was introduced by the const
1262 // declaration. Set it if it hasn't been set before. NOTE: We
1263 // cannot use GetProperty() to get the current value as it
1264 // 'unholes' the value.
1265 LookupResult lookup;
1266 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1267 ASSERT(lookup.IsProperty()); // the property was declared
1268 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1269
1270 PropertyType type = lookup.type();
1271 if (type == FIELD) {
1272 FixedArray* properties = context_ext->properties();
1273 int index = lookup.GetFieldIndex();
1274 if (properties->get(index)->IsTheHole()) {
1275 properties->set(index, *value);
1276 }
1277 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001278 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
1279 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001280 }
1281 } else {
1282 // We should not reach here. Any real, named property should be
1283 // either a field or a dictionary slot.
1284 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001285 }
1286 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001287 // The property was found in a different context extension object.
1288 // Set it if it is not a read-only property.
1289 if ((attributes & READ_ONLY) == 0) {
1290 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1291 // Setting a property might throw an exception. Exceptions
1292 // are converted to empty handles in handle operations. We
1293 // need to convert back to exceptions here.
1294 if (set.is_null()) {
1295 ASSERT(Top::has_pending_exception());
1296 return Failure::Exception();
1297 }
1298 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001299 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001300
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001301 return *value;
1302}
1303
1304
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001305static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1306 Arguments args) {
1307 HandleScope scope;
1308 ASSERT(args.length() == 2);
1309 CONVERT_ARG_CHECKED(JSObject, object, 0);
1310 CONVERT_SMI_CHECKED(properties, args[1]);
1311 if (object->HasFastProperties()) {
1312 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1313 }
1314 return *object;
1315}
1316
1317
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001318static Object* Runtime_RegExpExec(Arguments args) {
1319 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001320 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001321 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1322 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001323 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001324 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001325 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001326 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001327 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001328 RUNTIME_ASSERT(index >= 0);
1329 RUNTIME_ASSERT(index <= subject->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001330 Counters::regexp_entry_runtime.Increment();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001331 Handle<Object> result = RegExpImpl::Exec(regexp,
1332 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001333 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001334 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001335 if (result.is_null()) return Failure::Exception();
1336 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001337}
1338
1339
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00001340static Object* Runtime_RegExpConstructResult(Arguments args) {
1341 ASSERT(args.length() == 3);
1342 CONVERT_SMI_CHECKED(elements_count, args[0]);
1343 if (elements_count > JSArray::kMaxFastElementsLength) {
1344 return Top::ThrowIllegalOperation();
1345 }
1346 Object* new_object = Heap::AllocateFixedArrayWithHoles(elements_count);
1347 if (new_object->IsFailure()) return new_object;
1348 FixedArray* elements = FixedArray::cast(new_object);
1349 new_object = Heap::AllocateRaw(JSRegExpResult::kSize,
1350 NEW_SPACE,
1351 OLD_POINTER_SPACE);
1352 if (new_object->IsFailure()) return new_object;
1353 {
1354 AssertNoAllocation no_gc;
1355 HandleScope scope;
1356 reinterpret_cast<HeapObject*>(new_object)->
1357 set_map(Top::global_context()->regexp_result_map());
1358 }
1359 JSArray* array = JSArray::cast(new_object);
1360 array->set_properties(Heap::empty_fixed_array());
1361 array->set_elements(elements);
1362 array->set_length(Smi::FromInt(elements_count));
1363 // Write in-object properties after the length of the array.
1364 array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, args[1]);
1365 array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, args[2]);
1366 return array;
1367}
1368
1369
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00001370static Object* Runtime_RegExpCloneResult(Arguments args) {
1371 ASSERT(args.length() == 1);
1372 Map* regexp_result_map;
1373 {
1374 AssertNoAllocation no_gc;
1375 HandleScope handles;
1376 regexp_result_map = Top::global_context()->regexp_result_map();
1377 }
1378 if (!args[0]->IsJSArray()) return args[0];
1379
1380 JSArray* result = JSArray::cast(args[0]);
1381 // Arguments to RegExpCloneResult should always be fresh RegExp exec call
1382 // results (either a fresh JSRegExpResult or null).
1383 // If the argument is not a JSRegExpResult, or isn't unmodified, just return
1384 // the argument uncloned.
1385 if (result->map() != regexp_result_map) return result;
1386
1387 // Having the original JSRegExpResult map guarantees that we have
1388 // fast elements and no properties except the two in-object properties.
1389 ASSERT(result->HasFastElements());
1390 ASSERT(result->properties() == Heap::empty_fixed_array());
1391 ASSERT_EQ(2, regexp_result_map->inobject_properties());
1392
1393 Object* new_array_alloc = Heap::AllocateRaw(JSRegExpResult::kSize,
1394 NEW_SPACE,
1395 OLD_POINTER_SPACE);
1396 if (new_array_alloc->IsFailure()) return new_array_alloc;
1397
1398 // Set HeapObject map to JSRegExpResult map.
1399 reinterpret_cast<HeapObject*>(new_array_alloc)->set_map(regexp_result_map);
1400
1401 JSArray* new_array = JSArray::cast(new_array_alloc);
1402
1403 // Copy JSObject properties.
1404 new_array->set_properties(result->properties()); // Empty FixedArray.
1405
1406 // Copy JSObject elements as copy-on-write.
1407 FixedArray* elements = FixedArray::cast(result->elements());
1408 if (elements != Heap::empty_fixed_array()) {
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00001409 elements->set_map(Heap::fixed_cow_array_map());
1410 }
1411 new_array->set_elements(elements);
1412
1413 // Copy JSArray length.
1414 new_array->set_length(result->length());
1415
1416 // Copy JSRegExpResult in-object property fields input and index.
1417 new_array->FastPropertyAtPut(JSRegExpResult::kIndexIndex,
1418 result->FastPropertyAt(
1419 JSRegExpResult::kIndexIndex));
1420 new_array->FastPropertyAtPut(JSRegExpResult::kInputIndex,
1421 result->FastPropertyAt(
1422 JSRegExpResult::kInputIndex));
1423 return new_array;
1424}
1425
1426
lrn@chromium.org25156de2010-04-06 13:10:27 +00001427static Object* Runtime_RegExpInitializeObject(Arguments args) {
1428 AssertNoAllocation no_alloc;
1429 ASSERT(args.length() == 5);
1430 CONVERT_CHECKED(JSRegExp, regexp, args[0]);
1431 CONVERT_CHECKED(String, source, args[1]);
1432
1433 Object* global = args[2];
1434 if (!global->IsTrue()) global = Heap::false_value();
1435
1436 Object* ignoreCase = args[3];
1437 if (!ignoreCase->IsTrue()) ignoreCase = Heap::false_value();
1438
1439 Object* multiline = args[4];
1440 if (!multiline->IsTrue()) multiline = Heap::false_value();
1441
1442 Map* map = regexp->map();
1443 Object* constructor = map->constructor();
1444 if (constructor->IsJSFunction() &&
1445 JSFunction::cast(constructor)->initial_map() == map) {
1446 // If we still have the original map, set in-object properties directly.
1447 regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, source);
1448 // TODO(lrn): Consider skipping write barrier on booleans as well.
1449 // Both true and false should be in oldspace at all times.
1450 regexp->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, global);
1451 regexp->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex, ignoreCase);
1452 regexp->InObjectPropertyAtPut(JSRegExp::kMultilineFieldIndex, multiline);
1453 regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex,
1454 Smi::FromInt(0),
1455 SKIP_WRITE_BARRIER);
1456 return regexp;
1457 }
1458
1459 // Map has changed, so use generic, but slower, method.
1460 PropertyAttributes final =
1461 static_cast<PropertyAttributes>(READ_ONLY | DONT_ENUM | DONT_DELETE);
1462 PropertyAttributes writable =
1463 static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
1464 regexp->IgnoreAttributesAndSetLocalProperty(Heap::source_symbol(),
1465 source,
1466 final);
1467 regexp->IgnoreAttributesAndSetLocalProperty(Heap::global_symbol(),
1468 global,
1469 final);
1470 regexp->IgnoreAttributesAndSetLocalProperty(Heap::ignore_case_symbol(),
1471 ignoreCase,
1472 final);
1473 regexp->IgnoreAttributesAndSetLocalProperty(Heap::multiline_symbol(),
1474 multiline,
1475 final);
1476 regexp->IgnoreAttributesAndSetLocalProperty(Heap::last_index_symbol(),
1477 Smi::FromInt(0),
1478 writable);
1479 return regexp;
1480}
1481
1482
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001483static Object* Runtime_FinishArrayPrototypeSetup(Arguments args) {
1484 HandleScope scope;
1485 ASSERT(args.length() == 1);
1486 CONVERT_ARG_CHECKED(JSArray, prototype, 0);
1487 // This is necessary to enable fast checks for absence of elements
1488 // on Array.prototype and below.
1489 prototype->set_elements(Heap::empty_fixed_array());
1490 return Smi::FromInt(0);
1491}
1492
1493
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001494static Handle<JSFunction> InstallBuiltin(Handle<JSObject> holder,
1495 const char* name,
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001496 Builtins::Name builtin_name) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001497 Handle<String> key = Factory::LookupAsciiSymbol(name);
1498 Handle<Code> code(Builtins::builtin(builtin_name));
1499 Handle<JSFunction> optimized = Factory::NewFunction(key,
1500 JS_OBJECT_TYPE,
1501 JSObject::kHeaderSize,
1502 code,
1503 false);
1504 optimized->shared()->DontAdaptArguments();
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001505 SetProperty(holder, key, optimized, NONE);
1506 return optimized;
1507}
1508
1509
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001510static Object* Runtime_SpecialArrayFunctions(Arguments args) {
1511 HandleScope scope;
1512 ASSERT(args.length() == 1);
1513 CONVERT_ARG_CHECKED(JSObject, holder, 0);
1514
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001515 InstallBuiltin(holder, "pop", Builtins::ArrayPop);
1516 InstallBuiltin(holder, "push", Builtins::ArrayPush);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001517 InstallBuiltin(holder, "shift", Builtins::ArrayShift);
1518 InstallBuiltin(holder, "unshift", Builtins::ArrayUnshift);
1519 InstallBuiltin(holder, "slice", Builtins::ArraySlice);
1520 InstallBuiltin(holder, "splice", Builtins::ArraySplice);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001521 InstallBuiltin(holder, "concat", Builtins::ArrayConcat);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001522
1523 return *holder;
1524}
1525
1526
ager@chromium.org357bf652010-04-12 11:30:10 +00001527static Object* Runtime_GetGlobalReceiver(Arguments args) {
1528 // Returns a real global receiver, not one of builtins object.
1529 Context* global_context = Top::context()->global()->global_context();
1530 return global_context->global()->global_receiver();
1531}
1532
1533
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001534static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1535 HandleScope scope;
1536 ASSERT(args.length() == 4);
1537 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1538 int index = Smi::cast(args[1])->value();
1539 Handle<String> pattern = args.at<String>(2);
1540 Handle<String> flags = args.at<String>(3);
1541
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001542 // Get the RegExp function from the context in the literals array.
1543 // This is the RegExp function from the context in which the
1544 // function was created. We do not use the RegExp function from the
1545 // current global context because this might be the RegExp function
1546 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001547 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001548 Handle<JSFunction>(
1549 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001550 // Compute the regular expression literal.
1551 bool has_pending_exception;
1552 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001553 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1554 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001555 if (has_pending_exception) {
1556 ASSERT(Top::has_pending_exception());
1557 return Failure::Exception();
1558 }
1559 literals->set(index, *regexp);
1560 return *regexp;
1561}
1562
1563
1564static Object* Runtime_FunctionGetName(Arguments args) {
1565 NoHandleAllocation ha;
1566 ASSERT(args.length() == 1);
1567
1568 CONVERT_CHECKED(JSFunction, f, args[0]);
1569 return f->shared()->name();
1570}
1571
1572
ager@chromium.org236ad962008-09-25 09:45:57 +00001573static Object* Runtime_FunctionSetName(Arguments args) {
1574 NoHandleAllocation ha;
1575 ASSERT(args.length() == 2);
1576
1577 CONVERT_CHECKED(JSFunction, f, args[0]);
1578 CONVERT_CHECKED(String, name, args[1]);
1579 f->shared()->set_name(name);
1580 return Heap::undefined_value();
1581}
1582
1583
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001584static Object* Runtime_FunctionRemovePrototype(Arguments args) {
1585 NoHandleAllocation ha;
1586 ASSERT(args.length() == 1);
1587
1588 CONVERT_CHECKED(JSFunction, f, args[0]);
1589 Object* obj = f->RemovePrototype();
1590 if (obj->IsFailure()) return obj;
1591
1592 return Heap::undefined_value();
1593}
1594
1595
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001596static Object* Runtime_FunctionGetScript(Arguments args) {
1597 HandleScope scope;
1598 ASSERT(args.length() == 1);
1599
1600 CONVERT_CHECKED(JSFunction, fun, args[0]);
1601 Handle<Object> script = Handle<Object>(fun->shared()->script());
1602 if (!script->IsScript()) return Heap::undefined_value();
1603
1604 return *GetScriptWrapper(Handle<Script>::cast(script));
1605}
1606
1607
1608static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1609 NoHandleAllocation ha;
1610 ASSERT(args.length() == 1);
1611
1612 CONVERT_CHECKED(JSFunction, f, args[0]);
1613 return f->shared()->GetSourceCode();
1614}
1615
1616
1617static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1618 NoHandleAllocation ha;
1619 ASSERT(args.length() == 1);
1620
1621 CONVERT_CHECKED(JSFunction, fun, args[0]);
1622 int pos = fun->shared()->start_position();
1623 return Smi::FromInt(pos);
1624}
1625
1626
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001627static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1628 ASSERT(args.length() == 2);
1629
1630 CONVERT_CHECKED(JSFunction, fun, args[0]);
1631 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1632
1633 Code* code = fun->code();
1634 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1635
1636 Address pc = code->address() + offset;
1637 return Smi::FromInt(fun->code()->SourcePosition(pc));
1638}
1639
1640
1641
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001642static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1643 NoHandleAllocation ha;
1644 ASSERT(args.length() == 2);
1645
1646 CONVERT_CHECKED(JSFunction, fun, args[0]);
1647 CONVERT_CHECKED(String, name, args[1]);
1648 fun->SetInstanceClassName(name);
1649 return Heap::undefined_value();
1650}
1651
1652
1653static Object* Runtime_FunctionSetLength(Arguments args) {
1654 NoHandleAllocation ha;
1655 ASSERT(args.length() == 2);
1656
1657 CONVERT_CHECKED(JSFunction, fun, args[0]);
1658 CONVERT_CHECKED(Smi, length, args[1]);
1659 fun->shared()->set_length(length->value());
1660 return length;
1661}
1662
1663
1664static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001665 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001666 ASSERT(args.length() == 2);
1667
1668 CONVERT_CHECKED(JSFunction, fun, args[0]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001669 ASSERT(fun->should_have_prototype());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001670 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1671 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001672 return args[0]; // return TOS
1673}
1674
1675
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001676static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1677 NoHandleAllocation ha;
1678 ASSERT(args.length() == 1);
1679
1680 CONVERT_CHECKED(JSFunction, f, args[0]);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001681 return f->shared()->IsApiFunction() ? Heap::true_value()
1682 : Heap::false_value();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001683}
1684
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001685static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1686 NoHandleAllocation ha;
1687 ASSERT(args.length() == 1);
1688
1689 CONVERT_CHECKED(JSFunction, f, args[0]);
1690 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1691}
1692
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001693
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001694static Object* Runtime_SetCode(Arguments args) {
1695 HandleScope scope;
1696 ASSERT(args.length() == 2);
1697
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001698 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001699 Handle<Object> code = args.at<Object>(1);
1700
1701 Handle<Context> context(target->context());
1702
1703 if (!code->IsNull()) {
1704 RUNTIME_ASSERT(code->IsJSFunction());
1705 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001706 Handle<SharedFunctionInfo> shared(fun->shared());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001707
1708 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001709 return Failure::Exception();
1710 }
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00001711 // Set the code, scope info, formal parameter count,
1712 // and the length of the target function.
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001713 target->shared()->set_code(shared->code());
1714 target->set_code(shared->code());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00001715 target->shared()->set_scope_info(shared->scope_info());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001716 target->shared()->set_length(shared->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001717 target->shared()->set_formal_parameter_count(
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001718 shared->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001719 // Set the source code of the target function to undefined.
1720 // SetCode is only used for built-in constructors like String,
1721 // Array, and Object, and some web code
1722 // doesn't like seeing source code for constructors.
1723 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001724 // Clear the optimization hints related to the compiled code as these are no
1725 // longer valid when the code is overwritten.
1726 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001727 context = Handle<Context>(fun->context());
1728
1729 // Make sure we get a fresh copy of the literal vector to avoid
1730 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001731 int number_of_literals = fun->NumberOfLiterals();
1732 Handle<FixedArray> literals =
1733 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001734 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001735 // Insert the object, regexp and array functions in the literals
1736 // array prefix. These are the functions that will be used when
1737 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001738 literals->set(JSFunction::kLiteralGlobalContextIndex,
1739 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001740 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001741 // It's okay to skip the write barrier here because the literals
1742 // are guaranteed to be in old space.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001743 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001744 }
1745
1746 target->set_context(*context);
1747 return *target;
1748}
1749
1750
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00001751static Object* Runtime_SetExpectedNumberOfProperties(Arguments args) {
1752 HandleScope scope;
1753 ASSERT(args.length() == 2);
1754 CONVERT_ARG_CHECKED(JSFunction, function, 0);
1755 CONVERT_SMI_CHECKED(num, args[1]);
1756 RUNTIME_ASSERT(num >= 0);
1757 SetExpectedNofProperties(function, num);
1758 return Heap::undefined_value();
1759}
1760
1761
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001762static Object* CharFromCode(Object* char_code) {
1763 uint32_t code;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001764 if (char_code->ToArrayIndex(&code)) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001765 if (code <= 0xffff) {
1766 return Heap::LookupSingleCharacterStringFromCode(code);
1767 }
1768 }
1769 return Heap::empty_string();
1770}
1771
1772
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001773static Object* Runtime_StringCharCodeAt(Arguments args) {
1774 NoHandleAllocation ha;
1775 ASSERT(args.length() == 2);
1776
1777 CONVERT_CHECKED(String, subject, args[0]);
1778 Object* index = args[1];
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001779 RUNTIME_ASSERT(index->IsNumber());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001780
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001781 uint32_t i = 0;
1782 if (index->IsSmi()) {
1783 int value = Smi::cast(index)->value();
1784 if (value < 0) return Heap::nan_value();
1785 i = value;
1786 } else {
1787 ASSERT(index->IsHeapNumber());
1788 double value = HeapNumber::cast(index)->value();
1789 i = static_cast<uint32_t>(DoubleToInteger(value));
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001790 }
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001791
1792 // Flatten the string. If someone wants to get a char at an index
1793 // in a cons string, it is likely that more indices will be
1794 // accessed.
1795 Object* flat = subject->TryFlatten();
1796 if (flat->IsFailure()) return flat;
1797 subject = String::cast(flat);
1798
1799 if (i >= static_cast<uint32_t>(subject->length())) {
1800 return Heap::nan_value();
1801 }
1802
1803 return Smi::FromInt(subject->Get(i));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001804}
1805
1806
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001807static Object* Runtime_CharFromCode(Arguments args) {
1808 NoHandleAllocation ha;
1809 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001810 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001811}
1812
lrn@chromium.org25156de2010-04-06 13:10:27 +00001813
1814class FixedArrayBuilder {
1815 public:
1816 explicit FixedArrayBuilder(int initial_capacity)
1817 : array_(Factory::NewFixedArrayWithHoles(initial_capacity)),
1818 length_(0) {
1819 // Require a non-zero initial size. Ensures that doubling the size to
1820 // extend the array will work.
1821 ASSERT(initial_capacity > 0);
1822 }
1823
1824 explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
1825 : array_(backing_store),
1826 length_(0) {
1827 // Require a non-zero initial size. Ensures that doubling the size to
1828 // extend the array will work.
1829 ASSERT(backing_store->length() > 0);
1830 }
1831
1832 bool HasCapacity(int elements) {
1833 int length = array_->length();
1834 int required_length = length_ + elements;
1835 return (length >= required_length);
1836 }
1837
1838 void EnsureCapacity(int elements) {
1839 int length = array_->length();
1840 int required_length = length_ + elements;
1841 if (length < required_length) {
1842 int new_length = length;
1843 do {
1844 new_length *= 2;
1845 } while (new_length < required_length);
1846 Handle<FixedArray> extended_array =
1847 Factory::NewFixedArrayWithHoles(new_length);
1848 array_->CopyTo(0, *extended_array, 0, length_);
1849 array_ = extended_array;
1850 }
1851 }
1852
1853 void Add(Object* value) {
1854 ASSERT(length_ < capacity());
1855 array_->set(length_, value);
1856 length_++;
1857 }
1858
1859 void Add(Smi* value) {
1860 ASSERT(length_ < capacity());
1861 array_->set(length_, value);
1862 length_++;
1863 }
1864
1865 Handle<FixedArray> array() {
1866 return array_;
1867 }
1868
1869 int length() {
1870 return length_;
1871 }
1872
1873 int capacity() {
1874 return array_->length();
1875 }
1876
1877 Handle<JSArray> ToJSArray() {
1878 Handle<JSArray> result_array = Factory::NewJSArrayWithElements(array_);
1879 result_array->set_length(Smi::FromInt(length_));
1880 return result_array;
1881 }
1882
1883 Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
1884 target_array->set_elements(*array_);
1885 target_array->set_length(Smi::FromInt(length_));
1886 return target_array;
1887 }
1888
1889 private:
1890 Handle<FixedArray> array_;
1891 int length_;
1892};
1893
1894
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001895// Forward declarations.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001896const int kStringBuilderConcatHelperLengthBits = 11;
1897const int kStringBuilderConcatHelperPositionBits = 19;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001898
1899template <typename schar>
1900static inline void StringBuilderConcatHelper(String*,
1901 schar*,
1902 FixedArray*,
1903 int);
1904
lrn@chromium.org25156de2010-04-06 13:10:27 +00001905typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
1906 StringBuilderSubstringLength;
1907typedef BitField<int,
1908 kStringBuilderConcatHelperLengthBits,
1909 kStringBuilderConcatHelperPositionBits>
1910 StringBuilderSubstringPosition;
1911
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001912
1913class ReplacementStringBuilder {
1914 public:
1915 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
lrn@chromium.org25156de2010-04-06 13:10:27 +00001916 : array_builder_(estimated_part_count),
1917 subject_(subject),
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001918 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001919 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001920 // Require a non-zero initial size. Ensures that doubling the size to
1921 // extend the array will work.
1922 ASSERT(estimated_part_count > 0);
1923 }
1924
lrn@chromium.org25156de2010-04-06 13:10:27 +00001925 static inline void AddSubjectSlice(FixedArrayBuilder* builder,
1926 int from,
1927 int to) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001928 ASSERT(from >= 0);
1929 int length = to - from;
1930 ASSERT(length > 0);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001931 if (StringBuilderSubstringLength::is_valid(length) &&
1932 StringBuilderSubstringPosition::is_valid(from)) {
1933 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1934 StringBuilderSubstringPosition::encode(from);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001935 builder->Add(Smi::FromInt(encoded_slice));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001936 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001937 // Otherwise encode as two smis.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001938 builder->Add(Smi::FromInt(-length));
1939 builder->Add(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001940 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00001941 }
1942
1943
1944 void EnsureCapacity(int elements) {
1945 array_builder_.EnsureCapacity(elements);
1946 }
1947
1948
1949 void AddSubjectSlice(int from, int to) {
1950 AddSubjectSlice(&array_builder_, from, to);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001951 IncrementCharacterCount(to - from);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001952 }
1953
1954
1955 void AddString(Handle<String> string) {
1956 int length = string->length();
1957 ASSERT(length > 0);
1958 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001959 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001960 is_ascii_ = false;
1961 }
1962 IncrementCharacterCount(length);
1963 }
1964
1965
1966 Handle<String> ToString() {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001967 if (array_builder_.length() == 0) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001968 return Factory::empty_string();
1969 }
1970
1971 Handle<String> joined_string;
1972 if (is_ascii_) {
1973 joined_string = NewRawAsciiString(character_count_);
1974 AssertNoAllocation no_alloc;
1975 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1976 char* char_buffer = seq->GetChars();
1977 StringBuilderConcatHelper(*subject_,
1978 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001979 *array_builder_.array(),
1980 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001981 } else {
1982 // Non-ASCII.
1983 joined_string = NewRawTwoByteString(character_count_);
1984 AssertNoAllocation no_alloc;
1985 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1986 uc16* char_buffer = seq->GetChars();
1987 StringBuilderConcatHelper(*subject_,
1988 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001989 *array_builder_.array(),
1990 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001991 }
1992 return joined_string;
1993 }
1994
1995
1996 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001997 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001998 V8::FatalProcessOutOfMemory("String.replace result too large.");
1999 }
2000 character_count_ += by;
2001 }
2002
lrn@chromium.org25156de2010-04-06 13:10:27 +00002003 Handle<JSArray> GetParts() {
2004 Handle<JSArray> result =
2005 Factory::NewJSArrayWithElements(array_builder_.array());
2006 result->set_length(Smi::FromInt(array_builder_.length()));
2007 return result;
2008 }
kmillikin@chromium.orgd9825192010-03-30 08:36:16 +00002009
lrn@chromium.org25156de2010-04-06 13:10:27 +00002010 private:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002011 Handle<String> NewRawAsciiString(int size) {
2012 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
2013 }
2014
2015
2016 Handle<String> NewRawTwoByteString(int size) {
2017 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
2018 }
2019
2020
2021 void AddElement(Object* element) {
2022 ASSERT(element->IsSmi() || element->IsString());
lrn@chromium.org25156de2010-04-06 13:10:27 +00002023 ASSERT(array_builder_.capacity() > array_builder_.length());
2024 array_builder_.Add(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002025 }
2026
lrn@chromium.org25156de2010-04-06 13:10:27 +00002027 FixedArrayBuilder array_builder_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002028 Handle<String> subject_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002029 int character_count_;
2030 bool is_ascii_;
2031};
2032
2033
2034class CompiledReplacement {
2035 public:
2036 CompiledReplacement()
2037 : parts_(1), replacement_substrings_(0) {}
2038
2039 void Compile(Handle<String> replacement,
2040 int capture_count,
2041 int subject_length);
2042
2043 void Apply(ReplacementStringBuilder* builder,
2044 int match_from,
2045 int match_to,
2046 Handle<JSArray> last_match_info);
2047
2048 // Number of distinct parts of the replacement pattern.
2049 int parts() {
2050 return parts_.length();
2051 }
2052 private:
2053 enum PartType {
2054 SUBJECT_PREFIX = 1,
2055 SUBJECT_SUFFIX,
2056 SUBJECT_CAPTURE,
2057 REPLACEMENT_SUBSTRING,
2058 REPLACEMENT_STRING,
2059
2060 NUMBER_OF_PART_TYPES
2061 };
2062
2063 struct ReplacementPart {
2064 static inline ReplacementPart SubjectMatch() {
2065 return ReplacementPart(SUBJECT_CAPTURE, 0);
2066 }
2067 static inline ReplacementPart SubjectCapture(int capture_index) {
2068 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
2069 }
2070 static inline ReplacementPart SubjectPrefix() {
2071 return ReplacementPart(SUBJECT_PREFIX, 0);
2072 }
2073 static inline ReplacementPart SubjectSuffix(int subject_length) {
2074 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
2075 }
2076 static inline ReplacementPart ReplacementString() {
2077 return ReplacementPart(REPLACEMENT_STRING, 0);
2078 }
2079 static inline ReplacementPart ReplacementSubString(int from, int to) {
2080 ASSERT(from >= 0);
2081 ASSERT(to > from);
2082 return ReplacementPart(-from, to);
2083 }
2084
2085 // If tag <= 0 then it is the negation of a start index of a substring of
2086 // the replacement pattern, otherwise it's a value from PartType.
2087 ReplacementPart(int tag, int data)
2088 : tag(tag), data(data) {
2089 // Must be non-positive or a PartType value.
2090 ASSERT(tag < NUMBER_OF_PART_TYPES);
2091 }
2092 // Either a value of PartType or a non-positive number that is
2093 // the negation of an index into the replacement string.
2094 int tag;
2095 // The data value's interpretation depends on the value of tag:
2096 // tag == SUBJECT_PREFIX ||
2097 // tag == SUBJECT_SUFFIX: data is unused.
2098 // tag == SUBJECT_CAPTURE: data is the number of the capture.
2099 // tag == REPLACEMENT_SUBSTRING ||
2100 // tag == REPLACEMENT_STRING: data is index into array of substrings
2101 // of the replacement string.
2102 // tag <= 0: Temporary representation of the substring of the replacement
2103 // string ranging over -tag .. data.
2104 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
2105 // substring objects.
2106 int data;
2107 };
2108
2109 template<typename Char>
2110 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
2111 Vector<Char> characters,
2112 int capture_count,
2113 int subject_length) {
2114 int length = characters.length();
2115 int last = 0;
2116 for (int i = 0; i < length; i++) {
2117 Char c = characters[i];
2118 if (c == '$') {
2119 int next_index = i + 1;
2120 if (next_index == length) { // No next character!
2121 break;
2122 }
2123 Char c2 = characters[next_index];
2124 switch (c2) {
2125 case '$':
2126 if (i > last) {
2127 // There is a substring before. Include the first "$".
2128 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
2129 last = next_index + 1; // Continue after the second "$".
2130 } else {
2131 // Let the next substring start with the second "$".
2132 last = next_index;
2133 }
2134 i = next_index;
2135 break;
2136 case '`':
2137 if (i > last) {
2138 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2139 }
2140 parts->Add(ReplacementPart::SubjectPrefix());
2141 i = next_index;
2142 last = i + 1;
2143 break;
2144 case '\'':
2145 if (i > last) {
2146 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2147 }
2148 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
2149 i = next_index;
2150 last = i + 1;
2151 break;
2152 case '&':
2153 if (i > last) {
2154 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2155 }
2156 parts->Add(ReplacementPart::SubjectMatch());
2157 i = next_index;
2158 last = i + 1;
2159 break;
2160 case '0':
2161 case '1':
2162 case '2':
2163 case '3':
2164 case '4':
2165 case '5':
2166 case '6':
2167 case '7':
2168 case '8':
2169 case '9': {
2170 int capture_ref = c2 - '0';
2171 if (capture_ref > capture_count) {
2172 i = next_index;
2173 continue;
2174 }
2175 int second_digit_index = next_index + 1;
2176 if (second_digit_index < length) {
2177 // Peek ahead to see if we have two digits.
2178 Char c3 = characters[second_digit_index];
2179 if ('0' <= c3 && c3 <= '9') { // Double digits.
2180 int double_digit_ref = capture_ref * 10 + c3 - '0';
2181 if (double_digit_ref <= capture_count) {
2182 next_index = second_digit_index;
2183 capture_ref = double_digit_ref;
2184 }
2185 }
2186 }
2187 if (capture_ref > 0) {
2188 if (i > last) {
2189 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2190 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002191 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002192 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
2193 last = next_index + 1;
2194 }
2195 i = next_index;
2196 break;
2197 }
2198 default:
2199 i = next_index;
2200 break;
2201 }
2202 }
2203 }
2204 if (length > last) {
2205 if (last == 0) {
2206 parts->Add(ReplacementPart::ReplacementString());
2207 } else {
2208 parts->Add(ReplacementPart::ReplacementSubString(last, length));
2209 }
2210 }
2211 }
2212
2213 ZoneList<ReplacementPart> parts_;
2214 ZoneList<Handle<String> > replacement_substrings_;
2215};
2216
2217
2218void CompiledReplacement::Compile(Handle<String> replacement,
2219 int capture_count,
2220 int subject_length) {
2221 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00002222 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002223 AssertNoAllocation no_alloc;
2224 ParseReplacementPattern(&parts_,
2225 replacement->ToAsciiVector(),
2226 capture_count,
2227 subject_length);
2228 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00002229 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002230 AssertNoAllocation no_alloc;
2231
2232 ParseReplacementPattern(&parts_,
2233 replacement->ToUC16Vector(),
2234 capture_count,
2235 subject_length);
2236 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002237 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002238 int substring_index = 0;
2239 for (int i = 0, n = parts_.length(); i < n; i++) {
2240 int tag = parts_[i].tag;
2241 if (tag <= 0) { // A replacement string slice.
2242 int from = -tag;
2243 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002244 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002245 parts_[i].tag = REPLACEMENT_SUBSTRING;
2246 parts_[i].data = substring_index;
2247 substring_index++;
2248 } else if (tag == REPLACEMENT_STRING) {
2249 replacement_substrings_.Add(replacement);
2250 parts_[i].data = substring_index;
2251 substring_index++;
2252 }
2253 }
2254}
2255
2256
2257void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
2258 int match_from,
2259 int match_to,
2260 Handle<JSArray> last_match_info) {
2261 for (int i = 0, n = parts_.length(); i < n; i++) {
2262 ReplacementPart part = parts_[i];
2263 switch (part.tag) {
2264 case SUBJECT_PREFIX:
2265 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
2266 break;
2267 case SUBJECT_SUFFIX: {
2268 int subject_length = part.data;
2269 if (match_to < subject_length) {
2270 builder->AddSubjectSlice(match_to, subject_length);
2271 }
2272 break;
2273 }
2274 case SUBJECT_CAPTURE: {
2275 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002276 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002277 int from = RegExpImpl::GetCapture(match_info, capture * 2);
2278 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
2279 if (from >= 0 && to > from) {
2280 builder->AddSubjectSlice(from, to);
2281 }
2282 break;
2283 }
2284 case REPLACEMENT_SUBSTRING:
2285 case REPLACEMENT_STRING:
2286 builder->AddString(replacement_substrings_[part.data]);
2287 break;
2288 default:
2289 UNREACHABLE();
2290 }
2291 }
2292}
2293
2294
2295
2296static Object* StringReplaceRegExpWithString(String* subject,
2297 JSRegExp* regexp,
2298 String* replacement,
2299 JSArray* last_match_info) {
2300 ASSERT(subject->IsFlat());
2301 ASSERT(replacement->IsFlat());
2302
2303 HandleScope handles;
2304
2305 int length = subject->length();
2306 Handle<String> subject_handle(subject);
2307 Handle<JSRegExp> regexp_handle(regexp);
2308 Handle<String> replacement_handle(replacement);
2309 Handle<JSArray> last_match_info_handle(last_match_info);
2310 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2311 subject_handle,
2312 0,
2313 last_match_info_handle);
2314 if (match.is_null()) {
2315 return Failure::Exception();
2316 }
2317 if (match->IsNull()) {
2318 return *subject_handle;
2319 }
2320
2321 int capture_count = regexp_handle->CaptureCount();
2322
2323 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002324 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002325 CompiledReplacement compiled_replacement;
2326 compiled_replacement.Compile(replacement_handle,
2327 capture_count,
2328 length);
2329
2330 bool is_global = regexp_handle->GetFlags().is_global();
2331
2332 // Guessing the number of parts that the final result string is built
2333 // from. Global regexps can match any number of times, so we guess
2334 // conservatively.
2335 int expected_parts =
2336 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
2337 ReplacementStringBuilder builder(subject_handle, expected_parts);
2338
2339 // Index of end of last match.
2340 int prev = 0;
2341
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002342 // Number of parts added by compiled replacement plus preceeding
2343 // string and possibly suffix after last match. It is possible for
2344 // all components to use two elements when encoded as two smis.
2345 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002346 bool matched = true;
2347 do {
2348 ASSERT(last_match_info_handle->HasFastElements());
2349 // Increase the capacity of the builder before entering local handle-scope,
2350 // so its internal buffer can safely allocate a new handle if it grows.
2351 builder.EnsureCapacity(parts_added_per_loop);
2352
2353 HandleScope loop_scope;
2354 int start, end;
2355 {
2356 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002357 FixedArray* match_info_array =
2358 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002359
2360 ASSERT_EQ(capture_count * 2 + 2,
2361 RegExpImpl::GetLastCaptureCount(match_info_array));
2362 start = RegExpImpl::GetCapture(match_info_array, 0);
2363 end = RegExpImpl::GetCapture(match_info_array, 1);
2364 }
2365
2366 if (prev < start) {
2367 builder.AddSubjectSlice(prev, start);
2368 }
2369 compiled_replacement.Apply(&builder,
2370 start,
2371 end,
2372 last_match_info_handle);
2373 prev = end;
2374
2375 // Only continue checking for global regexps.
2376 if (!is_global) break;
2377
2378 // Continue from where the match ended, unless it was an empty match.
2379 int next = end;
2380 if (start == end) {
2381 next = end + 1;
2382 if (next > length) break;
2383 }
2384
2385 match = RegExpImpl::Exec(regexp_handle,
2386 subject_handle,
2387 next,
2388 last_match_info_handle);
2389 if (match.is_null()) {
2390 return Failure::Exception();
2391 }
2392 matched = !match->IsNull();
2393 } while (matched);
2394
2395 if (prev < length) {
2396 builder.AddSubjectSlice(prev, length);
2397 }
2398
2399 return *(builder.ToString());
2400}
2401
2402
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00002403template <typename ResultSeqString>
2404static Object* StringReplaceRegExpWithEmptyString(String* subject,
2405 JSRegExp* regexp,
2406 JSArray* last_match_info) {
2407 ASSERT(subject->IsFlat());
2408
2409 HandleScope handles;
2410
2411 Handle<String> subject_handle(subject);
2412 Handle<JSRegExp> regexp_handle(regexp);
2413 Handle<JSArray> last_match_info_handle(last_match_info);
2414 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2415 subject_handle,
2416 0,
2417 last_match_info_handle);
2418 if (match.is_null()) return Failure::Exception();
2419 if (match->IsNull()) return *subject_handle;
2420
2421 ASSERT(last_match_info_handle->HasFastElements());
2422
2423 HandleScope loop_scope;
2424 int start, end;
2425 {
2426 AssertNoAllocation match_info_array_is_not_in_a_handle;
2427 FixedArray* match_info_array =
2428 FixedArray::cast(last_match_info_handle->elements());
2429
2430 start = RegExpImpl::GetCapture(match_info_array, 0);
2431 end = RegExpImpl::GetCapture(match_info_array, 1);
2432 }
2433
2434 int length = subject->length();
2435 int new_length = length - (end - start);
2436 if (new_length == 0) {
2437 return Heap::empty_string();
2438 }
2439 Handle<ResultSeqString> answer;
2440 if (ResultSeqString::kHasAsciiEncoding) {
2441 answer =
2442 Handle<ResultSeqString>::cast(Factory::NewRawAsciiString(new_length));
2443 } else {
2444 answer =
2445 Handle<ResultSeqString>::cast(Factory::NewRawTwoByteString(new_length));
2446 }
2447
2448 // If the regexp isn't global, only match once.
2449 if (!regexp_handle->GetFlags().is_global()) {
2450 if (start > 0) {
2451 String::WriteToFlat(*subject_handle,
2452 answer->GetChars(),
2453 0,
2454 start);
2455 }
2456 if (end < length) {
2457 String::WriteToFlat(*subject_handle,
2458 answer->GetChars() + start,
2459 end,
2460 length);
2461 }
2462 return *answer;
2463 }
2464
2465 int prev = 0; // Index of end of last match.
2466 int next = 0; // Start of next search (prev unless last match was empty).
2467 int position = 0;
2468
2469 do {
2470 if (prev < start) {
2471 // Add substring subject[prev;start] to answer string.
2472 String::WriteToFlat(*subject_handle,
2473 answer->GetChars() + position,
2474 prev,
2475 start);
2476 position += start - prev;
2477 }
2478 prev = end;
2479 next = end;
2480 // Continue from where the match ended, unless it was an empty match.
2481 if (start == end) {
2482 next++;
2483 if (next > length) break;
2484 }
2485 match = RegExpImpl::Exec(regexp_handle,
2486 subject_handle,
2487 next,
2488 last_match_info_handle);
2489 if (match.is_null()) return Failure::Exception();
2490 if (match->IsNull()) break;
2491
2492 ASSERT(last_match_info_handle->HasFastElements());
2493 HandleScope loop_scope;
2494 {
2495 AssertNoAllocation match_info_array_is_not_in_a_handle;
2496 FixedArray* match_info_array =
2497 FixedArray::cast(last_match_info_handle->elements());
2498 start = RegExpImpl::GetCapture(match_info_array, 0);
2499 end = RegExpImpl::GetCapture(match_info_array, 1);
2500 }
2501 } while (true);
2502
2503 if (prev < length) {
2504 // Add substring subject[prev;length] to answer string.
2505 String::WriteToFlat(*subject_handle,
2506 answer->GetChars() + position,
2507 prev,
2508 length);
2509 position += length - prev;
2510 }
2511
2512 if (position == 0) {
2513 return Heap::empty_string();
2514 }
2515
2516 // Shorten string and fill
2517 int string_size = ResultSeqString::SizeFor(position);
2518 int allocated_string_size = ResultSeqString::SizeFor(new_length);
2519 int delta = allocated_string_size - string_size;
2520
2521 answer->set_length(position);
2522 if (delta == 0) return *answer;
2523
2524 Address end_of_string = answer->address() + string_size;
2525 Heap::CreateFillerObjectAt(end_of_string, delta);
2526
2527 return *answer;
2528}
2529
2530
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002531static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2532 ASSERT(args.length() == 4);
2533
2534 CONVERT_CHECKED(String, subject, args[0]);
2535 if (!subject->IsFlat()) {
2536 Object* flat_subject = subject->TryFlatten();
2537 if (flat_subject->IsFailure()) {
2538 return flat_subject;
2539 }
2540 subject = String::cast(flat_subject);
2541 }
2542
2543 CONVERT_CHECKED(String, replacement, args[2]);
2544 if (!replacement->IsFlat()) {
2545 Object* flat_replacement = replacement->TryFlatten();
2546 if (flat_replacement->IsFailure()) {
2547 return flat_replacement;
2548 }
2549 replacement = String::cast(flat_replacement);
2550 }
2551
2552 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2553 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2554
2555 ASSERT(last_match_info->HasFastElements());
2556
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00002557 if (replacement->length() == 0) {
2558 if (subject->HasOnlyAsciiChars()) {
2559 return StringReplaceRegExpWithEmptyString<SeqAsciiString>(
2560 subject, regexp, last_match_info);
2561 } else {
2562 return StringReplaceRegExpWithEmptyString<SeqTwoByteString>(
2563 subject, regexp, last_match_info);
2564 }
2565 }
2566
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002567 return StringReplaceRegExpWithString(subject,
2568 regexp,
2569 replacement,
2570 last_match_info);
2571}
2572
2573
ager@chromium.org7c537e22008-10-16 08:43:32 +00002574// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2575// limit, we can fix the size of tables.
2576static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002577// Reduce alphabet to this size.
2578static const int kBMAlphabetSize = 0x100;
2579// For patterns below this length, the skip length of Boyer-Moore is too short
2580// to compensate for the algorithmic overhead compared to simple brute force.
2581static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002582
ager@chromium.org7c537e22008-10-16 08:43:32 +00002583// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2584// shift. Only allows the last kBMMaxShift characters of the needle
2585// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002586class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002587 public:
2588 BMGoodSuffixBuffers() {}
2589 inline void init(int needle_length) {
2590 ASSERT(needle_length > 1);
2591 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2592 int len = needle_length - start;
2593 biased_suffixes_ = suffixes_ - start;
2594 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2595 for (int i = 0; i <= len; i++) {
2596 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002597 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002598 }
2599 inline int& suffix(int index) {
2600 ASSERT(biased_suffixes_ + index >= suffixes_);
2601 return biased_suffixes_[index];
2602 }
2603 inline int& shift(int index) {
2604 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2605 return biased_good_suffix_shift_[index];
2606 }
2607 private:
2608 int suffixes_[kBMMaxShift + 1];
2609 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002610 int* biased_suffixes_;
2611 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002612 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2613};
2614
2615// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002616static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002617static BMGoodSuffixBuffers bmgs_buffers;
2618
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002619// State of the string match tables.
2620// SIMPLE: No usable content in the buffers.
2621// BOYER_MOORE_HORSPOOL: The bad_char_occurences table has been populated.
2622// BOYER_MOORE: The bmgs_buffers tables have also been populated.
2623// Whenever starting with a new needle, one should call InitializeStringSearch
2624// to determine which search strategy to use, and in the case of a long-needle
2625// strategy, the call also initializes the algorithm to SIMPLE.
2626enum StringSearchAlgorithm { SIMPLE_SEARCH, BOYER_MOORE_HORSPOOL, BOYER_MOORE };
2627static StringSearchAlgorithm algorithm;
2628
2629
ager@chromium.org7c537e22008-10-16 08:43:32 +00002630// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002631template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002632static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern) {
2633 // Only preprocess at most kBMMaxShift last characters of pattern.
2634 int start = pattern.length() < kBMMaxShift ? 0
2635 : pattern.length() - kBMMaxShift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002636 // Run forwards to populate bad_char_table, so that *last* instance
2637 // of character equivalence class is the one registered.
2638 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002639 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2640 : kBMAlphabetSize;
2641 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002642 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002643 } else {
2644 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002645 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002646 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002647 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002648 for (int i = start; i < pattern.length() - 1; i++) {
2649 pchar c = pattern[i];
2650 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002651 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002652 }
2653}
2654
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002655
ager@chromium.org7c537e22008-10-16 08:43:32 +00002656template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002657static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002658 int m = pattern.length();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002659 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002660 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002661 // Compute Good Suffix tables.
2662 bmgs_buffers.init(m);
2663
2664 bmgs_buffers.shift(m-1) = 1;
2665 bmgs_buffers.suffix(m) = m + 1;
2666 pchar last_char = pattern[m - 1];
2667 int suffix = m + 1;
2668 for (int i = m; i > start;) {
2669 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2670 if (bmgs_buffers.shift(suffix) == len) {
2671 bmgs_buffers.shift(suffix) = suffix - i;
2672 }
2673 suffix = bmgs_buffers.suffix(suffix);
2674 }
2675 i--;
2676 suffix--;
2677 bmgs_buffers.suffix(i) = suffix;
2678 if (suffix == m) {
2679 // No suffix to extend, so we check against last_char only.
2680 while (i > start && pattern[i - 1] != last_char) {
2681 if (bmgs_buffers.shift(m) == len) {
2682 bmgs_buffers.shift(m) = m - i;
2683 }
2684 i--;
2685 bmgs_buffers.suffix(i) = m;
2686 }
2687 if (i > start) {
2688 i--;
2689 suffix--;
2690 bmgs_buffers.suffix(i) = suffix;
2691 }
2692 }
2693 }
2694 if (suffix < m) {
2695 for (int i = start; i <= m; i++) {
2696 if (bmgs_buffers.shift(i) == len) {
2697 bmgs_buffers.shift(i) = suffix - start;
2698 }
2699 if (i == suffix) {
2700 suffix = bmgs_buffers.suffix(suffix);
2701 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002702 }
2703 }
2704}
2705
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002706
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002707template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002708static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002709 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002710 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002711 }
2712 if (sizeof(pchar) == 1) {
2713 if (char_code > String::kMaxAsciiCharCode) {
2714 return -1;
2715 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002716 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002717 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002718 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002719}
2720
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002721
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002722// Restricted simplified Boyer-Moore string matching.
2723// Uses only the bad-shift table of Boyer-Moore and only uses it
2724// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002725template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002726static int BoyerMooreHorspool(Vector<const schar> subject,
2727 Vector<const pchar> pattern,
2728 int start_index,
2729 bool* complete) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002730 ASSERT(algorithm <= BOYER_MOORE_HORSPOOL);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002731 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002732 int m = pattern.length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002733
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002734 int badness = -m;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002735
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002736 // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002737 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002738 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002739 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002740 // Perform search
2741 for (idx = start_index; idx <= n - m;) {
2742 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002743 int c;
2744 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002745 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002746 int shift = j - bc_occ;
2747 idx += shift;
2748 badness += 1 - shift; // at most zero, so badness cannot increase.
2749 if (idx > n - m) {
2750 *complete = true;
2751 return -1;
2752 }
2753 }
2754 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002755 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002756 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002757 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002758 return idx;
2759 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002760 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002761 // Badness increases by the number of characters we have
2762 // checked, and decreases by the number of characters we
2763 // can skip by shifting. It's a measure of how we are doing
2764 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002765 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002766 if (badness > 0) {
2767 *complete = false;
2768 return idx;
2769 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002770 }
2771 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002772 *complete = true;
2773 return -1;
2774}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002775
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002776
2777template <typename schar, typename pchar>
2778static int BoyerMooreIndexOf(Vector<const schar> subject,
2779 Vector<const pchar> pattern,
2780 int idx) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002781 ASSERT(algorithm <= BOYER_MOORE);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002782 int n = subject.length();
2783 int m = pattern.length();
2784 // Only preprocess at most kBMMaxShift last characters of pattern.
2785 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2786
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002787 pchar last_char = pattern[m - 1];
2788 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002789 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002790 int j = m - 1;
2791 schar c;
2792 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002793 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002794 idx += shift;
2795 if (idx > n - m) {
2796 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002797 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002798 }
2799 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2800 if (j < 0) {
2801 return idx;
2802 } else if (j < start) {
2803 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002804 // Fall back on BMH shift.
2805 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002806 } else {
2807 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002808 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002809 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002810 if (gs_shift > shift) {
2811 shift = gs_shift;
2812 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002813 idx += shift;
2814 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002815 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002816
2817 return -1;
2818}
2819
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002820
ager@chromium.org7c537e22008-10-16 08:43:32 +00002821// Trivial string search for shorter strings.
2822// On return, if "complete" is set to true, the return value is the
2823// final result of searching for the patter in the subject.
2824// If "complete" is set to false, the return value is the index where
2825// further checking should start, i.e., it's guaranteed that the pattern
2826// does not occur at a position prior to the returned index.
2827template <typename pchar, typename schar>
2828static int SimpleIndexOf(Vector<const schar> subject,
2829 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002830 int idx,
2831 bool* complete) {
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00002832 ASSERT(pattern.length() > 1);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002833 // Badness is a count of how much work we have done. When we have
2834 // done enough work we decide it's probably worth switching to a better
2835 // algorithm.
2836 int badness = -10 - (pattern.length() << 2);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002837
ager@chromium.org7c537e22008-10-16 08:43:32 +00002838 // We know our pattern is at least 2 characters, we cache the first so
2839 // the common case of the first character not matching is faster.
2840 pchar pattern_first_char = pattern[0];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002841 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2842 badness++;
2843 if (badness > 0) {
2844 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002845 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002846 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002847 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2848 const schar* pos = reinterpret_cast<const schar*>(
2849 memchr(subject.start() + i,
2850 pattern_first_char,
2851 n - i + 1));
2852 if (pos == NULL) {
2853 *complete = true;
2854 return -1;
2855 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002856 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002857 } else {
2858 if (subject[i] != pattern_first_char) continue;
2859 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002860 int j = 1;
2861 do {
2862 if (pattern[j] != subject[i+j]) {
2863 break;
2864 }
2865 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002866 } while (j < pattern.length());
2867 if (j == pattern.length()) {
2868 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002869 return i;
2870 }
2871 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002872 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002873 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002874 return -1;
2875}
2876
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002877// Simple indexOf that never bails out. For short patterns only.
2878template <typename pchar, typename schar>
2879static int SimpleIndexOf(Vector<const schar> subject,
2880 Vector<const pchar> pattern,
2881 int idx) {
2882 pchar pattern_first_char = pattern[0];
2883 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002884 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2885 const schar* pos = reinterpret_cast<const schar*>(
2886 memchr(subject.start() + i,
2887 pattern_first_char,
2888 n - i + 1));
2889 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002890 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002891 } else {
2892 if (subject[i] != pattern_first_char) continue;
2893 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002894 int j = 1;
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00002895 while (j < pattern.length()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002896 if (pattern[j] != subject[i+j]) {
2897 break;
2898 }
2899 j++;
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00002900 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002901 if (j == pattern.length()) {
2902 return i;
2903 }
2904 }
2905 return -1;
2906}
2907
2908
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002909// Strategy for searching for a string in another string.
2910enum StringSearchStrategy { SEARCH_FAIL, SEARCH_SHORT, SEARCH_LONG };
ager@chromium.org7c537e22008-10-16 08:43:32 +00002911
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002912
2913template <typename pchar>
2914static inline StringSearchStrategy InitializeStringSearch(
2915 Vector<const pchar> pat, bool ascii_subject) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002916 // We have an ASCII haystack and a non-ASCII needle. Check if there
2917 // really is a non-ASCII character in the needle and bail out if there
2918 // is.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002919 if (ascii_subject && sizeof(pchar) > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002920 for (int i = 0; i < pat.length(); i++) {
2921 uc16 c = pat[i];
2922 if (c > String::kMaxAsciiCharCode) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002923 return SEARCH_FAIL;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002924 }
2925 }
2926 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002927 if (pat.length() < kBMMinPatternLength) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002928 return SEARCH_SHORT;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002929 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002930 algorithm = SIMPLE_SEARCH;
2931 return SEARCH_LONG;
2932}
2933
2934
2935// Dispatch long needle searches to different algorithms.
2936template <typename schar, typename pchar>
2937static int ComplexIndexOf(Vector<const schar> sub,
2938 Vector<const pchar> pat,
2939 int start_index) {
2940 ASSERT(pat.length() >= kBMMinPatternLength);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002941 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002942 bool complete;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002943 int idx = start_index;
2944 switch (algorithm) {
2945 case SIMPLE_SEARCH:
2946 idx = SimpleIndexOf(sub, pat, idx, &complete);
2947 if (complete) return idx;
2948 BoyerMoorePopulateBadCharTable(pat);
2949 algorithm = BOYER_MOORE_HORSPOOL;
2950 // FALLTHROUGH.
2951 case BOYER_MOORE_HORSPOOL:
2952 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
2953 if (complete) return idx;
2954 // Build the Good Suffix table and continue searching.
2955 BoyerMoorePopulateGoodSuffixTable(pat);
2956 algorithm = BOYER_MOORE;
2957 // FALLTHROUGH.
2958 case BOYER_MOORE:
2959 return BoyerMooreIndexOf(sub, pat, idx);
2960 }
2961 UNREACHABLE();
2962 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002963}
2964
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002965
2966// Dispatch to different search strategies for a single search.
2967// If searching multiple times on the same needle, the search
2968// strategy should only be computed once and then dispatch to different
2969// loops.
2970template <typename schar, typename pchar>
2971static int StringSearch(Vector<const schar> sub,
2972 Vector<const pchar> pat,
2973 int start_index) {
2974 bool ascii_subject = (sizeof(schar) == 1);
2975 StringSearchStrategy strategy = InitializeStringSearch(pat, ascii_subject);
2976 switch (strategy) {
2977 case SEARCH_FAIL: return -1;
2978 case SEARCH_SHORT: return SimpleIndexOf(sub, pat, start_index);
2979 case SEARCH_LONG: return ComplexIndexOf(sub, pat, start_index);
2980 }
2981 UNREACHABLE();
2982 return -1;
2983}
2984
2985
ager@chromium.org7c537e22008-10-16 08:43:32 +00002986// Perform string match of pattern on subject, starting at start index.
2987// Caller must ensure that 0 <= start_index <= sub->length(),
2988// and should check that pat->length() + start_index <= sub->length()
2989int Runtime::StringMatch(Handle<String> sub,
2990 Handle<String> pat,
2991 int start_index) {
2992 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002993 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002994
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002995 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002996 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002997
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002998 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002999 if (start_index + pattern_length > subject_length) return -1;
3000
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00003001 if (!sub->IsFlat()) FlattenString(sub);
3002 if (!pat->IsFlat()) FlattenString(pat);
ager@chromium.org236ad962008-09-25 09:45:57 +00003003
ager@chromium.org7c537e22008-10-16 08:43:32 +00003004 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
lrn@chromium.org32d961d2010-06-30 09:09:34 +00003005 // Extract flattened substrings of cons strings before determining asciiness.
3006 String* seq_sub = *sub;
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00003007 if (seq_sub->IsConsString()) seq_sub = ConsString::cast(seq_sub)->first();
lrn@chromium.org32d961d2010-06-30 09:09:34 +00003008 String* seq_pat = *pat;
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00003009 if (seq_pat->IsConsString()) seq_pat = ConsString::cast(seq_pat)->first();
lrn@chromium.org32d961d2010-06-30 09:09:34 +00003010
ager@chromium.org7c537e22008-10-16 08:43:32 +00003011 // dispatch on type of strings
lrn@chromium.org32d961d2010-06-30 09:09:34 +00003012 if (seq_pat->IsAsciiRepresentation()) {
3013 Vector<const char> pat_vector = seq_pat->ToAsciiVector();
3014 if (seq_sub->IsAsciiRepresentation()) {
3015 return StringSearch(seq_sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00003016 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00003017 return StringSearch(seq_sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00003018 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00003019 Vector<const uc16> pat_vector = seq_pat->ToUC16Vector();
3020 if (seq_sub->IsAsciiRepresentation()) {
3021 return StringSearch(seq_sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003022 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00003023 return StringSearch(seq_sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003024}
3025
3026
3027static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003028 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003029 ASSERT(args.length() == 3);
3030
ager@chromium.org7c537e22008-10-16 08:43:32 +00003031 CONVERT_ARG_CHECKED(String, sub, 0);
3032 CONVERT_ARG_CHECKED(String, pat, 1);
3033
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003034 Object* index = args[2];
3035 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003036 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003037
ager@chromium.org870a0b62008-11-04 11:43:05 +00003038 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00003039 int position = Runtime::StringMatch(sub, pat, start_index);
3040 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003041}
3042
3043
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003044template <typename schar, typename pchar>
3045static int StringMatchBackwards(Vector<const schar> sub,
3046 Vector<const pchar> pat,
3047 int idx) {
3048 ASSERT(pat.length() >= 1);
3049 ASSERT(idx + pat.length() <= sub.length());
3050
3051 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
3052 for (int i = 0; i < pat.length(); i++) {
3053 uc16 c = pat[i];
3054 if (c > String::kMaxAsciiCharCode) {
3055 return -1;
3056 }
3057 }
3058 }
3059
3060 pchar pattern_first_char = pat[0];
3061 for (int i = idx; i >= 0; i--) {
3062 if (sub[i] != pattern_first_char) continue;
3063 int j = 1;
3064 while (j < pat.length()) {
3065 if (pat[j] != sub[i+j]) {
3066 break;
3067 }
3068 j++;
3069 }
3070 if (j == pat.length()) {
3071 return i;
3072 }
3073 }
3074 return -1;
3075}
3076
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003077static Object* Runtime_StringLastIndexOf(Arguments args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003078 HandleScope scope; // create a new handle scope
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003079 ASSERT(args.length() == 3);
3080
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003081 CONVERT_ARG_CHECKED(String, sub, 0);
3082 CONVERT_ARG_CHECKED(String, pat, 1);
3083
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003084 Object* index = args[2];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003085 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003086 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003087
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003088 uint32_t pat_length = pat->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003089 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003090
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003091 if (start_index + pat_length > sub_length) {
3092 start_index = sub_length - pat_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00003093 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003094
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003095 if (pat_length == 0) {
3096 return Smi::FromInt(start_index);
3097 }
3098
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00003099 if (!sub->IsFlat()) FlattenString(sub);
3100 if (!pat->IsFlat()) FlattenString(pat);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003101
3102 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
3103
3104 int position = -1;
3105
3106 if (pat->IsAsciiRepresentation()) {
3107 Vector<const char> pat_vector = pat->ToAsciiVector();
3108 if (sub->IsAsciiRepresentation()) {
3109 position = StringMatchBackwards(sub->ToAsciiVector(),
3110 pat_vector,
3111 start_index);
3112 } else {
3113 position = StringMatchBackwards(sub->ToUC16Vector(),
3114 pat_vector,
3115 start_index);
3116 }
3117 } else {
3118 Vector<const uc16> pat_vector = pat->ToUC16Vector();
3119 if (sub->IsAsciiRepresentation()) {
3120 position = StringMatchBackwards(sub->ToAsciiVector(),
3121 pat_vector,
3122 start_index);
3123 } else {
3124 position = StringMatchBackwards(sub->ToUC16Vector(),
3125 pat_vector,
3126 start_index);
3127 }
3128 }
3129
3130 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003131}
3132
3133
3134static Object* Runtime_StringLocaleCompare(Arguments args) {
3135 NoHandleAllocation ha;
3136 ASSERT(args.length() == 2);
3137
3138 CONVERT_CHECKED(String, str1, args[0]);
3139 CONVERT_CHECKED(String, str2, args[1]);
3140
3141 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003142 int str1_length = str1->length();
3143 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003144
3145 // Decide trivial cases without flattening.
3146 if (str1_length == 0) {
3147 if (str2_length == 0) return Smi::FromInt(0); // Equal.
3148 return Smi::FromInt(-str2_length);
3149 } else {
3150 if (str2_length == 0) return Smi::FromInt(str1_length);
3151 }
3152
3153 int end = str1_length < str2_length ? str1_length : str2_length;
3154
3155 // No need to flatten if we are going to find the answer on the first
3156 // character. At this point we know there is at least one character
3157 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003158 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003159 if (d != 0) return Smi::FromInt(d);
3160
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003161 str1->TryFlatten();
3162 str2->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003163
3164 static StringInputBuffer buf1;
3165 static StringInputBuffer buf2;
3166
3167 buf1.Reset(str1);
3168 buf2.Reset(str2);
3169
3170 for (int i = 0; i < end; i++) {
3171 uint16_t char1 = buf1.GetNext();
3172 uint16_t char2 = buf2.GetNext();
3173 if (char1 != char2) return Smi::FromInt(char1 - char2);
3174 }
3175
3176 return Smi::FromInt(str1_length - str2_length);
3177}
3178
3179
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003180static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003181 NoHandleAllocation ha;
3182 ASSERT(args.length() == 3);
3183
3184 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00003185 Object* from = args[1];
3186 Object* to = args[2];
3187 int start, end;
3188 // We have a fast integer-only case here to avoid a conversion to double in
3189 // the common case where from and to are Smis.
3190 if (from->IsSmi() && to->IsSmi()) {
3191 start = Smi::cast(from)->value();
3192 end = Smi::cast(to)->value();
3193 } else {
3194 CONVERT_DOUBLE_CHECKED(from_number, from);
3195 CONVERT_DOUBLE_CHECKED(to_number, to);
3196 start = FastD2I(from_number);
3197 end = FastD2I(to_number);
3198 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003199 RUNTIME_ASSERT(end >= start);
3200 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003201 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003202 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003203 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003204}
3205
3206
ager@chromium.org41826e72009-03-30 13:30:57 +00003207static Object* Runtime_StringMatch(Arguments args) {
3208 ASSERT_EQ(3, args.length());
3209
3210 CONVERT_ARG_CHECKED(String, subject, 0);
3211 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
3212 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
3213 HandleScope handles;
3214
3215 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
3216
3217 if (match.is_null()) {
3218 return Failure::Exception();
3219 }
3220 if (match->IsNull()) {
3221 return Heap::null_value();
3222 }
3223 int length = subject->length();
3224
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00003225 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00003226 ZoneList<int> offsets(8);
3227 do {
3228 int start;
3229 int end;
3230 {
3231 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003232 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00003233 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
3234 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
3235 }
3236 offsets.Add(start);
3237 offsets.Add(end);
3238 int index = start < end ? end : end + 1;
3239 if (index > length) break;
3240 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
3241 if (match.is_null()) {
3242 return Failure::Exception();
3243 }
3244 } while (!match->IsNull());
3245 int matches = offsets.length() / 2;
3246 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
3247 for (int i = 0; i < matches ; i++) {
3248 int from = offsets.at(i * 2);
3249 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003250 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00003251 }
3252 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
3253 result->set_length(Smi::FromInt(matches));
3254 return *result;
3255}
3256
3257
lrn@chromium.org25156de2010-04-06 13:10:27 +00003258// Two smis before and after the match, for very long strings.
3259const int kMaxBuilderEntriesPerRegExpMatch = 5;
3260
3261
3262static void SetLastMatchInfoNoCaptures(Handle<String> subject,
3263 Handle<JSArray> last_match_info,
3264 int match_start,
3265 int match_end) {
3266 // Fill last_match_info with a single capture.
3267 last_match_info->EnsureSize(2 + RegExpImpl::kLastMatchOverhead);
3268 AssertNoAllocation no_gc;
3269 FixedArray* elements = FixedArray::cast(last_match_info->elements());
3270 RegExpImpl::SetLastCaptureCount(elements, 2);
3271 RegExpImpl::SetLastInput(elements, *subject);
3272 RegExpImpl::SetLastSubject(elements, *subject);
3273 RegExpImpl::SetCapture(elements, 0, match_start);
3274 RegExpImpl::SetCapture(elements, 1, match_end);
3275}
3276
3277
lrn@chromium.org25156de2010-04-06 13:10:27 +00003278template <typename schar, typename pchar>
3279static bool SearchStringMultiple(Vector<schar> subject,
3280 String* pattern,
3281 Vector<pchar> pattern_string,
3282 FixedArrayBuilder* builder,
3283 int* match_pos) {
3284 int pos = *match_pos;
3285 int subject_length = subject.length();
3286 int pattern_length = pattern_string.length();
3287 int max_search_start = subject_length - pattern_length;
3288 bool is_ascii = (sizeof(schar) == 1);
3289 StringSearchStrategy strategy =
3290 InitializeStringSearch(pattern_string, is_ascii);
3291 switch (strategy) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003292 case SEARCH_FAIL: break;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003293 case SEARCH_SHORT:
3294 while (pos <= max_search_start) {
3295 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3296 *match_pos = pos;
3297 return false;
3298 }
3299 // Position of end of previous match.
3300 int match_end = pos + pattern_length;
3301 int new_pos = SimpleIndexOf(subject, pattern_string, match_end);
3302 if (new_pos >= 0) {
3303 // A match.
3304 if (new_pos > match_end) {
3305 ReplacementStringBuilder::AddSubjectSlice(builder,
3306 match_end,
3307 new_pos);
3308 }
3309 pos = new_pos;
3310 builder->Add(pattern);
3311 } else {
3312 break;
3313 }
3314 }
3315 break;
3316 case SEARCH_LONG:
3317 while (pos <= max_search_start) {
3318 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003319 *match_pos = pos;
3320 return false;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003321 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003322 int match_end = pos + pattern_length;
3323 int new_pos = ComplexIndexOf(subject, pattern_string, match_end);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003324 if (new_pos >= 0) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003325 // A match has been found.
3326 if (new_pos > match_end) {
3327 ReplacementStringBuilder::AddSubjectSlice(builder,
3328 match_end,
3329 new_pos);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003330 }
3331 pos = new_pos;
3332 builder->Add(pattern);
3333 } else {
3334 break;
3335 }
3336 }
3337 break;
3338 }
3339 if (pos < max_search_start) {
3340 ReplacementStringBuilder::AddSubjectSlice(builder,
3341 pos + pattern_length,
3342 subject_length);
3343 }
3344 *match_pos = pos;
3345 return true;
3346}
3347
3348
3349static bool SearchStringMultiple(Handle<String> subject,
3350 Handle<String> pattern,
3351 Handle<JSArray> last_match_info,
3352 FixedArrayBuilder* builder) {
3353 ASSERT(subject->IsFlat());
3354 ASSERT(pattern->IsFlat());
lrn@chromium.org25156de2010-04-06 13:10:27 +00003355
3356 // Treating as if a previous match was before first character.
3357 int match_pos = -pattern->length();
3358
3359 for (;;) { // Break when search complete.
3360 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3361 AssertNoAllocation no_gc;
3362 if (subject->IsAsciiRepresentation()) {
3363 Vector<const char> subject_vector = subject->ToAsciiVector();
3364 if (pattern->IsAsciiRepresentation()) {
3365 if (SearchStringMultiple(subject_vector,
3366 *pattern,
3367 pattern->ToAsciiVector(),
3368 builder,
3369 &match_pos)) break;
3370 } else {
3371 if (SearchStringMultiple(subject_vector,
3372 *pattern,
3373 pattern->ToUC16Vector(),
3374 builder,
3375 &match_pos)) break;
3376 }
3377 } else {
3378 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3379 if (pattern->IsAsciiRepresentation()) {
3380 if (SearchStringMultiple(subject_vector,
3381 *pattern,
3382 pattern->ToAsciiVector(),
3383 builder,
3384 &match_pos)) break;
3385 } else {
3386 if (SearchStringMultiple(subject_vector,
3387 *pattern,
3388 pattern->ToUC16Vector(),
3389 builder,
3390 &match_pos)) break;
3391 }
3392 }
3393 }
3394
3395 if (match_pos >= 0) {
3396 SetLastMatchInfoNoCaptures(subject,
3397 last_match_info,
3398 match_pos,
3399 match_pos + pattern->length());
3400 return true;
3401 }
3402 return false; // No matches at all.
3403}
3404
3405
3406static RegExpImpl::IrregexpResult SearchRegExpNoCaptureMultiple(
3407 Handle<String> subject,
3408 Handle<JSRegExp> regexp,
3409 Handle<JSArray> last_match_array,
3410 FixedArrayBuilder* builder) {
3411 ASSERT(subject->IsFlat());
3412 int match_start = -1;
3413 int match_end = 0;
3414 int pos = 0;
3415 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3416 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3417
3418 OffsetsVector registers(required_registers);
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00003419 Vector<int32_t> register_vector(registers.vector(), registers.length());
lrn@chromium.org25156de2010-04-06 13:10:27 +00003420 int subject_length = subject->length();
3421
3422 for (;;) { // Break on failure, return on exception.
3423 RegExpImpl::IrregexpResult result =
3424 RegExpImpl::IrregexpExecOnce(regexp,
3425 subject,
3426 pos,
3427 register_vector);
3428 if (result == RegExpImpl::RE_SUCCESS) {
3429 match_start = register_vector[0];
3430 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3431 if (match_end < match_start) {
3432 ReplacementStringBuilder::AddSubjectSlice(builder,
3433 match_end,
3434 match_start);
3435 }
3436 match_end = register_vector[1];
3437 HandleScope loop_scope;
3438 builder->Add(*Factory::NewSubString(subject, match_start, match_end));
3439 if (match_start != match_end) {
3440 pos = match_end;
3441 } else {
3442 pos = match_end + 1;
3443 if (pos > subject_length) break;
3444 }
3445 } else if (result == RegExpImpl::RE_FAILURE) {
3446 break;
3447 } else {
3448 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3449 return result;
3450 }
3451 }
3452
3453 if (match_start >= 0) {
3454 if (match_end < subject_length) {
3455 ReplacementStringBuilder::AddSubjectSlice(builder,
3456 match_end,
3457 subject_length);
3458 }
3459 SetLastMatchInfoNoCaptures(subject,
3460 last_match_array,
3461 match_start,
3462 match_end);
3463 return RegExpImpl::RE_SUCCESS;
3464 } else {
3465 return RegExpImpl::RE_FAILURE; // No matches at all.
3466 }
3467}
3468
3469
3470static RegExpImpl::IrregexpResult SearchRegExpMultiple(
3471 Handle<String> subject,
3472 Handle<JSRegExp> regexp,
3473 Handle<JSArray> last_match_array,
3474 FixedArrayBuilder* builder) {
3475
3476 ASSERT(subject->IsFlat());
3477 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3478 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3479
3480 OffsetsVector registers(required_registers);
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00003481 Vector<int32_t> register_vector(registers.vector(), registers.length());
lrn@chromium.org25156de2010-04-06 13:10:27 +00003482
3483 RegExpImpl::IrregexpResult result =
3484 RegExpImpl::IrregexpExecOnce(regexp,
3485 subject,
3486 0,
3487 register_vector);
3488
3489 int capture_count = regexp->CaptureCount();
3490 int subject_length = subject->length();
3491
3492 // Position to search from.
3493 int pos = 0;
3494 // End of previous match. Differs from pos if match was empty.
3495 int match_end = 0;
3496 if (result == RegExpImpl::RE_SUCCESS) {
3497 // Need to keep a copy of the previous match for creating last_match_info
3498 // at the end, so we have two vectors that we swap between.
3499 OffsetsVector registers2(required_registers);
3500 Vector<int> prev_register_vector(registers2.vector(), registers2.length());
3501
3502 do {
3503 int match_start = register_vector[0];
3504 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3505 if (match_end < match_start) {
3506 ReplacementStringBuilder::AddSubjectSlice(builder,
3507 match_end,
3508 match_start);
3509 }
3510 match_end = register_vector[1];
3511
3512 {
3513 // Avoid accumulating new handles inside loop.
3514 HandleScope temp_scope;
3515 // Arguments array to replace function is match, captures, index and
3516 // subject, i.e., 3 + capture count in total.
3517 Handle<FixedArray> elements = Factory::NewFixedArray(3 + capture_count);
3518 elements->set(0, *Factory::NewSubString(subject,
3519 match_start,
3520 match_end));
3521 for (int i = 1; i <= capture_count; i++) {
3522 int start = register_vector[i * 2];
3523 if (start >= 0) {
3524 int end = register_vector[i * 2 + 1];
3525 ASSERT(start <= end);
3526 Handle<String> substring = Factory::NewSubString(subject,
3527 start,
3528 end);
3529 elements->set(i, *substring);
3530 } else {
3531 ASSERT(register_vector[i * 2 + 1] < 0);
3532 elements->set(i, Heap::undefined_value());
3533 }
3534 }
3535 elements->set(capture_count + 1, Smi::FromInt(match_start));
3536 elements->set(capture_count + 2, *subject);
3537 builder->Add(*Factory::NewJSArrayWithElements(elements));
3538 }
3539 // Swap register vectors, so the last successful match is in
3540 // prev_register_vector.
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00003541 Vector<int32_t> tmp = prev_register_vector;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003542 prev_register_vector = register_vector;
3543 register_vector = tmp;
3544
3545 if (match_end > match_start) {
3546 pos = match_end;
3547 } else {
3548 pos = match_end + 1;
3549 if (pos > subject_length) {
3550 break;
3551 }
3552 }
3553
3554 result = RegExpImpl::IrregexpExecOnce(regexp,
3555 subject,
3556 pos,
3557 register_vector);
3558 } while (result == RegExpImpl::RE_SUCCESS);
3559
3560 if (result != RegExpImpl::RE_EXCEPTION) {
3561 // Finished matching, with at least one match.
3562 if (match_end < subject_length) {
3563 ReplacementStringBuilder::AddSubjectSlice(builder,
3564 match_end,
3565 subject_length);
3566 }
3567
3568 int last_match_capture_count = (capture_count + 1) * 2;
3569 int last_match_array_size =
3570 last_match_capture_count + RegExpImpl::kLastMatchOverhead;
3571 last_match_array->EnsureSize(last_match_array_size);
3572 AssertNoAllocation no_gc;
3573 FixedArray* elements = FixedArray::cast(last_match_array->elements());
3574 RegExpImpl::SetLastCaptureCount(elements, last_match_capture_count);
3575 RegExpImpl::SetLastSubject(elements, *subject);
3576 RegExpImpl::SetLastInput(elements, *subject);
3577 for (int i = 0; i < last_match_capture_count; i++) {
3578 RegExpImpl::SetCapture(elements, i, prev_register_vector[i]);
3579 }
3580 return RegExpImpl::RE_SUCCESS;
3581 }
3582 }
3583 // No matches at all, return failure or exception result directly.
3584 return result;
3585}
3586
3587
3588static Object* Runtime_RegExpExecMultiple(Arguments args) {
3589 ASSERT(args.length() == 4);
3590 HandleScope handles;
3591
3592 CONVERT_ARG_CHECKED(String, subject, 1);
3593 if (!subject->IsFlat()) { FlattenString(subject); }
3594 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
3595 CONVERT_ARG_CHECKED(JSArray, last_match_info, 2);
3596 CONVERT_ARG_CHECKED(JSArray, result_array, 3);
3597
3598 ASSERT(last_match_info->HasFastElements());
3599 ASSERT(regexp->GetFlags().is_global());
3600 Handle<FixedArray> result_elements;
3601 if (result_array->HasFastElements()) {
3602 result_elements =
3603 Handle<FixedArray>(FixedArray::cast(result_array->elements()));
3604 } else {
3605 result_elements = Factory::NewFixedArrayWithHoles(16);
3606 }
3607 FixedArrayBuilder builder(result_elements);
3608
3609 if (regexp->TypeTag() == JSRegExp::ATOM) {
3610 Handle<String> pattern(
3611 String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex)));
lrn@chromium.org25156de2010-04-06 13:10:27 +00003612 if (!pattern->IsFlat()) FlattenString(pattern);
3613 if (SearchStringMultiple(subject, pattern, last_match_info, &builder)) {
3614 return *builder.ToJSArray(result_array);
3615 }
3616 return Heap::null_value();
3617 }
3618
3619 ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
3620
3621 RegExpImpl::IrregexpResult result;
3622 if (regexp->CaptureCount() == 0) {
3623 result = SearchRegExpNoCaptureMultiple(subject,
3624 regexp,
3625 last_match_info,
3626 &builder);
3627 } else {
3628 result = SearchRegExpMultiple(subject, regexp, last_match_info, &builder);
3629 }
3630 if (result == RegExpImpl::RE_SUCCESS) return *builder.ToJSArray(result_array);
3631 if (result == RegExpImpl::RE_FAILURE) return Heap::null_value();
3632 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3633 return Failure::Exception();
3634}
3635
3636
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003637static Object* Runtime_NumberToRadixString(Arguments args) {
3638 NoHandleAllocation ha;
3639 ASSERT(args.length() == 2);
3640
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003641 // Fast case where the result is a one character string.
3642 if (args[0]->IsSmi() && args[1]->IsSmi()) {
3643 int value = Smi::cast(args[0])->value();
3644 int radix = Smi::cast(args[1])->value();
3645 if (value >= 0 && value < radix) {
3646 RUNTIME_ASSERT(radix <= 36);
3647 // Character array used for conversion.
3648 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
3649 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
3650 }
3651 }
3652
3653 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003654 CONVERT_DOUBLE_CHECKED(value, args[0]);
3655 if (isnan(value)) {
3656 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3657 }
3658 if (isinf(value)) {
3659 if (value < 0) {
3660 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3661 }
3662 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3663 }
3664 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
3665 int radix = FastD2I(radix_number);
3666 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3667 char* str = DoubleToRadixCString(value, radix);
3668 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3669 DeleteArray(str);
3670 return result;
3671}
3672
3673
3674static Object* Runtime_NumberToFixed(Arguments args) {
3675 NoHandleAllocation ha;
3676 ASSERT(args.length() == 2);
3677
3678 CONVERT_DOUBLE_CHECKED(value, args[0]);
3679 if (isnan(value)) {
3680 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3681 }
3682 if (isinf(value)) {
3683 if (value < 0) {
3684 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3685 }
3686 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3687 }
3688 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3689 int f = FastD2I(f_number);
3690 RUNTIME_ASSERT(f >= 0);
3691 char* str = DoubleToFixedCString(value, f);
3692 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3693 DeleteArray(str);
3694 return res;
3695}
3696
3697
3698static Object* Runtime_NumberToExponential(Arguments args) {
3699 NoHandleAllocation ha;
3700 ASSERT(args.length() == 2);
3701
3702 CONVERT_DOUBLE_CHECKED(value, args[0]);
3703 if (isnan(value)) {
3704 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3705 }
3706 if (isinf(value)) {
3707 if (value < 0) {
3708 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3709 }
3710 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3711 }
3712 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3713 int f = FastD2I(f_number);
3714 RUNTIME_ASSERT(f >= -1 && f <= 20);
3715 char* str = DoubleToExponentialCString(value, f);
3716 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3717 DeleteArray(str);
3718 return res;
3719}
3720
3721
3722static Object* Runtime_NumberToPrecision(Arguments args) {
3723 NoHandleAllocation ha;
3724 ASSERT(args.length() == 2);
3725
3726 CONVERT_DOUBLE_CHECKED(value, args[0]);
3727 if (isnan(value)) {
3728 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3729 }
3730 if (isinf(value)) {
3731 if (value < 0) {
3732 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3733 }
3734 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3735 }
3736 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3737 int f = FastD2I(f_number);
3738 RUNTIME_ASSERT(f >= 1 && f <= 21);
3739 char* str = DoubleToPrecisionCString(value, f);
3740 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3741 DeleteArray(str);
3742 return res;
3743}
3744
3745
3746// Returns a single character string where first character equals
3747// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003748static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003749 if (index < static_cast<uint32_t>(string->length())) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003750 string->TryFlatten();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003751 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003752 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003753 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003754 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003755}
3756
3757
3758Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
3759 // Handle [] indexing on Strings
3760 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003761 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
3762 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003763 }
3764
3765 // Handle [] indexing on String objects
3766 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003767 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
3768 Handle<Object> result =
3769 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
3770 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003771 }
3772
3773 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003774 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003775 return prototype->GetElement(index);
3776 }
3777
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003778 return GetElement(object, index);
3779}
3780
3781
3782Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003783 return object->GetElement(index);
3784}
3785
3786
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003787Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
3788 HandleScope scope;
3789
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003790 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003791 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003792 Handle<Object> error =
3793 Factory::NewTypeError("non_object_property_load",
3794 HandleVector(args, 2));
3795 return Top::Throw(*error);
3796 }
3797
3798 // Check if the given key is an array index.
3799 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003800 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003801 return GetElementOrCharAt(object, index);
3802 }
3803
3804 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003805 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003806 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003807 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003808 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003809 bool has_pending_exception = false;
3810 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003811 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003812 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003813 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003814 }
3815
ager@chromium.org32912102009-01-16 10:38:43 +00003816 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003817 // the element if so.
3818 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003819 return GetElementOrCharAt(object, index);
3820 } else {
3821 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003822 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003823 }
3824}
3825
3826
3827static Object* Runtime_GetProperty(Arguments args) {
3828 NoHandleAllocation ha;
3829 ASSERT(args.length() == 2);
3830
3831 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003832 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003833
3834 return Runtime::GetObjectProperty(object, key);
3835}
3836
3837
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003838// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003839static Object* Runtime_KeyedGetProperty(Arguments args) {
3840 NoHandleAllocation ha;
3841 ASSERT(args.length() == 2);
3842
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003843 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00003844 // itself.
3845 //
3846 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00003847 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00003848 // global proxy object never has properties. This is the case
3849 // because the global proxy object forwards everything to its hidden
3850 // prototype including local lookups.
3851 //
3852 // Additionally, we need to make sure that we do not cache results
3853 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003854 if (args[0]->IsJSObject() &&
3855 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00003856 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003857 args[1]->IsString()) {
3858 JSObject* receiver = JSObject::cast(args[0]);
3859 String* key = String::cast(args[1]);
3860 if (receiver->HasFastProperties()) {
3861 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003862 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003863 int offset = KeyedLookupCache::Lookup(receiver_map, key);
3864 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003865 Object* value = receiver->FastPropertyAt(offset);
3866 return value->IsTheHole() ? Heap::undefined_value() : value;
3867 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003868 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003869 LookupResult result;
3870 receiver->LocalLookup(key, &result);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00003871 if (result.IsProperty() && result.type() == FIELD) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003872 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003873 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003874 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003875 }
3876 } else {
3877 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003878 StringDictionary* dictionary = receiver->property_dictionary();
3879 int entry = dictionary->FindEntry(key);
3880 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003881 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003882 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003883 if (!receiver->IsGlobalObject()) return value;
3884 value = JSGlobalPropertyCell::cast(value)->value();
3885 if (!value->IsTheHole()) return value;
3886 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003887 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003888 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003889 } else if (args[0]->IsString() && args[1]->IsSmi()) {
3890 // Fast case for string indexing using [] with a smi index.
3891 HandleScope scope;
3892 Handle<String> str = args.at<String>(0);
3893 int index = Smi::cast(args[1])->value();
3894 Handle<Object> result = GetCharAt(str, index);
3895 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003896 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003897
3898 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003899 return Runtime::GetObjectProperty(args.at<Object>(0),
3900 args.at<Object>(1));
3901}
3902
3903
ager@chromium.org5c838252010-02-19 08:53:10 +00003904static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
3905 ASSERT(args.length() == 5);
3906 HandleScope scope;
3907 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3908 CONVERT_CHECKED(String, name, args[1]);
3909 CONVERT_CHECKED(Smi, flag_setter, args[2]);
3910 CONVERT_CHECKED(JSFunction, fun, args[3]);
3911 CONVERT_CHECKED(Smi, flag_attr, args[4]);
3912 int unchecked = flag_attr->value();
3913 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3914 RUNTIME_ASSERT(!obj->IsNull());
3915 LookupResult result;
3916 obj->LocalLookupRealNamedProperty(name, &result);
3917
3918 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3919 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
3920 // delete it to avoid running into trouble in DefineAccessor, which
3921 // handles this incorrectly if the property is readonly (does nothing)
3922 if (result.IsProperty() &&
3923 (result.type() == FIELD || result.type() == NORMAL
3924 || result.type() == CONSTANT_FUNCTION)) {
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00003925 Object* ok = obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
3926 if (ok->IsFailure()) return ok;
ager@chromium.org5c838252010-02-19 08:53:10 +00003927 }
3928 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
3929}
3930
3931static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
3932 ASSERT(args.length() == 4);
3933 HandleScope scope;
3934 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
3935 CONVERT_ARG_CHECKED(String, name, 1);
3936 Handle<Object> obj_value = args.at<Object>(2);
3937
3938 CONVERT_CHECKED(Smi, flag, args[3]);
3939 int unchecked = flag->value();
3940 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3941
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00003942 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3943
3944 // Check if this is an element.
3945 uint32_t index;
3946 bool is_element = name->AsArrayIndex(&index);
3947
3948 // Special case for elements if any of the flags are true.
3949 // If elements are in fast case we always implicitly assume that:
3950 // DONT_DELETE: false, DONT_ENUM: false, READ_ONLY: false.
3951 if (((unchecked & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) &&
3952 is_element) {
3953 // Normalize the elements to enable attributes on the property.
3954 js_object->NormalizeElements();
3955 NumberDictionary* dictionary = js_object->element_dictionary();
3956 // Make sure that we never go back to fast case.
3957 dictionary->set_requires_slow_elements();
3958 PropertyDetails details = PropertyDetails(attr, NORMAL);
3959 dictionary->Set(index, *obj_value, details);
3960 }
3961
ager@chromium.org5c838252010-02-19 08:53:10 +00003962 LookupResult result;
3963 js_object->LocalLookupRealNamedProperty(*name, &result);
3964
ager@chromium.org5c838252010-02-19 08:53:10 +00003965 // Take special care when attributes are different and there is already
3966 // a property. For simplicity we normalize the property which enables us
3967 // to not worry about changing the instance_descriptor and creating a new
3968 // map. The current version of SetObjectProperty does not handle attributes
3969 // correctly in the case where a property is a field and is reset with
3970 // new attributes.
3971 if (result.IsProperty() && attr != result.GetAttributes()) {
3972 // New attributes - normalize to avoid writing to instance descriptor
3973 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3974 // Use IgnoreAttributes version since a readonly property may be
3975 // overridden and SetProperty does not allow this.
3976 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
3977 *obj_value,
3978 attr);
3979 }
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00003980
ager@chromium.org5c838252010-02-19 08:53:10 +00003981 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
3982}
3983
3984
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003985Object* Runtime::SetObjectProperty(Handle<Object> object,
3986 Handle<Object> key,
3987 Handle<Object> value,
3988 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003989 HandleScope scope;
3990
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003991 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003992 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003993 Handle<Object> error =
3994 Factory::NewTypeError("non_object_property_store",
3995 HandleVector(args, 2));
3996 return Top::Throw(*error);
3997 }
3998
3999 // If the object isn't a JavaScript object, we ignore the store.
4000 if (!object->IsJSObject()) return *value;
4001
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004002 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
4003
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004004 // Check if the given key is an array index.
4005 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004006 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004007 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
4008 // of a string using [] notation. We need to support this too in
4009 // JavaScript.
4010 // In the case of a String object we just need to redirect the assignment to
4011 // the underlying string if the index is in range. Since the underlying
4012 // string does nothing with the assignment then we can ignore such
4013 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004014 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004015 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004016 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004017
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004018 Handle<Object> result = SetElement(js_object, index, value);
4019 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004020 return *value;
4021 }
4022
4023 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004024 Handle<Object> result;
4025 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004026 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004027 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004028 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004029 key_string->TryFlatten();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004030 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004031 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004032 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004033 return *value;
4034 }
4035
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004036 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004037 bool has_pending_exception = false;
4038 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4039 if (has_pending_exception) return Failure::Exception();
4040 Handle<String> name = Handle<String>::cast(converted);
4041
4042 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004043 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004044 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004045 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004046 }
4047}
4048
4049
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004050Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
4051 Handle<Object> key,
4052 Handle<Object> value,
4053 PropertyAttributes attr) {
4054 HandleScope scope;
4055
4056 // Check if the given key is an array index.
4057 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004058 if (key->ToArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004059 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
4060 // of a string using [] notation. We need to support this too in
4061 // JavaScript.
4062 // In the case of a String object we just need to redirect the assignment to
4063 // the underlying string if the index is in range. Since the underlying
4064 // string does nothing with the assignment then we can ignore such
4065 // assignments.
4066 if (js_object->IsStringObjectWithCharacterAt(index)) {
4067 return *value;
4068 }
4069
4070 return js_object->SetElement(index, *value);
4071 }
4072
4073 if (key->IsString()) {
4074 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004075 return js_object->SetElement(index, *value);
4076 } else {
4077 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004078 key_string->TryFlatten();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004079 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
4080 *value,
4081 attr);
4082 }
4083 }
4084
4085 // Call-back into JavaScript to convert the key to a string.
4086 bool has_pending_exception = false;
4087 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4088 if (has_pending_exception) return Failure::Exception();
4089 Handle<String> name = Handle<String>::cast(converted);
4090
4091 if (name->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004092 return js_object->SetElement(index, *value);
4093 } else {
4094 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
4095 }
4096}
4097
4098
ager@chromium.orge2902be2009-06-08 12:21:35 +00004099Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
4100 Handle<Object> key) {
4101 HandleScope scope;
4102
4103 // Check if the given key is an array index.
4104 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004105 if (key->ToArrayIndex(&index)) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00004106 // In Firefox/SpiderMonkey, Safari and Opera you can access the
4107 // characters of a string using [] notation. In the case of a
4108 // String object we just need to redirect the deletion to the
4109 // underlying string if the index is in range. Since the
4110 // underlying string does nothing with the deletion, we can ignore
4111 // such deletions.
4112 if (js_object->IsStringObjectWithCharacterAt(index)) {
4113 return Heap::true_value();
4114 }
4115
4116 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
4117 }
4118
4119 Handle<String> key_string;
4120 if (key->IsString()) {
4121 key_string = Handle<String>::cast(key);
4122 } else {
4123 // Call-back into JavaScript to convert the key to a string.
4124 bool has_pending_exception = false;
4125 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4126 if (has_pending_exception) return Failure::Exception();
4127 key_string = Handle<String>::cast(converted);
4128 }
4129
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004130 key_string->TryFlatten();
ager@chromium.orge2902be2009-06-08 12:21:35 +00004131 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
4132}
4133
4134
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004135static Object* Runtime_SetProperty(Arguments args) {
4136 NoHandleAllocation ha;
4137 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
4138
4139 Handle<Object> object = args.at<Object>(0);
4140 Handle<Object> key = args.at<Object>(1);
4141 Handle<Object> value = args.at<Object>(2);
4142
4143 // Compute attributes.
4144 PropertyAttributes attributes = NONE;
4145 if (args.length() == 4) {
4146 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004147 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004148 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004149 RUNTIME_ASSERT(
4150 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4151 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004152 }
4153 return Runtime::SetObjectProperty(object, key, value, attributes);
4154}
4155
4156
4157// Set a local property, even if it is READ_ONLY. If the property does not
4158// exist, it will be added with attributes NONE.
4159static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
4160 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004161 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004162 CONVERT_CHECKED(JSObject, object, args[0]);
4163 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004164 // Compute attributes.
4165 PropertyAttributes attributes = NONE;
4166 if (args.length() == 4) {
4167 CONVERT_CHECKED(Smi, value_obj, args[3]);
4168 int unchecked_value = value_obj->value();
4169 // Only attribute bits should be set.
4170 RUNTIME_ASSERT(
4171 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4172 attributes = static_cast<PropertyAttributes>(unchecked_value);
4173 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004174
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004175 return object->
4176 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004177}
4178
4179
4180static Object* Runtime_DeleteProperty(Arguments args) {
4181 NoHandleAllocation ha;
4182 ASSERT(args.length() == 2);
4183
4184 CONVERT_CHECKED(JSObject, object, args[0]);
4185 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00004186 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004187}
4188
4189
ager@chromium.org9085a012009-05-11 19:22:57 +00004190static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
4191 Handle<String> key) {
4192 if (object->HasLocalProperty(*key)) return Heap::true_value();
4193 // Handle hidden prototypes. If there's a hidden prototype above this thing
4194 // then we have to check it for properties, because they are supposed to
4195 // look like they are on this object.
4196 Handle<Object> proto(object->GetPrototype());
4197 if (proto->IsJSObject() &&
4198 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
4199 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
4200 }
4201 return Heap::false_value();
4202}
4203
4204
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004205static Object* Runtime_HasLocalProperty(Arguments args) {
4206 NoHandleAllocation ha;
4207 ASSERT(args.length() == 2);
4208 CONVERT_CHECKED(String, key, args[1]);
4209
ager@chromium.org9085a012009-05-11 19:22:57 +00004210 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004211 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00004212 if (obj->IsJSObject()) {
4213 JSObject* object = JSObject::cast(obj);
4214 // Fast case - no interceptors.
4215 if (object->HasRealNamedProperty(key)) return Heap::true_value();
4216 // Slow case. Either it's not there or we have an interceptor. We should
4217 // have handles for this kind of deal.
4218 HandleScope scope;
4219 return HasLocalPropertyImplementation(Handle<JSObject>(object),
4220 Handle<String>(key));
4221 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004222 // Well, there is one exception: Handle [] on strings.
4223 uint32_t index;
4224 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00004225 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004226 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004227 return Heap::true_value();
4228 }
4229 }
4230 return Heap::false_value();
4231}
4232
4233
4234static Object* Runtime_HasProperty(Arguments args) {
4235 NoHandleAllocation na;
4236 ASSERT(args.length() == 2);
4237
4238 // Only JS objects can have properties.
4239 if (args[0]->IsJSObject()) {
4240 JSObject* object = JSObject::cast(args[0]);
4241 CONVERT_CHECKED(String, key, args[1]);
4242 if (object->HasProperty(key)) return Heap::true_value();
4243 }
4244 return Heap::false_value();
4245}
4246
4247
4248static Object* Runtime_HasElement(Arguments args) {
4249 NoHandleAllocation na;
4250 ASSERT(args.length() == 2);
4251
4252 // Only JS objects can have elements.
4253 if (args[0]->IsJSObject()) {
4254 JSObject* object = JSObject::cast(args[0]);
4255 CONVERT_CHECKED(Smi, index_obj, args[1]);
4256 uint32_t index = index_obj->value();
4257 if (object->HasElement(index)) return Heap::true_value();
4258 }
4259 return Heap::false_value();
4260}
4261
4262
4263static Object* Runtime_IsPropertyEnumerable(Arguments args) {
4264 NoHandleAllocation ha;
4265 ASSERT(args.length() == 2);
4266
4267 CONVERT_CHECKED(JSObject, object, args[0]);
4268 CONVERT_CHECKED(String, key, args[1]);
4269
4270 uint32_t index;
4271 if (key->AsArrayIndex(&index)) {
4272 return Heap::ToBoolean(object->HasElement(index));
4273 }
4274
ager@chromium.org870a0b62008-11-04 11:43:05 +00004275 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
4276 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004277}
4278
4279
4280static Object* Runtime_GetPropertyNames(Arguments args) {
4281 HandleScope scope;
4282 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004283 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004284 return *GetKeysFor(object);
4285}
4286
4287
4288// Returns either a FixedArray as Runtime_GetPropertyNames,
4289// or, if the given object has an enum cache that contains
4290// all enumerable properties of the object and its prototypes
4291// have none, the map of the object. This is used to speed up
4292// the check for deletions during a for-in.
4293static Object* Runtime_GetPropertyNamesFast(Arguments args) {
4294 ASSERT(args.length() == 1);
4295
4296 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4297
4298 if (raw_object->IsSimpleEnum()) return raw_object->map();
4299
4300 HandleScope scope;
4301 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004302 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
4303 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004304
4305 // Test again, since cache may have been built by preceding call.
4306 if (object->IsSimpleEnum()) return object->map();
4307
4308 return *content;
4309}
4310
4311
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004312// Find the length of the prototype chain that is to to handled as one. If a
4313// prototype object is hidden it is to be viewed as part of the the object it
4314// is prototype for.
4315static int LocalPrototypeChainLength(JSObject* obj) {
4316 int count = 1;
4317 Object* proto = obj->GetPrototype();
4318 while (proto->IsJSObject() &&
4319 JSObject::cast(proto)->map()->is_hidden_prototype()) {
4320 count++;
4321 proto = JSObject::cast(proto)->GetPrototype();
4322 }
4323 return count;
4324}
4325
4326
4327// Return the names of the local named properties.
4328// args[0]: object
4329static Object* Runtime_GetLocalPropertyNames(Arguments args) {
4330 HandleScope scope;
4331 ASSERT(args.length() == 1);
4332 if (!args[0]->IsJSObject()) {
4333 return Heap::undefined_value();
4334 }
4335 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4336
4337 // Skip the global proxy as it has no properties and always delegates to the
4338 // real global object.
4339 if (obj->IsJSGlobalProxy()) {
4340 // Only collect names if access is permitted.
4341 if (obj->IsAccessCheckNeeded() &&
4342 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
4343 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
4344 return *Factory::NewJSArray(0);
4345 }
4346 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
4347 }
4348
4349 // Find the number of objects making up this.
4350 int length = LocalPrototypeChainLength(*obj);
4351
4352 // Find the number of local properties for each of the objects.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004353 ScopedVector<int> local_property_count(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004354 int total_property_count = 0;
4355 Handle<JSObject> jsproto = obj;
4356 for (int i = 0; i < length; i++) {
4357 // Only collect names if access is permitted.
4358 if (jsproto->IsAccessCheckNeeded() &&
4359 !Top::MayNamedAccess(*jsproto,
4360 Heap::undefined_value(),
4361 v8::ACCESS_KEYS)) {
4362 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
4363 return *Factory::NewJSArray(0);
4364 }
4365 int n;
4366 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4367 local_property_count[i] = n;
4368 total_property_count += n;
4369 if (i < length - 1) {
4370 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4371 }
4372 }
4373
4374 // Allocate an array with storage for all the property names.
4375 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
4376
4377 // Get the property names.
4378 jsproto = obj;
4379 int proto_with_hidden_properties = 0;
4380 for (int i = 0; i < length; i++) {
4381 jsproto->GetLocalPropertyNames(*names,
4382 i == 0 ? 0 : local_property_count[i - 1]);
4383 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
4384 proto_with_hidden_properties++;
4385 }
4386 if (i < length - 1) {
4387 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4388 }
4389 }
4390
4391 // Filter out name of hidden propeties object.
4392 if (proto_with_hidden_properties > 0) {
4393 Handle<FixedArray> old_names = names;
4394 names = Factory::NewFixedArray(
4395 names->length() - proto_with_hidden_properties);
4396 int dest_pos = 0;
4397 for (int i = 0; i < total_property_count; i++) {
4398 Object* name = old_names->get(i);
4399 if (name == Heap::hidden_symbol()) {
4400 continue;
4401 }
4402 names->set(dest_pos++, name);
4403 }
4404 }
4405
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004406 return *Factory::NewJSArrayWithElements(names);
4407}
4408
4409
4410// Return the names of the local indexed properties.
4411// args[0]: object
4412static Object* Runtime_GetLocalElementNames(Arguments args) {
4413 HandleScope scope;
4414 ASSERT(args.length() == 1);
4415 if (!args[0]->IsJSObject()) {
4416 return Heap::undefined_value();
4417 }
4418 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4419
4420 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4421 Handle<FixedArray> names = Factory::NewFixedArray(n);
4422 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4423 return *Factory::NewJSArrayWithElements(names);
4424}
4425
4426
4427// Return information on whether an object has a named or indexed interceptor.
4428// args[0]: object
4429static Object* Runtime_GetInterceptorInfo(Arguments args) {
4430 HandleScope scope;
4431 ASSERT(args.length() == 1);
4432 if (!args[0]->IsJSObject()) {
4433 return Smi::FromInt(0);
4434 }
4435 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4436
4437 int result = 0;
4438 if (obj->HasNamedInterceptor()) result |= 2;
4439 if (obj->HasIndexedInterceptor()) result |= 1;
4440
4441 return Smi::FromInt(result);
4442}
4443
4444
4445// Return property names from named interceptor.
4446// args[0]: object
4447static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
4448 HandleScope scope;
4449 ASSERT(args.length() == 1);
4450 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4451
4452 if (obj->HasNamedInterceptor()) {
4453 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4454 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4455 }
4456 return Heap::undefined_value();
4457}
4458
4459
4460// Return element names from indexed interceptor.
4461// args[0]: object
4462static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
4463 HandleScope scope;
4464 ASSERT(args.length() == 1);
4465 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4466
4467 if (obj->HasIndexedInterceptor()) {
4468 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4469 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4470 }
4471 return Heap::undefined_value();
4472}
4473
4474
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004475static Object* Runtime_LocalKeys(Arguments args) {
4476 ASSERT_EQ(args.length(), 1);
4477 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4478 HandleScope scope;
4479 Handle<JSObject> object(raw_object);
4480 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
4481 LOCAL_ONLY);
4482 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
4483 // property array and since the result is mutable we have to create
4484 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004485 int length = contents->length();
4486 Handle<FixedArray> copy = Factory::NewFixedArray(length);
4487 for (int i = 0; i < length; i++) {
4488 Object* entry = contents->get(i);
4489 if (entry->IsString()) {
4490 copy->set(i, entry);
4491 } else {
4492 ASSERT(entry->IsNumber());
4493 HandleScope scope;
4494 Handle<Object> entry_handle(entry);
4495 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
4496 copy->set(i, *entry_str);
4497 }
4498 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004499 return *Factory::NewJSArrayWithElements(copy);
4500}
4501
4502
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004503static Object* Runtime_GetArgumentsProperty(Arguments args) {
4504 NoHandleAllocation ha;
4505 ASSERT(args.length() == 1);
4506
4507 // Compute the frame holding the arguments.
4508 JavaScriptFrameIterator it;
4509 it.AdvanceToArgumentsFrame();
4510 JavaScriptFrame* frame = it.frame();
4511
4512 // Get the actual number of provided arguments.
4513 const uint32_t n = frame->GetProvidedParametersCount();
4514
4515 // Try to convert the key to an index. If successful and within
4516 // index return the the argument from the frame.
4517 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004518 if (args[0]->ToArrayIndex(&index) && index < n) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004519 return frame->GetParameter(index);
4520 }
4521
4522 // Convert the key to a string.
4523 HandleScope scope;
4524 bool exception = false;
4525 Handle<Object> converted =
4526 Execution::ToString(args.at<Object>(0), &exception);
4527 if (exception) return Failure::Exception();
4528 Handle<String> key = Handle<String>::cast(converted);
4529
4530 // Try to convert the string key into an array index.
4531 if (key->AsArrayIndex(&index)) {
4532 if (index < n) {
4533 return frame->GetParameter(index);
4534 } else {
4535 return Top::initial_object_prototype()->GetElement(index);
4536 }
4537 }
4538
4539 // Handle special arguments properties.
4540 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
4541 if (key->Equals(Heap::callee_symbol())) return frame->function();
4542
4543 // Lookup in the initial Object.prototype object.
4544 return Top::initial_object_prototype()->GetProperty(*key);
4545}
4546
4547
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004548static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004549 HandleScope scope;
4550
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004551 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004552 Handle<Object> object = args.at<Object>(0);
4553 if (object->IsJSObject()) {
4554 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00004555 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
4556 js_object->TransformToFastProperties(0);
4557 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004558 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004559 return *object;
4560}
4561
4562
4563static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004564 HandleScope scope;
4565
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004566 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004567 Handle<Object> object = args.at<Object>(0);
4568 if (object->IsJSObject()) {
4569 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004570 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004571 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004572 return *object;
4573}
4574
4575
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004576static Object* Runtime_ToBool(Arguments args) {
4577 NoHandleAllocation ha;
4578 ASSERT(args.length() == 1);
4579
4580 return args[0]->ToBoolean();
4581}
4582
4583
4584// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
4585// Possible optimizations: put the type string into the oddballs.
4586static Object* Runtime_Typeof(Arguments args) {
4587 NoHandleAllocation ha;
4588
4589 Object* obj = args[0];
4590 if (obj->IsNumber()) return Heap::number_symbol();
4591 HeapObject* heap_obj = HeapObject::cast(obj);
4592
4593 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004594 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004595
4596 InstanceType instance_type = heap_obj->map()->instance_type();
4597 if (instance_type < FIRST_NONSTRING_TYPE) {
4598 return Heap::string_symbol();
4599 }
4600
4601 switch (instance_type) {
4602 case ODDBALL_TYPE:
4603 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
4604 return Heap::boolean_symbol();
4605 }
4606 if (heap_obj->IsNull()) {
4607 return Heap::object_symbol();
4608 }
4609 ASSERT(heap_obj->IsUndefined());
4610 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004611 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004612 return Heap::function_symbol();
4613 default:
4614 // For any kind of object not handled above, the spec rule for
4615 // host objects gives that it is okay to return "object"
4616 return Heap::object_symbol();
4617 }
4618}
4619
4620
lrn@chromium.org25156de2010-04-06 13:10:27 +00004621static bool AreDigits(const char*s, int from, int to) {
4622 for (int i = from; i < to; i++) {
4623 if (s[i] < '0' || s[i] > '9') return false;
4624 }
4625
4626 return true;
4627}
4628
4629
4630static int ParseDecimalInteger(const char*s, int from, int to) {
4631 ASSERT(to - from < 10); // Overflow is not possible.
4632 ASSERT(from < to);
4633 int d = s[from] - '0';
4634
4635 for (int i = from + 1; i < to; i++) {
4636 d = 10 * d + (s[i] - '0');
4637 }
4638
4639 return d;
4640}
4641
4642
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004643static Object* Runtime_StringToNumber(Arguments args) {
4644 NoHandleAllocation ha;
4645 ASSERT(args.length() == 1);
4646 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004647 subject->TryFlatten();
lrn@chromium.org25156de2010-04-06 13:10:27 +00004648
4649 // Fast case: short integer or some sorts of junk values.
4650 int len = subject->length();
4651 if (subject->IsSeqAsciiString()) {
4652 if (len == 0) return Smi::FromInt(0);
4653
4654 char const* data = SeqAsciiString::cast(subject)->GetChars();
4655 bool minus = (data[0] == '-');
4656 int start_pos = (minus ? 1 : 0);
4657
4658 if (start_pos == len) {
4659 return Heap::nan_value();
4660 } else if (data[start_pos] > '9') {
4661 // Fast check for a junk value. A valid string may start from a
4662 // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit or
4663 // the 'I' character ('Infinity'). All of that have codes not greater than
4664 // '9' except 'I'.
4665 if (data[start_pos] != 'I') {
4666 return Heap::nan_value();
4667 }
4668 } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
4669 // The maximal/minimal smi has 10 digits. If the string has less digits we
4670 // know it will fit into the smi-data type.
4671 int d = ParseDecimalInteger(data, start_pos, len);
4672 if (minus) {
4673 if (d == 0) return Heap::minus_zero_value();
4674 d = -d;
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00004675 } else if (!subject->HasHashCode() &&
4676 len <= String::kMaxArrayIndexSize &&
4677 (len == 1 || data[0] != '0')) {
4678 // String hash is not calculated yet but all the data are present.
4679 // Update the hash field to speed up sequential convertions.
ager@chromium.org5b2fbee2010-09-08 06:38:15 +00004680 uint32_t hash = StringHasher::MakeArrayIndexHash(d, len);
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00004681#ifdef DEBUG
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00004682 subject->Hash(); // Force hash calculation.
4683 ASSERT_EQ(static_cast<int>(subject->hash_field()),
4684 static_cast<int>(hash));
4685#endif
4686 subject->set_hash_field(hash);
lrn@chromium.org25156de2010-04-06 13:10:27 +00004687 }
4688 return Smi::FromInt(d);
4689 }
4690 }
4691
4692 // Slower case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004693 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
4694}
4695
4696
4697static Object* Runtime_StringFromCharCodeArray(Arguments args) {
4698 NoHandleAllocation ha;
4699 ASSERT(args.length() == 1);
4700
4701 CONVERT_CHECKED(JSArray, codes, args[0]);
4702 int length = Smi::cast(codes->length())->value();
4703
4704 // Check if the string can be ASCII.
4705 int i;
4706 for (i = 0; i < length; i++) {
4707 Object* element = codes->GetElement(i);
4708 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
4709 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
4710 break;
4711 }
4712
4713 Object* object = NULL;
4714 if (i == length) { // The string is ASCII.
4715 object = Heap::AllocateRawAsciiString(length);
4716 } else { // The string is not ASCII.
4717 object = Heap::AllocateRawTwoByteString(length);
4718 }
4719
4720 if (object->IsFailure()) return object;
4721 String* result = String::cast(object);
4722 for (int i = 0; i < length; i++) {
4723 Object* element = codes->GetElement(i);
4724 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004725 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004726 }
4727 return result;
4728}
4729
4730
4731// kNotEscaped is generated by the following:
4732//
4733// #!/bin/perl
4734// for (my $i = 0; $i < 256; $i++) {
4735// print "\n" if $i % 16 == 0;
4736// my $c = chr($i);
4737// my $escaped = 1;
4738// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
4739// print $escaped ? "0, " : "1, ";
4740// }
4741
4742
4743static bool IsNotEscaped(uint16_t character) {
4744 // Only for 8 bit characters, the rest are always escaped (in a different way)
4745 ASSERT(character < 256);
4746 static const char kNotEscaped[256] = {
4747 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4748 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4749 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
4750 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
4751 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4752 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
4753 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4754 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
4755 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4756 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4757 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4758 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4759 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4760 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4761 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4762 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4763 };
4764 return kNotEscaped[character] != 0;
4765}
4766
4767
4768static Object* Runtime_URIEscape(Arguments args) {
4769 const char hex_chars[] = "0123456789ABCDEF";
4770 NoHandleAllocation ha;
4771 ASSERT(args.length() == 1);
4772 CONVERT_CHECKED(String, source, args[0]);
4773
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004774 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004775
4776 int escaped_length = 0;
4777 int length = source->length();
4778 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004779 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004780 buffer->Reset(source);
4781 while (buffer->has_more()) {
4782 uint16_t character = buffer->GetNext();
4783 if (character >= 256) {
4784 escaped_length += 6;
4785 } else if (IsNotEscaped(character)) {
4786 escaped_length++;
4787 } else {
4788 escaped_length += 3;
4789 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004790 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004791 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004792 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004793 Top::context()->mark_out_of_memory();
4794 return Failure::OutOfMemoryException();
4795 }
4796 }
4797 }
4798 // No length change implies no change. Return original string if no change.
4799 if (escaped_length == length) {
4800 return source;
4801 }
4802 Object* o = Heap::AllocateRawAsciiString(escaped_length);
4803 if (o->IsFailure()) return o;
4804 String* destination = String::cast(o);
4805 int dest_position = 0;
4806
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004807 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004808 buffer->Rewind();
4809 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00004810 uint16_t chr = buffer->GetNext();
4811 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004812 destination->Set(dest_position, '%');
4813 destination->Set(dest_position+1, 'u');
4814 destination->Set(dest_position+2, hex_chars[chr >> 12]);
4815 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
4816 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
4817 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004818 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00004819 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004820 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004821 dest_position++;
4822 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004823 destination->Set(dest_position, '%');
4824 destination->Set(dest_position+1, hex_chars[chr >> 4]);
4825 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004826 dest_position += 3;
4827 }
4828 }
4829 return destination;
4830}
4831
4832
4833static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
4834 static const signed char kHexValue['g'] = {
4835 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4836 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4837 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4838 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
4839 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4840 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4841 -1, 10, 11, 12, 13, 14, 15 };
4842
4843 if (character1 > 'f') return -1;
4844 int hi = kHexValue[character1];
4845 if (hi == -1) return -1;
4846 if (character2 > 'f') return -1;
4847 int lo = kHexValue[character2];
4848 if (lo == -1) return -1;
4849 return (hi << 4) + lo;
4850}
4851
4852
ager@chromium.org870a0b62008-11-04 11:43:05 +00004853static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004854 int i,
4855 int length,
4856 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004857 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00004858 int32_t hi = 0;
4859 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004860 if (character == '%' &&
4861 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004862 source->Get(i + 1) == 'u' &&
4863 (hi = TwoDigitHex(source->Get(i + 2),
4864 source->Get(i + 3))) != -1 &&
4865 (lo = TwoDigitHex(source->Get(i + 4),
4866 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004867 *step = 6;
4868 return (hi << 8) + lo;
4869 } else if (character == '%' &&
4870 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004871 (lo = TwoDigitHex(source->Get(i + 1),
4872 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004873 *step = 3;
4874 return lo;
4875 } else {
4876 *step = 1;
4877 return character;
4878 }
4879}
4880
4881
4882static Object* Runtime_URIUnescape(Arguments args) {
4883 NoHandleAllocation ha;
4884 ASSERT(args.length() == 1);
4885 CONVERT_CHECKED(String, source, args[0]);
4886
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004887 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004888
4889 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004890 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004891
4892 int unescaped_length = 0;
4893 for (int i = 0; i < length; unescaped_length++) {
4894 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004895 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004896 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004897 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004898 i += step;
4899 }
4900
4901 // No length change implies no change. Return original string if no change.
4902 if (unescaped_length == length)
4903 return source;
4904
4905 Object* o = ascii ?
4906 Heap::AllocateRawAsciiString(unescaped_length) :
4907 Heap::AllocateRawTwoByteString(unescaped_length);
4908 if (o->IsFailure()) return o;
4909 String* destination = String::cast(o);
4910
4911 int dest_position = 0;
4912 for (int i = 0; i < length; dest_position++) {
4913 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004914 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004915 i += step;
4916 }
4917 return destination;
4918}
4919
4920
4921static Object* Runtime_StringParseInt(Arguments args) {
4922 NoHandleAllocation ha;
4923
4924 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004925 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004926
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004927 s->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004928
lrn@chromium.org25156de2010-04-06 13:10:27 +00004929 RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
4930 double value = StringToInt(s, radix);
4931 return Heap::NumberFromDouble(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004932 return Heap::nan_value();
4933}
4934
4935
4936static Object* Runtime_StringParseFloat(Arguments args) {
4937 NoHandleAllocation ha;
4938 CONVERT_CHECKED(String, str, args[0]);
4939
4940 // ECMA-262 section 15.1.2.3, empty string is NaN
4941 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
4942
4943 // Create a number object from the value.
4944 return Heap::NumberFromDouble(value);
4945}
4946
4947
4948static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
4949static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
4950
4951
4952template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004953static Object* ConvertCaseHelper(String* s,
4954 int length,
4955 int input_string_length,
4956 unibrow::Mapping<Converter, 128>* mapping) {
4957 // We try this twice, once with the assumption that the result is no longer
4958 // than the input and, if that assumption breaks, again with the exact
4959 // length. This may not be pretty, but it is nicer than what was here before
4960 // and I hereby claim my vaffel-is.
4961 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004962 // Allocate the resulting string.
4963 //
4964 // NOTE: This assumes that the upper/lower case of an ascii
4965 // character is also ascii. This is currently the case, but it
4966 // might break in the future if we implement more context and locale
4967 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00004968 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004969 ? Heap::AllocateRawAsciiString(length)
4970 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004971 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004972 String* result = String::cast(o);
4973 bool has_changed_character = false;
4974
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004975 // Convert all characters to upper case, assuming that they will fit
4976 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004977 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004978 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004979 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004980 // We can assume that the string is not empty
4981 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004982 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00004983 bool has_next = buffer->has_more();
4984 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004985 int char_length = mapping->get(current, next, chars);
4986 if (char_length == 0) {
4987 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004988 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004989 i++;
4990 } else if (char_length == 1) {
4991 // Common case: converting the letter resulted in one character.
4992 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004993 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004994 has_changed_character = true;
4995 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004996 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004997 // We've assumed that the result would be as long as the
4998 // input but here is a character that converts to several
4999 // characters. No matter, we calculate the exact length
5000 // of the result and try the whole thing again.
5001 //
5002 // Note that this leaves room for optimization. We could just
5003 // memcpy what we already have to the result string. Also,
5004 // the result string is the last object allocated we could
5005 // "realloc" it and probably, in the vast majority of cases,
5006 // extend the existing string to be able to hold the full
5007 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00005008 int next_length = 0;
5009 if (has_next) {
5010 next_length = mapping->get(next, 0, chars);
5011 if (next_length == 0) next_length = 1;
5012 }
5013 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005014 while (buffer->has_more()) {
5015 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00005016 // NOTE: we use 0 as the next character here because, while
5017 // the next character may affect what a character converts to,
5018 // it does not in any case affect the length of what it convert
5019 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005020 int char_length = mapping->get(current, 0, chars);
5021 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00005022 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005023 if (current_length > Smi::kMaxValue) {
5024 Top::context()->mark_out_of_memory();
5025 return Failure::OutOfMemoryException();
5026 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005027 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005028 // Try again with the real length.
5029 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005030 } else {
5031 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005032 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005033 i++;
5034 }
5035 has_changed_character = true;
5036 }
5037 current = next;
5038 }
5039 if (has_changed_character) {
5040 return result;
5041 } else {
5042 // If we didn't actually change anything in doing the conversion
5043 // we simple return the result and let the converted string
5044 // become garbage; there is no reason to keep two identical strings
5045 // alive.
5046 return s;
5047 }
5048}
5049
5050
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005051namespace {
5052
5053struct ToLowerTraits {
5054 typedef unibrow::ToLowercase UnibrowConverter;
5055
5056 static bool ConvertAscii(char* dst, char* src, int length) {
5057 bool changed = false;
5058 for (int i = 0; i < length; ++i) {
5059 char c = src[i];
5060 if ('A' <= c && c <= 'Z') {
5061 c += ('a' - 'A');
5062 changed = true;
5063 }
5064 dst[i] = c;
5065 }
5066 return changed;
5067 }
5068};
5069
5070
5071struct ToUpperTraits {
5072 typedef unibrow::ToUppercase UnibrowConverter;
5073
5074 static bool ConvertAscii(char* dst, char* src, int length) {
5075 bool changed = false;
5076 for (int i = 0; i < length; ++i) {
5077 char c = src[i];
5078 if ('a' <= c && c <= 'z') {
5079 c -= ('a' - 'A');
5080 changed = true;
5081 }
5082 dst[i] = c;
5083 }
5084 return changed;
5085 }
5086};
5087
5088} // namespace
5089
5090
5091template <typename ConvertTraits>
5092static Object* ConvertCase(
5093 Arguments args,
5094 unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005095 NoHandleAllocation ha;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005096 CONVERT_CHECKED(String, s, args[0]);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005097 s = s->TryFlattenGetString();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005098
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005099 const int length = s->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005100 // Assume that the string is not empty; we need this assumption later
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005101 if (length == 0) return s;
5102
5103 // Simpler handling of ascii strings.
5104 //
5105 // NOTE: This assumes that the upper/lower case of an ascii
5106 // character is also ascii. This is currently the case, but it
5107 // might break in the future if we implement more context and locale
5108 // dependent upper/lower conversions.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005109 if (s->IsSeqAsciiString()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005110 Object* o = Heap::AllocateRawAsciiString(length);
5111 if (o->IsFailure()) return o;
5112 SeqAsciiString* result = SeqAsciiString::cast(o);
5113 bool has_changed_character = ConvertTraits::ConvertAscii(
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005114 result->GetChars(), SeqAsciiString::cast(s)->GetChars(), length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005115 return has_changed_character ? result : s;
5116 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005117
5118 Object* answer = ConvertCaseHelper(s, length, length, mapping);
5119 if (answer->IsSmi()) {
5120 // Retry with correct length.
5121 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
5122 }
5123 return answer; // This may be a failure.
5124}
5125
5126
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005127static Object* Runtime_StringToLowerCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005128 return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005129}
5130
5131
5132static Object* Runtime_StringToUpperCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005133 return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005134}
5135
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005136
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005137static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
5138 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
5139}
5140
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005141
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005142static Object* Runtime_StringTrim(Arguments args) {
5143 NoHandleAllocation ha;
5144 ASSERT(args.length() == 3);
5145
5146 CONVERT_CHECKED(String, s, args[0]);
5147 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
5148 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
5149
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005150 s->TryFlatten();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005151 int length = s->length();
5152
5153 int left = 0;
5154 if (trimLeft) {
5155 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
5156 left++;
5157 }
5158 }
5159
5160 int right = length;
5161 if (trimRight) {
5162 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
5163 right--;
5164 }
5165 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005166 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005167}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005168
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005169
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005170template <typename schar, typename pchar>
5171void FindStringIndices(Vector<const schar> subject,
5172 Vector<const pchar> pattern,
5173 ZoneList<int>* indices,
5174 unsigned int limit) {
5175 ASSERT(limit > 0);
5176 // Collect indices of pattern in subject, and the end-of-string index.
5177 // Stop after finding at most limit values.
5178 StringSearchStrategy strategy =
5179 InitializeStringSearch(pattern, sizeof(schar) == 1);
5180 switch (strategy) {
5181 case SEARCH_FAIL: return;
5182 case SEARCH_SHORT: {
5183 int pattern_length = pattern.length();
5184 int index = 0;
5185 while (limit > 0) {
5186 index = SimpleIndexOf(subject, pattern, index);
5187 if (index < 0) return;
5188 indices->Add(index);
5189 index += pattern_length;
5190 limit--;
5191 }
5192 return;
5193 }
5194 case SEARCH_LONG: {
5195 int pattern_length = pattern.length();
5196 int index = 0;
5197 while (limit > 0) {
5198 index = ComplexIndexOf(subject, pattern, index);
5199 if (index < 0) return;
5200 indices->Add(index);
5201 index += pattern_length;
5202 limit--;
5203 }
5204 return;
5205 }
5206 default:
5207 UNREACHABLE();
5208 return;
5209 }
5210}
5211
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005212
5213static Object* Runtime_StringSplit(Arguments args) {
5214 ASSERT(args.length() == 3);
5215 HandleScope handle_scope;
5216 CONVERT_ARG_CHECKED(String, subject, 0);
5217 CONVERT_ARG_CHECKED(String, pattern, 1);
5218 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
5219
5220 int subject_length = subject->length();
5221 int pattern_length = pattern->length();
5222 RUNTIME_ASSERT(pattern_length > 0);
5223
5224 // The limit can be very large (0xffffffffu), but since the pattern
5225 // isn't empty, we can never create more parts than ~half the length
5226 // of the subject.
5227
5228 if (!subject->IsFlat()) FlattenString(subject);
5229
5230 static const int kMaxInitialListCapacity = 16;
5231
5232 ZoneScope scope(DELETE_ON_EXIT);
5233
5234 // Find (up to limit) indices of separator and end-of-string in subject
5235 int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
5236 ZoneList<int> indices(initial_capacity);
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00005237 if (!pattern->IsFlat()) FlattenString(pattern);
5238
5239 // No allocation block.
5240 {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005241 AssertNoAllocation nogc;
5242 if (subject->IsAsciiRepresentation()) {
5243 Vector<const char> subject_vector = subject->ToAsciiVector();
5244 if (pattern->IsAsciiRepresentation()) {
5245 FindStringIndices(subject_vector,
5246 pattern->ToAsciiVector(),
5247 &indices,
5248 limit);
5249 } else {
5250 FindStringIndices(subject_vector,
5251 pattern->ToUC16Vector(),
5252 &indices,
5253 limit);
5254 }
5255 } else {
5256 Vector<const uc16> subject_vector = subject->ToUC16Vector();
5257 if (pattern->IsAsciiRepresentation()) {
5258 FindStringIndices(subject_vector,
5259 pattern->ToAsciiVector(),
5260 &indices,
5261 limit);
5262 } else {
5263 FindStringIndices(subject_vector,
5264 pattern->ToUC16Vector(),
5265 &indices,
5266 limit);
5267 }
5268 }
5269 }
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00005270
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005271 if (static_cast<uint32_t>(indices.length()) < limit) {
5272 indices.Add(subject_length);
5273 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005274
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00005275 // The list indices now contains the end of each part to create.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005276
5277 // Create JSArray of substrings separated by separator.
5278 int part_count = indices.length();
5279
5280 Handle<JSArray> result = Factory::NewJSArray(part_count);
5281 result->set_length(Smi::FromInt(part_count));
5282
5283 ASSERT(result->HasFastElements());
5284
5285 if (part_count == 1 && indices.at(0) == subject_length) {
5286 FixedArray::cast(result->elements())->set(0, *subject);
5287 return *result;
5288 }
5289
5290 Handle<FixedArray> elements(FixedArray::cast(result->elements()));
5291 int part_start = 0;
5292 for (int i = 0; i < part_count; i++) {
5293 HandleScope local_loop_handle;
5294 int part_end = indices.at(i);
5295 Handle<String> substring =
5296 Factory::NewSubString(subject, part_start, part_end);
5297 elements->set(i, *substring);
5298 part_start = part_end + pattern_length;
5299 }
5300
5301 return *result;
5302}
5303
5304
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005305// Copies ascii characters to the given fixed array looking up
5306// one-char strings in the cache. Gives up on the first char that is
5307// not in the cache and fills the remainder with smi zeros. Returns
5308// the length of the successfully copied prefix.
5309static int CopyCachedAsciiCharsToArray(const char* chars,
5310 FixedArray* elements,
5311 int length) {
5312 AssertNoAllocation nogc;
5313 FixedArray* ascii_cache = Heap::single_character_string_cache();
5314 Object* undefined = Heap::undefined_value();
5315 int i;
5316 for (i = 0; i < length; ++i) {
5317 Object* value = ascii_cache->get(chars[i]);
5318 if (value == undefined) break;
5319 ASSERT(!Heap::InNewSpace(value));
5320 elements->set(i, value, SKIP_WRITE_BARRIER);
5321 }
5322 if (i < length) {
5323 ASSERT(Smi::FromInt(0) == 0);
5324 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
5325 }
5326#ifdef DEBUG
5327 for (int j = 0; j < length; ++j) {
5328 Object* element = elements->get(j);
5329 ASSERT(element == Smi::FromInt(0) ||
5330 (element->IsString() && String::cast(element)->LooksValid()));
5331 }
5332#endif
5333 return i;
5334}
5335
5336
5337// Converts a String to JSArray.
5338// For example, "foo" => ["f", "o", "o"].
5339static Object* Runtime_StringToArray(Arguments args) {
5340 HandleScope scope;
5341 ASSERT(args.length() == 1);
5342 CONVERT_ARG_CHECKED(String, s, 0);
5343
5344 s->TryFlatten();
5345 const int length = s->length();
5346
5347 Handle<FixedArray> elements;
5348 if (s->IsFlat() && s->IsAsciiRepresentation()) {
5349 Object* obj = Heap::AllocateUninitializedFixedArray(length);
5350 if (obj->IsFailure()) return obj;
5351 elements = Handle<FixedArray>(FixedArray::cast(obj));
5352
5353 Vector<const char> chars = s->ToAsciiVector();
5354 // Note, this will initialize all elements (not only the prefix)
5355 // to prevent GC from seeing partially initialized array.
5356 int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
5357 *elements,
5358 length);
5359
5360 for (int i = num_copied_from_cache; i < length; ++i) {
5361 elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
5362 }
5363 } else {
5364 elements = Factory::NewFixedArray(length);
5365 for (int i = 0; i < length; ++i) {
5366 elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
5367 }
5368 }
5369
5370#ifdef DEBUG
5371 for (int i = 0; i < length; ++i) {
5372 ASSERT(String::cast(elements->get(i))->length() == 1);
5373 }
5374#endif
5375
5376 return *Factory::NewJSArrayWithElements(elements);
5377}
5378
5379
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00005380static Object* Runtime_NewStringWrapper(Arguments args) {
5381 NoHandleAllocation ha;
5382 ASSERT(args.length() == 1);
5383 CONVERT_CHECKED(String, value, args[0]);
5384 return value->ToObject();
5385}
5386
5387
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00005388bool Runtime::IsUpperCaseChar(uint16_t ch) {
5389 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
5390 int char_length = to_upper_mapping.get(ch, 0, chars);
5391 return char_length == 0;
5392}
5393
5394
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005395static Object* Runtime_NumberToString(Arguments args) {
5396 NoHandleAllocation ha;
5397 ASSERT(args.length() == 1);
5398
5399 Object* number = args[0];
5400 RUNTIME_ASSERT(number->IsNumber());
5401
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005402 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005403}
5404
5405
ager@chromium.org357bf652010-04-12 11:30:10 +00005406static Object* Runtime_NumberToStringSkipCache(Arguments args) {
5407 NoHandleAllocation ha;
5408 ASSERT(args.length() == 1);
5409
5410 Object* number = args[0];
5411 RUNTIME_ASSERT(number->IsNumber());
5412
5413 return Heap::NumberToString(number, false);
5414}
5415
5416
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005417static Object* Runtime_NumberToInteger(Arguments args) {
5418 NoHandleAllocation ha;
5419 ASSERT(args.length() == 1);
5420
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005421 CONVERT_DOUBLE_CHECKED(number, args[0]);
5422
5423 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5424 if (number > 0 && number <= Smi::kMaxValue) {
5425 return Smi::FromInt(static_cast<int>(number));
5426 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005427 return Heap::NumberFromDouble(DoubleToInteger(number));
5428}
5429
5430
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005431static Object* Runtime_NumberToIntegerMapMinusZero(Arguments args) {
5432 NoHandleAllocation ha;
5433 ASSERT(args.length() == 1);
5434
5435 CONVERT_DOUBLE_CHECKED(number, args[0]);
5436
5437 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5438 if (number > 0 && number <= Smi::kMaxValue) {
5439 return Smi::FromInt(static_cast<int>(number));
5440 }
5441
5442 double double_value = DoubleToInteger(number);
5443 // Map both -0 and +0 to +0.
5444 if (double_value == 0) double_value = 0;
5445
5446 return Heap::NumberFromDouble(double_value);
5447}
5448
5449
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005450static Object* Runtime_NumberToJSUint32(Arguments args) {
5451 NoHandleAllocation ha;
5452 ASSERT(args.length() == 1);
5453
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005454 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005455 return Heap::NumberFromUint32(number);
5456}
5457
5458
5459static Object* Runtime_NumberToJSInt32(Arguments args) {
5460 NoHandleAllocation ha;
5461 ASSERT(args.length() == 1);
5462
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005463 CONVERT_DOUBLE_CHECKED(number, args[0]);
5464
5465 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5466 if (number > 0 && number <= Smi::kMaxValue) {
5467 return Smi::FromInt(static_cast<int>(number));
5468 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005469 return Heap::NumberFromInt32(DoubleToInt32(number));
5470}
5471
5472
ager@chromium.org870a0b62008-11-04 11:43:05 +00005473// Converts a Number to a Smi, if possible. Returns NaN if the number is not
5474// a small integer.
5475static Object* Runtime_NumberToSmi(Arguments args) {
5476 NoHandleAllocation ha;
5477 ASSERT(args.length() == 1);
5478
5479 Object* obj = args[0];
5480 if (obj->IsSmi()) {
5481 return obj;
5482 }
5483 if (obj->IsHeapNumber()) {
5484 double value = HeapNumber::cast(obj)->value();
5485 int int_value = FastD2I(value);
5486 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
5487 return Smi::FromInt(int_value);
5488 }
5489 }
5490 return Heap::nan_value();
5491}
5492
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005493
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005494static Object* Runtime_NumberAdd(Arguments args) {
5495 NoHandleAllocation ha;
5496 ASSERT(args.length() == 2);
5497
5498 CONVERT_DOUBLE_CHECKED(x, args[0]);
5499 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005500 return Heap::NumberFromDouble(x + y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005501}
5502
5503
5504static Object* Runtime_NumberSub(Arguments args) {
5505 NoHandleAllocation ha;
5506 ASSERT(args.length() == 2);
5507
5508 CONVERT_DOUBLE_CHECKED(x, args[0]);
5509 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005510 return Heap::NumberFromDouble(x - y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005511}
5512
5513
5514static Object* Runtime_NumberMul(Arguments args) {
5515 NoHandleAllocation ha;
5516 ASSERT(args.length() == 2);
5517
5518 CONVERT_DOUBLE_CHECKED(x, args[0]);
5519 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005520 return Heap::NumberFromDouble(x * y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005521}
5522
5523
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005524static Object* Runtime_NumberUnaryMinus(Arguments args) {
5525 NoHandleAllocation ha;
5526 ASSERT(args.length() == 1);
5527
5528 CONVERT_DOUBLE_CHECKED(x, args[0]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005529 return Heap::NumberFromDouble(-x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005530}
5531
5532
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00005533static Object* Runtime_NumberAlloc(Arguments args) {
5534 NoHandleAllocation ha;
5535 ASSERT(args.length() == 0);
5536
5537 return Heap::NumberFromDouble(9876543210.0);
5538}
5539
5540
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005541static Object* Runtime_NumberDiv(Arguments args) {
5542 NoHandleAllocation ha;
5543 ASSERT(args.length() == 2);
5544
5545 CONVERT_DOUBLE_CHECKED(x, args[0]);
5546 CONVERT_DOUBLE_CHECKED(y, args[1]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005547 return Heap::NumberFromDouble(x / y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005548}
5549
5550
5551static Object* Runtime_NumberMod(Arguments args) {
5552 NoHandleAllocation ha;
5553 ASSERT(args.length() == 2);
5554
5555 CONVERT_DOUBLE_CHECKED(x, args[0]);
5556 CONVERT_DOUBLE_CHECKED(y, args[1]);
5557
ager@chromium.org3811b432009-10-28 14:53:37 +00005558 x = modulo(x, y);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005559 // NumberFromDouble may return a Smi instead of a Number object
5560 return Heap::NumberFromDouble(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005561}
5562
5563
5564static Object* Runtime_StringAdd(Arguments args) {
5565 NoHandleAllocation ha;
5566 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005567 CONVERT_CHECKED(String, str1, args[0]);
5568 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005569 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00005570 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005571}
5572
5573
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005574template <typename sinkchar>
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005575static inline void StringBuilderConcatHelper(String* special,
5576 sinkchar* sink,
5577 FixedArray* fixed_array,
5578 int array_length) {
5579 int position = 0;
5580 for (int i = 0; i < array_length; i++) {
5581 Object* element = fixed_array->get(i);
5582 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005583 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005584 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005585 int pos;
5586 int len;
5587 if (encoded_slice > 0) {
5588 // Position and length encoded in one smi.
5589 pos = StringBuilderSubstringPosition::decode(encoded_slice);
5590 len = StringBuilderSubstringLength::decode(encoded_slice);
5591 } else {
5592 // Position and length encoded in two smis.
5593 Object* obj = fixed_array->get(++i);
5594 ASSERT(obj->IsSmi());
5595 pos = Smi::cast(obj)->value();
5596 len = -encoded_slice;
5597 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00005598 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00005599 sink + position,
5600 pos,
5601 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005602 position += len;
5603 } else {
5604 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005605 int element_length = string->length();
5606 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005607 position += element_length;
5608 }
5609 }
5610}
5611
5612
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005613static Object* Runtime_StringBuilderConcat(Arguments args) {
5614 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005615 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005616 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005617 if (!args[1]->IsSmi()) {
5618 Top::context()->mark_out_of_memory();
5619 return Failure::OutOfMemoryException();
5620 }
5621 int array_length = Smi::cast(args[1])->value();
5622 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005623
5624 // This assumption is used by the slice encoding in one or two smis.
5625 ASSERT(Smi::kMaxValue >= String::kMaxLength);
5626
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005627 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005628 if (!array->HasFastElements()) {
5629 return Top::Throw(Heap::illegal_argument_symbol());
5630 }
5631 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005632 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005633 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005634 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005635
5636 if (array_length == 0) {
5637 return Heap::empty_string();
5638 } else if (array_length == 1) {
5639 Object* first = fixed_array->get(0);
5640 if (first->IsString()) return first;
5641 }
5642
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005643 bool ascii = special->HasOnlyAsciiChars();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005644 int position = 0;
5645 for (int i = 0; i < array_length; i++) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005646 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005647 Object* elt = fixed_array->get(i);
5648 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005649 // Smi encoding of position and length.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005650 int smi_value = Smi::cast(elt)->value();
5651 int pos;
5652 int len;
5653 if (smi_value > 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005654 // Position and length encoded in one smi.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005655 pos = StringBuilderSubstringPosition::decode(smi_value);
5656 len = StringBuilderSubstringLength::decode(smi_value);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005657 } else {
5658 // Position and length encoded in two smis.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005659 len = -smi_value;
5660 // Get the position and check that it is a positive smi.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005661 i++;
5662 if (i >= array_length) {
5663 return Top::Throw(Heap::illegal_argument_symbol());
5664 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005665 Object* next_smi = fixed_array->get(i);
5666 if (!next_smi->IsSmi()) {
5667 return Top::Throw(Heap::illegal_argument_symbol());
5668 }
5669 pos = Smi::cast(next_smi)->value();
5670 if (pos < 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005671 return Top::Throw(Heap::illegal_argument_symbol());
5672 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005673 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005674 ASSERT(pos >= 0);
5675 ASSERT(len >= 0);
5676 if (pos > special_length || len > special_length - pos) {
5677 return Top::Throw(Heap::illegal_argument_symbol());
5678 }
5679 increment = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005680 } else if (elt->IsString()) {
5681 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005682 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005683 increment = element_length;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005684 if (ascii && !element->HasOnlyAsciiChars()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005685 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005686 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005687 } else {
5688 return Top::Throw(Heap::illegal_argument_symbol());
5689 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005690 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005691 Top::context()->mark_out_of_memory();
5692 return Failure::OutOfMemoryException();
5693 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005694 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005695 }
5696
5697 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005698 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005699
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005700 if (ascii) {
5701 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005702 if (object->IsFailure()) return object;
5703 SeqAsciiString* answer = SeqAsciiString::cast(object);
5704 StringBuilderConcatHelper(special,
5705 answer->GetChars(),
5706 fixed_array,
5707 array_length);
5708 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005709 } else {
5710 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005711 if (object->IsFailure()) return object;
5712 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
5713 StringBuilderConcatHelper(special,
5714 answer->GetChars(),
5715 fixed_array,
5716 array_length);
5717 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005718 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005719}
5720
5721
5722static Object* Runtime_NumberOr(Arguments args) {
5723 NoHandleAllocation ha;
5724 ASSERT(args.length() == 2);
5725
5726 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5727 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5728 return Heap::NumberFromInt32(x | y);
5729}
5730
5731
5732static Object* Runtime_NumberAnd(Arguments args) {
5733 NoHandleAllocation ha;
5734 ASSERT(args.length() == 2);
5735
5736 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5737 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5738 return Heap::NumberFromInt32(x & y);
5739}
5740
5741
5742static Object* Runtime_NumberXor(Arguments args) {
5743 NoHandleAllocation ha;
5744 ASSERT(args.length() == 2);
5745
5746 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5747 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5748 return Heap::NumberFromInt32(x ^ y);
5749}
5750
5751
5752static Object* Runtime_NumberNot(Arguments args) {
5753 NoHandleAllocation ha;
5754 ASSERT(args.length() == 1);
5755
5756 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5757 return Heap::NumberFromInt32(~x);
5758}
5759
5760
5761static Object* Runtime_NumberShl(Arguments args) {
5762 NoHandleAllocation ha;
5763 ASSERT(args.length() == 2);
5764
5765 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5766 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5767 return Heap::NumberFromInt32(x << (y & 0x1f));
5768}
5769
5770
5771static Object* Runtime_NumberShr(Arguments args) {
5772 NoHandleAllocation ha;
5773 ASSERT(args.length() == 2);
5774
5775 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
5776 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5777 return Heap::NumberFromUint32(x >> (y & 0x1f));
5778}
5779
5780
5781static Object* Runtime_NumberSar(Arguments args) {
5782 NoHandleAllocation ha;
5783 ASSERT(args.length() == 2);
5784
5785 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5786 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5787 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
5788}
5789
5790
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005791static Object* Runtime_NumberEquals(Arguments args) {
5792 NoHandleAllocation ha;
5793 ASSERT(args.length() == 2);
5794
5795 CONVERT_DOUBLE_CHECKED(x, args[0]);
5796 CONVERT_DOUBLE_CHECKED(y, args[1]);
5797 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
5798 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
5799 if (x == y) return Smi::FromInt(EQUAL);
5800 Object* result;
5801 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
5802 result = Smi::FromInt(EQUAL);
5803 } else {
5804 result = Smi::FromInt(NOT_EQUAL);
5805 }
5806 return result;
5807}
5808
5809
5810static Object* Runtime_StringEquals(Arguments args) {
5811 NoHandleAllocation ha;
5812 ASSERT(args.length() == 2);
5813
5814 CONVERT_CHECKED(String, x, args[0]);
5815 CONVERT_CHECKED(String, y, args[1]);
5816
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005817 bool not_equal = !x->Equals(y);
5818 // This is slightly convoluted because the value that signifies
5819 // equality is 0 and inequality is 1 so we have to negate the result
5820 // from String::Equals.
5821 ASSERT(not_equal == 0 || not_equal == 1);
5822 STATIC_CHECK(EQUAL == 0);
5823 STATIC_CHECK(NOT_EQUAL == 1);
5824 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005825}
5826
5827
5828static Object* Runtime_NumberCompare(Arguments args) {
5829 NoHandleAllocation ha;
5830 ASSERT(args.length() == 3);
5831
5832 CONVERT_DOUBLE_CHECKED(x, args[0]);
5833 CONVERT_DOUBLE_CHECKED(y, args[1]);
5834 if (isnan(x) || isnan(y)) return args[2];
5835 if (x == y) return Smi::FromInt(EQUAL);
5836 if (isless(x, y)) return Smi::FromInt(LESS);
5837 return Smi::FromInt(GREATER);
5838}
5839
5840
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005841// Compare two Smis as if they were converted to strings and then
5842// compared lexicographically.
5843static Object* Runtime_SmiLexicographicCompare(Arguments args) {
5844 NoHandleAllocation ha;
5845 ASSERT(args.length() == 2);
5846
5847 // Arrays for the individual characters of the two Smis. Smis are
5848 // 31 bit integers and 10 decimal digits are therefore enough.
5849 static int x_elms[10];
5850 static int y_elms[10];
5851
5852 // Extract the integer values from the Smis.
5853 CONVERT_CHECKED(Smi, x, args[0]);
5854 CONVERT_CHECKED(Smi, y, args[1]);
5855 int x_value = x->value();
5856 int y_value = y->value();
5857
5858 // If the integers are equal so are the string representations.
5859 if (x_value == y_value) return Smi::FromInt(EQUAL);
5860
5861 // If one of the integers are zero the normal integer order is the
5862 // same as the lexicographic order of the string representations.
5863 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
5864
ager@chromium.org32912102009-01-16 10:38:43 +00005865 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005866 // smallest because the char code of '-' is less than the char code
5867 // of any digit. Otherwise, we make both values positive.
5868 if (x_value < 0 || y_value < 0) {
5869 if (y_value >= 0) return Smi::FromInt(LESS);
5870 if (x_value >= 0) return Smi::FromInt(GREATER);
5871 x_value = -x_value;
5872 y_value = -y_value;
5873 }
5874
5875 // Convert the integers to arrays of their decimal digits.
5876 int x_index = 0;
5877 int y_index = 0;
5878 while (x_value > 0) {
5879 x_elms[x_index++] = x_value % 10;
5880 x_value /= 10;
5881 }
5882 while (y_value > 0) {
5883 y_elms[y_index++] = y_value % 10;
5884 y_value /= 10;
5885 }
5886
5887 // Loop through the arrays of decimal digits finding the first place
5888 // where they differ.
5889 while (--x_index >= 0 && --y_index >= 0) {
5890 int diff = x_elms[x_index] - y_elms[y_index];
5891 if (diff != 0) return Smi::FromInt(diff);
5892 }
5893
5894 // If one array is a suffix of the other array, the longest array is
5895 // the representation of the largest of the Smis in the
5896 // lexicographic ordering.
5897 return Smi::FromInt(x_index - y_index);
5898}
5899
5900
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005901static Object* StringInputBufferCompare(String* x, String* y) {
5902 static StringInputBuffer bufx;
5903 static StringInputBuffer bufy;
5904 bufx.Reset(x);
5905 bufy.Reset(y);
5906 while (bufx.has_more() && bufy.has_more()) {
5907 int d = bufx.GetNext() - bufy.GetNext();
5908 if (d < 0) return Smi::FromInt(LESS);
5909 else if (d > 0) return Smi::FromInt(GREATER);
5910 }
5911
5912 // x is (non-trivial) prefix of y:
5913 if (bufy.has_more()) return Smi::FromInt(LESS);
5914 // y is prefix of x:
5915 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
5916}
5917
5918
5919static Object* FlatStringCompare(String* x, String* y) {
5920 ASSERT(x->IsFlat());
5921 ASSERT(y->IsFlat());
5922 Object* equal_prefix_result = Smi::FromInt(EQUAL);
5923 int prefix_length = x->length();
5924 if (y->length() < prefix_length) {
5925 prefix_length = y->length();
5926 equal_prefix_result = Smi::FromInt(GREATER);
5927 } else if (y->length() > prefix_length) {
5928 equal_prefix_result = Smi::FromInt(LESS);
5929 }
5930 int r;
5931 if (x->IsAsciiRepresentation()) {
5932 Vector<const char> x_chars = x->ToAsciiVector();
5933 if (y->IsAsciiRepresentation()) {
5934 Vector<const char> y_chars = y->ToAsciiVector();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005935 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005936 } else {
5937 Vector<const uc16> y_chars = y->ToUC16Vector();
5938 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5939 }
5940 } else {
5941 Vector<const uc16> x_chars = x->ToUC16Vector();
5942 if (y->IsAsciiRepresentation()) {
5943 Vector<const char> y_chars = y->ToAsciiVector();
5944 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5945 } else {
5946 Vector<const uc16> y_chars = y->ToUC16Vector();
5947 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5948 }
5949 }
5950 Object* result;
5951 if (r == 0) {
5952 result = equal_prefix_result;
5953 } else {
5954 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
5955 }
5956 ASSERT(result == StringInputBufferCompare(x, y));
5957 return result;
5958}
5959
5960
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005961static Object* Runtime_StringCompare(Arguments args) {
5962 NoHandleAllocation ha;
5963 ASSERT(args.length() == 2);
5964
5965 CONVERT_CHECKED(String, x, args[0]);
5966 CONVERT_CHECKED(String, y, args[1]);
5967
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005968 Counters::string_compare_runtime.Increment();
5969
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005970 // A few fast case tests before we flatten.
5971 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005972 if (y->length() == 0) {
5973 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005974 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005975 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005976 return Smi::FromInt(LESS);
5977 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005978
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005979 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005980 if (d < 0) return Smi::FromInt(LESS);
5981 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005982
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005983 Object* obj = Heap::PrepareForCompare(x);
5984 if (obj->IsFailure()) return obj;
5985 obj = Heap::PrepareForCompare(y);
5986 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005987
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005988 return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
5989 : StringInputBufferCompare(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005990}
5991
5992
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005993static Object* Runtime_Math_acos(Arguments args) {
5994 NoHandleAllocation ha;
5995 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005996 Counters::math_acos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005997
5998 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005999 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006000}
6001
6002
6003static Object* Runtime_Math_asin(Arguments args) {
6004 NoHandleAllocation ha;
6005 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006006 Counters::math_asin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006007
6008 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006009 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006010}
6011
6012
6013static Object* Runtime_Math_atan(Arguments args) {
6014 NoHandleAllocation ha;
6015 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006016 Counters::math_atan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006017
6018 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006019 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006020}
6021
6022
6023static Object* Runtime_Math_atan2(Arguments args) {
6024 NoHandleAllocation ha;
6025 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006026 Counters::math_atan2.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006027
6028 CONVERT_DOUBLE_CHECKED(x, args[0]);
6029 CONVERT_DOUBLE_CHECKED(y, args[1]);
6030 double result;
6031 if (isinf(x) && isinf(y)) {
6032 // Make sure that the result in case of two infinite arguments
6033 // is a multiple of Pi / 4. The sign of the result is determined
6034 // by the first argument (x) and the sign of the second argument
6035 // determines the multiplier: one or three.
6036 static double kPiDividedBy4 = 0.78539816339744830962;
6037 int multiplier = (x < 0) ? -1 : 1;
6038 if (y < 0) multiplier *= 3;
6039 result = multiplier * kPiDividedBy4;
6040 } else {
6041 result = atan2(x, y);
6042 }
6043 return Heap::AllocateHeapNumber(result);
6044}
6045
6046
6047static Object* Runtime_Math_ceil(Arguments args) {
6048 NoHandleAllocation ha;
6049 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006050 Counters::math_ceil.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006051
6052 CONVERT_DOUBLE_CHECKED(x, args[0]);
6053 return Heap::NumberFromDouble(ceiling(x));
6054}
6055
6056
6057static Object* Runtime_Math_cos(Arguments args) {
6058 NoHandleAllocation ha;
6059 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006060 Counters::math_cos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006061
6062 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006063 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006064}
6065
6066
6067static Object* Runtime_Math_exp(Arguments args) {
6068 NoHandleAllocation ha;
6069 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006070 Counters::math_exp.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006071
6072 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006073 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006074}
6075
6076
6077static Object* Runtime_Math_floor(Arguments args) {
6078 NoHandleAllocation ha;
6079 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006080 Counters::math_floor.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006081
6082 CONVERT_DOUBLE_CHECKED(x, args[0]);
6083 return Heap::NumberFromDouble(floor(x));
6084}
6085
6086
6087static Object* Runtime_Math_log(Arguments args) {
6088 NoHandleAllocation ha;
6089 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006090 Counters::math_log.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006091
6092 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006093 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006094}
6095
6096
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006097// Helper function to compute x^y, where y is known to be an
6098// integer. Uses binary decomposition to limit the number of
6099// multiplications; see the discussion in "Hacker's Delight" by Henry
6100// S. Warren, Jr., figure 11-6, page 213.
6101static double powi(double x, int y) {
6102 ASSERT(y != kMinInt);
6103 unsigned n = (y < 0) ? -y : y;
6104 double m = x;
6105 double p = 1;
6106 while (true) {
6107 if ((n & 1) != 0) p *= m;
6108 n >>= 1;
6109 if (n == 0) {
6110 if (y < 0) {
6111 // Unfortunately, we have to be careful when p has reached
6112 // infinity in the computation, because sometimes the higher
6113 // internal precision in the pow() implementation would have
6114 // given us a finite p. This happens very rarely.
6115 double result = 1.0 / p;
6116 return (result == 0 && isinf(p))
6117 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
6118 : result;
6119 } else {
6120 return p;
6121 }
6122 }
6123 m *= m;
6124 }
6125}
6126
6127
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006128static Object* Runtime_Math_pow(Arguments args) {
6129 NoHandleAllocation ha;
6130 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006131 Counters::math_pow.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006132
6133 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006134
6135 // If the second argument is a smi, it is much faster to call the
6136 // custom powi() function than the generic pow().
6137 if (args[1]->IsSmi()) {
6138 int y = Smi::cast(args[1])->value();
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00006139 return Heap::NumberFromDouble(powi(x, y));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006140 }
6141
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006142 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00006143
6144 if (!isinf(x)) {
6145 if (y == 0.5) {
6146 // It's not uncommon to use Math.pow(x, 0.5) to compute the
6147 // square root of a number. To speed up such computations, we
6148 // explictly check for this case and use the sqrt() function
6149 // which is faster than pow().
6150 return Heap::AllocateHeapNumber(sqrt(x));
6151 } else if (y == -0.5) {
6152 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
6153 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
6154 }
6155 }
6156
6157 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006158 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006159 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6160 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006161 } else {
6162 return Heap::AllocateHeapNumber(pow(x, y));
6163 }
6164}
6165
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006166// Fast version of Math.pow if we know that y is not an integer and
6167// y is not -0.5 or 0.5. Used as slowcase from codegen.
6168static Object* Runtime_Math_pow_cfunction(Arguments args) {
6169 NoHandleAllocation ha;
6170 ASSERT(args.length() == 2);
6171 CONVERT_DOUBLE_CHECKED(x, args[0]);
6172 CONVERT_DOUBLE_CHECKED(y, args[1]);
6173 if (y == 0) {
6174 return Smi::FromInt(1);
6175 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6176 return Heap::nan_value();
6177 } else {
6178 return Heap::AllocateHeapNumber(pow(x, y));
6179 }
6180}
6181
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006182
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006183static Object* Runtime_RoundNumber(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006184 NoHandleAllocation ha;
6185 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006186 Counters::math_round.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006187
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006188 if (!args[0]->IsHeapNumber()) {
6189 // Must be smi. Return the argument unchanged for all the other types
6190 // to make fuzz-natives test happy.
6191 return args[0];
6192 }
6193
6194 HeapNumber* number = reinterpret_cast<HeapNumber*>(args[0]);
6195
6196 double value = number->value();
6197 int exponent = number->get_exponent();
6198 int sign = number->get_sign();
6199
6200 // We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
6201 // should be rounded to 2^30, which is not smi.
6202 if (!sign && exponent <= kSmiValueSize - 3) {
6203 return Smi::FromInt(static_cast<int>(value + 0.5));
6204 }
6205
6206 // If the magnitude is big enough, there's no place for fraction part. If we
6207 // try to add 0.5 to this number, 1.0 will be added instead.
6208 if (exponent >= 52) {
6209 return number;
6210 }
6211
6212 if (sign && value >= -0.5) return Heap::minus_zero_value();
6213
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006214 // Do not call NumberFromDouble() to avoid extra checks.
6215 return Heap::AllocateHeapNumber(floor(value + 0.5));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006216}
6217
6218
6219static Object* Runtime_Math_sin(Arguments args) {
6220 NoHandleAllocation ha;
6221 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006222 Counters::math_sin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006223
6224 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006225 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006226}
6227
6228
6229static Object* Runtime_Math_sqrt(Arguments args) {
6230 NoHandleAllocation ha;
6231 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006232 Counters::math_sqrt.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006233
6234 CONVERT_DOUBLE_CHECKED(x, args[0]);
6235 return Heap::AllocateHeapNumber(sqrt(x));
6236}
6237
6238
6239static Object* Runtime_Math_tan(Arguments args) {
6240 NoHandleAllocation ha;
6241 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006242 Counters::math_tan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006243
6244 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006245 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006246}
6247
6248
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006249static int MakeDay(int year, int month, int day) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006250 static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
6251 181, 212, 243, 273, 304, 334};
6252 static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
6253 182, 213, 244, 274, 305, 335};
6254
6255 year += month / 12;
6256 month %= 12;
6257 if (month < 0) {
6258 year--;
6259 month += 12;
6260 }
6261
6262 ASSERT(month >= 0);
6263 ASSERT(month < 12);
6264
6265 // year_delta is an arbitrary number such that:
6266 // a) year_delta = -1 (mod 400)
6267 // b) year + year_delta > 0 for years in the range defined by
6268 // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
6269 // Jan 1 1970. This is required so that we don't run into integer
6270 // division of negative numbers.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006271 // c) there shouldn't be an overflow for 32-bit integers in the following
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006272 // operations.
6273 static const int year_delta = 399999;
6274 static const int base_day = 365 * (1970 + year_delta) +
6275 (1970 + year_delta) / 4 -
6276 (1970 + year_delta) / 100 +
6277 (1970 + year_delta) / 400;
6278
6279 int year1 = year + year_delta;
6280 int day_from_year = 365 * year1 +
6281 year1 / 4 -
6282 year1 / 100 +
6283 year1 / 400 -
6284 base_day;
6285
6286 if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006287 return day_from_year + day_from_month[month] + day - 1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006288 }
6289
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006290 return day_from_year + day_from_month_leap[month] + day - 1;
6291}
6292
6293
6294static Object* Runtime_DateMakeDay(Arguments args) {
6295 NoHandleAllocation ha;
6296 ASSERT(args.length() == 3);
6297
6298 CONVERT_SMI_CHECKED(year, args[0]);
6299 CONVERT_SMI_CHECKED(month, args[1]);
6300 CONVERT_SMI_CHECKED(date, args[2]);
6301
6302 return Smi::FromInt(MakeDay(year, month, date));
6303}
6304
6305
6306static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
6307static const int kDaysIn4Years = 4 * 365 + 1;
6308static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
6309static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
6310static const int kDays1970to2000 = 30 * 365 + 7;
6311static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
6312 kDays1970to2000;
6313static const int kYearsOffset = 400000;
6314
6315static const char kDayInYear[] = {
6316 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6317 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6318 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6319 22, 23, 24, 25, 26, 27, 28,
6320 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6321 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6322 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6323 22, 23, 24, 25, 26, 27, 28, 29, 30,
6324 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6325 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6326 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6327 22, 23, 24, 25, 26, 27, 28, 29, 30,
6328 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6329 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6330 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6331 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6332 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6333 22, 23, 24, 25, 26, 27, 28, 29, 30,
6334 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6335 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6336 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6337 22, 23, 24, 25, 26, 27, 28, 29, 30,
6338 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6339 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6340
6341 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6342 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6343 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6344 22, 23, 24, 25, 26, 27, 28,
6345 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6346 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6347 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6348 22, 23, 24, 25, 26, 27, 28, 29, 30,
6349 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6350 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6351 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6352 22, 23, 24, 25, 26, 27, 28, 29, 30,
6353 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6354 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6355 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6356 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6357 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6358 22, 23, 24, 25, 26, 27, 28, 29, 30,
6359 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6360 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6361 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6362 22, 23, 24, 25, 26, 27, 28, 29, 30,
6363 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6364 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6365
6366 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6367 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6368 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6369 22, 23, 24, 25, 26, 27, 28, 29,
6370 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6371 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6372 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6373 22, 23, 24, 25, 26, 27, 28, 29, 30,
6374 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6375 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6376 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6377 22, 23, 24, 25, 26, 27, 28, 29, 30,
6378 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6379 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6380 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6381 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6382 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6383 22, 23, 24, 25, 26, 27, 28, 29, 30,
6384 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6385 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6386 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6387 22, 23, 24, 25, 26, 27, 28, 29, 30,
6388 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6389 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6390
6391 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6392 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6393 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6394 22, 23, 24, 25, 26, 27, 28,
6395 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6396 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6397 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6398 22, 23, 24, 25, 26, 27, 28, 29, 30,
6399 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6400 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6401 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6402 22, 23, 24, 25, 26, 27, 28, 29, 30,
6403 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6404 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6405 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6406 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6407 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6408 22, 23, 24, 25, 26, 27, 28, 29, 30,
6409 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6410 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6411 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6412 22, 23, 24, 25, 26, 27, 28, 29, 30,
6413 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6414 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
6415
6416static const char kMonthInYear[] = {
6417 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,
6418 0, 0, 0, 0, 0, 0,
6419 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,
6420 1, 1, 1,
6421 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,
6422 2, 2, 2, 2, 2, 2,
6423 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,
6424 3, 3, 3, 3, 3,
6425 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,
6426 4, 4, 4, 4, 4, 4,
6427 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,
6428 5, 5, 5, 5, 5,
6429 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,
6430 6, 6, 6, 6, 6, 6,
6431 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,
6432 7, 7, 7, 7, 7, 7,
6433 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,
6434 8, 8, 8, 8, 8,
6435 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,
6436 9, 9, 9, 9, 9, 9,
6437 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6438 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6439 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6440 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6441
6442 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,
6443 0, 0, 0, 0, 0, 0,
6444 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,
6445 1, 1, 1,
6446 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,
6447 2, 2, 2, 2, 2, 2,
6448 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,
6449 3, 3, 3, 3, 3,
6450 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,
6451 4, 4, 4, 4, 4, 4,
6452 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,
6453 5, 5, 5, 5, 5,
6454 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,
6455 6, 6, 6, 6, 6, 6,
6456 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,
6457 7, 7, 7, 7, 7, 7,
6458 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,
6459 8, 8, 8, 8, 8,
6460 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,
6461 9, 9, 9, 9, 9, 9,
6462 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6463 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6464 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6465 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6466
6467 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,
6468 0, 0, 0, 0, 0, 0,
6469 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,
6470 1, 1, 1, 1,
6471 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,
6472 2, 2, 2, 2, 2, 2,
6473 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,
6474 3, 3, 3, 3, 3,
6475 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,
6476 4, 4, 4, 4, 4, 4,
6477 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,
6478 5, 5, 5, 5, 5,
6479 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,
6480 6, 6, 6, 6, 6, 6,
6481 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,
6482 7, 7, 7, 7, 7, 7,
6483 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,
6484 8, 8, 8, 8, 8,
6485 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,
6486 9, 9, 9, 9, 9, 9,
6487 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6488 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6489 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6490 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6491
6492 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,
6493 0, 0, 0, 0, 0, 0,
6494 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,
6495 1, 1, 1,
6496 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,
6497 2, 2, 2, 2, 2, 2,
6498 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,
6499 3, 3, 3, 3, 3,
6500 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,
6501 4, 4, 4, 4, 4, 4,
6502 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,
6503 5, 5, 5, 5, 5,
6504 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,
6505 6, 6, 6, 6, 6, 6,
6506 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,
6507 7, 7, 7, 7, 7, 7,
6508 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,
6509 8, 8, 8, 8, 8,
6510 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,
6511 9, 9, 9, 9, 9, 9,
6512 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6513 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6514 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6515 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
6516
6517
6518// This function works for dates from 1970 to 2099.
6519static inline void DateYMDFromTimeAfter1970(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006520 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006521#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006522 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006523#endif
6524
6525 year = 1970 + (4 * date + 2) / kDaysIn4Years;
6526 date %= kDaysIn4Years;
6527
6528 month = kMonthInYear[date];
6529 day = kDayInYear[date];
6530
6531 ASSERT(MakeDay(year, month, day) == save_date);
6532}
6533
6534
6535static inline void DateYMDFromTimeSlow(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006536 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006537#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006538 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006539#endif
6540
6541 date += kDaysOffset;
6542 year = 400 * (date / kDaysIn400Years) - kYearsOffset;
6543 date %= kDaysIn400Years;
6544
6545 ASSERT(MakeDay(year, 0, 1) + date == save_date);
6546
6547 date--;
6548 int yd1 = date / kDaysIn100Years;
6549 date %= kDaysIn100Years;
6550 year += 100 * yd1;
6551
6552 date++;
6553 int yd2 = date / kDaysIn4Years;
6554 date %= kDaysIn4Years;
6555 year += 4 * yd2;
6556
6557 date--;
6558 int yd3 = date / 365;
6559 date %= 365;
6560 year += yd3;
6561
6562 bool is_leap = (!yd1 || yd2) && !yd3;
6563
6564 ASSERT(date >= -1);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006565 ASSERT(is_leap || (date >= 0));
6566 ASSERT((date < 365) || (is_leap && (date < 366)));
6567 ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0))));
6568 ASSERT(is_leap || ((MakeDay(year, 0, 1) + date) == save_date));
6569 ASSERT(!is_leap || ((MakeDay(year, 0, 1) + date + 1) == save_date));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006570
6571 if (is_leap) {
6572 day = kDayInYear[2*365 + 1 + date];
6573 month = kMonthInYear[2*365 + 1 + date];
6574 } else {
6575 day = kDayInYear[date];
6576 month = kMonthInYear[date];
6577 }
6578
6579 ASSERT(MakeDay(year, month, day) == save_date);
6580}
6581
6582
6583static inline void DateYMDFromTime(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006584 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006585 if (date >= 0 && date < 32 * kDaysIn4Years) {
6586 DateYMDFromTimeAfter1970(date, year, month, day);
6587 } else {
6588 DateYMDFromTimeSlow(date, year, month, day);
6589 }
6590}
6591
6592
6593static Object* Runtime_DateYMDFromTime(Arguments args) {
6594 NoHandleAllocation ha;
6595 ASSERT(args.length() == 2);
6596
6597 CONVERT_DOUBLE_CHECKED(t, args[0]);
6598 CONVERT_CHECKED(JSArray, res_array, args[1]);
6599
6600 int year, month, day;
6601 DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
6602
ricow@chromium.org0b9f8502010-08-18 07:45:01 +00006603 RUNTIME_ASSERT(res_array->elements()->map() == Heap::fixed_array_map());
6604 FixedArray* elms = FixedArray::cast(res_array->elements());
6605 RUNTIME_ASSERT(elms->length() == 3);
6606
6607 elms->set(0, Smi::FromInt(year));
6608 elms->set(1, Smi::FromInt(month));
6609 elms->set(2, Smi::FromInt(day));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006610
6611 return Heap::undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006612}
6613
6614
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006615static Object* Runtime_NewArgumentsFast(Arguments args) {
6616 NoHandleAllocation ha;
6617 ASSERT(args.length() == 3);
6618
6619 JSFunction* callee = JSFunction::cast(args[0]);
6620 Object** parameters = reinterpret_cast<Object**>(args[1]);
6621 const int length = Smi::cast(args[2])->value();
6622
6623 Object* result = Heap::AllocateArgumentsObject(callee, length);
6624 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006625 // Allocate the elements if needed.
6626 if (length > 0) {
6627 // Allocate the fixed array.
6628 Object* obj = Heap::AllocateRawFixedArray(length);
6629 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006630
6631 AssertNoAllocation no_gc;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006632 FixedArray* array = reinterpret_cast<FixedArray*>(obj);
6633 array->set_map(Heap::fixed_array_map());
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006634 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006635
6636 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006637 for (int i = 0; i < length; i++) {
6638 array->set(i, *--parameters, mode);
6639 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006640 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006641 }
6642 return result;
6643}
6644
6645
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006646static Object* Runtime_NewClosure(Arguments args) {
6647 HandleScope scope;
6648 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00006649 CONVERT_ARG_CHECKED(Context, context, 0);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006650 CONVERT_ARG_CHECKED(SharedFunctionInfo, shared, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006651
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00006652 PretenureFlag pretenure = (context->global_context() == *context)
6653 ? TENURED // Allocate global closures in old space.
6654 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006655 Handle<JSFunction> result =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006656 Factory::NewFunctionFromSharedFunctionInfo(shared, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006657 return *result;
6658}
6659
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006660static Object* Runtime_NewObjectFromBound(Arguments args) {
6661 HandleScope scope;
6662 ASSERT(args.length() == 2);
6663 CONVERT_ARG_CHECKED(JSFunction, function, 0);
6664 CONVERT_ARG_CHECKED(JSArray, params, 1);
6665
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006666 RUNTIME_ASSERT(params->HasFastElements());
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006667 FixedArray* fixed = FixedArray::cast(params->elements());
6668
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006669 int fixed_length = Smi::cast(params->length())->value();
6670 SmartPointer<Object**> param_data(NewArray<Object**>(fixed_length));
6671 for (int i = 0; i < fixed_length; i++) {
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006672 Handle<Object> val = Handle<Object>(fixed->get(i));
6673 param_data[i] = val.location();
6674 }
6675
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006676 bool exception = false;
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006677 Handle<Object> result = Execution::New(
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006678 function, fixed_length, *param_data, &exception);
6679 if (exception) {
6680 return Failure::Exception();
6681 }
6682 ASSERT(!result.is_null());
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006683 return *result;
6684}
6685
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006686
ager@chromium.org5c838252010-02-19 08:53:10 +00006687static Code* ComputeConstructStub(Handle<JSFunction> function) {
6688 Handle<Object> prototype = Factory::null_value();
6689 if (function->has_instance_prototype()) {
6690 prototype = Handle<Object>(function->instance_prototype());
6691 }
6692 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006693 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00006694 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006695 if (code->IsFailure()) {
6696 return Builtins::builtin(Builtins::JSConstructStubGeneric);
6697 }
6698 return Code::cast(code);
6699 }
6700
ager@chromium.org5c838252010-02-19 08:53:10 +00006701 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006702}
6703
6704
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006705static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006706 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006707 ASSERT(args.length() == 1);
6708
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006709 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006710
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006711 // If the constructor isn't a proper function we throw a type error.
6712 if (!constructor->IsJSFunction()) {
6713 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6714 Handle<Object> type_error =
6715 Factory::NewTypeError("not_constructor", arguments);
6716 return Top::Throw(*type_error);
6717 }
6718
6719 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006720
6721 // If function should not have prototype, construction is not allowed. In this
6722 // case generated code bailouts here, since function has no initial_map.
6723 if (!function->should_have_prototype()) {
6724 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6725 Handle<Object> type_error =
6726 Factory::NewTypeError("not_constructor", arguments);
6727 return Top::Throw(*type_error);
6728 }
6729
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006730#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006731 // Handle stepping into constructors if step into is active.
6732 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006733 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006734 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006735#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006736
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006737 if (function->has_initial_map()) {
6738 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006739 // The 'Function' function ignores the receiver object when
6740 // called using 'new' and creates a new JSFunction object that
6741 // is returned. The receiver object is only used for error
6742 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006743 // JSFunction. Factory::NewJSObject() should not be used to
6744 // allocate JSFunctions since it does not properly initialize
6745 // the shared part of the function. Since the receiver is
6746 // ignored anyway, we use the global object as the receiver
6747 // instead of a new JSFunction object. This way, errors are
6748 // reported the same way whether or not 'Function' is called
6749 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006750 return Top::context()->global();
6751 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006752 }
6753
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006754 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006755 Handle<SharedFunctionInfo> shared(function->shared());
6756 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006757
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006758 bool first_allocation = !function->has_initial_map();
6759 Handle<JSObject> result = Factory::NewJSObject(function);
6760 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006761 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00006762 ComputeConstructStub(Handle<JSFunction>(function)));
6763 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006764 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006765
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006766 Counters::constructed_objects.Increment();
6767 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006768
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006769 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006770}
6771
6772
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006773static Object* Runtime_LazyCompile(Arguments args) {
6774 HandleScope scope;
6775 ASSERT(args.length() == 1);
6776
6777 Handle<JSFunction> function = args.at<JSFunction>(0);
6778#ifdef DEBUG
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00006779 if (FLAG_trace_lazy && !function->shared()->is_compiled()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006780 PrintF("[lazy: ");
6781 function->shared()->name()->Print();
6782 PrintF("]\n");
6783 }
6784#endif
6785
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006786 // Compile the target function. Here we compile using CompileLazyInLoop in
6787 // order to get the optimized version. This helps code like delta-blue
6788 // that calls performance-critical routines through constructors. A
6789 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
6790 // direct call. Since the in-loop tracking takes place through CallICs
6791 // this means that things called through constructors are never known to
6792 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006793 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006794 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006795 return Failure::Exception();
6796 }
6797
6798 return function->code();
6799}
6800
6801
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006802static Object* Runtime_GetFunctionDelegate(Arguments args) {
6803 HandleScope scope;
6804 ASSERT(args.length() == 1);
6805 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6806 return *Execution::GetFunctionDelegate(args.at<Object>(0));
6807}
6808
6809
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00006810static Object* Runtime_GetConstructorDelegate(Arguments args) {
6811 HandleScope scope;
6812 ASSERT(args.length() == 1);
6813 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6814 return *Execution::GetConstructorDelegate(args.at<Object>(0));
6815}
6816
6817
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006818static Object* Runtime_NewContext(Arguments args) {
6819 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00006820 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006821
kasper.lund7276f142008-07-30 08:49:36 +00006822 CONVERT_CHECKED(JSFunction, function, args[0]);
ager@chromium.orgb5737492010-07-15 09:29:43 +00006823 int length = function->shared()->scope_info()->NumberOfContextSlots();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006824 Object* result = Heap::AllocateFunctionContext(length, function);
6825 if (result->IsFailure()) return result;
6826
6827 Top::set_context(Context::cast(result));
6828
kasper.lund7276f142008-07-30 08:49:36 +00006829 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006830}
6831
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006832static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006833 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006834 Object* js_object = object;
6835 if (!js_object->IsJSObject()) {
6836 js_object = js_object->ToObject();
6837 if (js_object->IsFailure()) {
6838 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006839 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006840 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006841 Handle<Object> result =
6842 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
6843 return Top::Throw(*result);
6844 }
6845 }
6846
6847 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006848 Heap::AllocateWithContext(Top::context(),
6849 JSObject::cast(js_object),
6850 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006851 if (result->IsFailure()) return result;
6852
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006853 Context* context = Context::cast(result);
6854 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006855
kasper.lund7276f142008-07-30 08:49:36 +00006856 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006857}
6858
6859
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006860static Object* Runtime_PushContext(Arguments args) {
6861 NoHandleAllocation ha;
6862 ASSERT(args.length() == 1);
6863 return PushContextHelper(args[0], false);
6864}
6865
6866
6867static Object* Runtime_PushCatchContext(Arguments args) {
6868 NoHandleAllocation ha;
6869 ASSERT(args.length() == 1);
6870 return PushContextHelper(args[0], true);
6871}
6872
6873
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006874static Object* Runtime_LookupContext(Arguments args) {
6875 HandleScope scope;
6876 ASSERT(args.length() == 2);
6877
6878 CONVERT_ARG_CHECKED(Context, context, 0);
6879 CONVERT_ARG_CHECKED(String, name, 1);
6880
6881 int index;
6882 PropertyAttributes attributes;
6883 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006884 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006885 context->Lookup(name, flags, &index, &attributes);
6886
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006887 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006888 ASSERT(holder->IsJSObject());
6889 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006890 }
6891
6892 // No intermediate context found. Use global object by default.
6893 return Top::context()->global();
6894}
6895
6896
ager@chromium.orga1645e22009-09-09 19:27:10 +00006897// A mechanism to return a pair of Object pointers in registers (if possible).
6898// How this is achieved is calling convention-dependent.
6899// All currently supported x86 compiles uses calling conventions that are cdecl
6900// variants where a 64-bit value is returned in two 32-bit registers
6901// (edx:eax on ia32, r1:r0 on ARM).
6902// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
6903// In Win64 calling convention, a struct of two pointers is returned in memory,
6904// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006905#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006906struct ObjectPair {
6907 Object* x;
6908 Object* y;
6909};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006910
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006911static inline ObjectPair MakePair(Object* x, Object* y) {
6912 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006913 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
6914 // In Win64 they are assigned to a hidden first argument.
6915 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006916}
6917#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006918typedef uint64_t ObjectPair;
6919static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006920 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006921 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006922}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006923#endif
6924
6925
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006926static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006927 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
6928 USE(attributes);
6929 return x->IsTheHole() ? Heap::undefined_value() : x;
6930}
6931
6932
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006933static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
6934 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006935 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006936 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006937 JSFunction* context_extension_function =
6938 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006939 // If the holder isn't a context extension object, we just return it
6940 // as the receiver. This allows arguments objects to be used as
6941 // receivers, but only if they are put in the context scope chain
6942 // explicitly via a with-statement.
6943 Object* constructor = holder->map()->constructor();
6944 if (constructor != context_extension_function) return holder;
6945 // Fall back to using the global object as the receiver if the
6946 // property turns out to be a local variable allocated in a context
6947 // extension object - introduced via eval.
6948 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006949}
6950
6951
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006952static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006953 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00006954 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006955
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006956 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00006957 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006958 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006959 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006960 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006961
6962 int index;
6963 PropertyAttributes attributes;
6964 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006965 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006966 context->Lookup(name, flags, &index, &attributes);
6967
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006968 // If the index is non-negative, the slot has been found in a local
6969 // variable or a parameter. Read it from the context object or the
6970 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006971 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006972 // If the "property" we were looking for is a local variable or an
6973 // argument in a context, the receiver is the global object; see
6974 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
6975 JSObject* receiver = Top::context()->global()->global_receiver();
6976 Object* value = (holder->IsContext())
6977 ? Context::cast(*holder)->get(index)
6978 : JSObject::cast(*holder)->GetElement(index);
6979 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006980 }
6981
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006982 // If the holder is found, we read the property from it.
6983 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006984 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006985 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006986 JSObject* receiver;
6987 if (object->IsGlobalObject()) {
6988 receiver = GlobalObject::cast(object)->global_receiver();
6989 } else if (context->is_exception_holder(*holder)) {
6990 receiver = Top::context()->global()->global_receiver();
6991 } else {
6992 receiver = ComputeReceiverForNonGlobal(object);
6993 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006994 // No need to unhole the value here. This is taken care of by the
6995 // GetProperty function.
6996 Object* value = object->GetProperty(*name);
6997 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006998 }
6999
7000 if (throw_error) {
7001 // The property doesn't exist - throw exception.
7002 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007003 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007004 return MakePair(Top::Throw(*reference_error), NULL);
7005 } else {
7006 // The property doesn't exist - return undefined
7007 return MakePair(Heap::undefined_value(), Heap::undefined_value());
7008 }
7009}
7010
7011
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007012static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007013 return LoadContextSlotHelper(args, true);
7014}
7015
7016
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007017static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007018 return LoadContextSlotHelper(args, false);
7019}
7020
7021
7022static Object* Runtime_StoreContextSlot(Arguments args) {
7023 HandleScope scope;
7024 ASSERT(args.length() == 3);
7025
7026 Handle<Object> value(args[0]);
7027 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007028 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007029
7030 int index;
7031 PropertyAttributes attributes;
7032 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007033 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007034 context->Lookup(name, flags, &index, &attributes);
7035
7036 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007037 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007038 // Ignore if read_only variable.
7039 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007040 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007041 }
7042 } else {
7043 ASSERT((attributes & READ_ONLY) == 0);
7044 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007045 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007046 USE(result);
7047 ASSERT(!result->IsFailure());
7048 }
7049 return *value;
7050 }
7051
7052 // Slow case: The property is not in a FixedArray context.
7053 // It is either in an JSObject extension context or it was not found.
7054 Handle<JSObject> context_ext;
7055
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007056 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007057 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007058 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007059 } else {
7060 // The property was not found. It needs to be stored in the global context.
7061 ASSERT(attributes == ABSENT);
7062 attributes = NONE;
7063 context_ext = Handle<JSObject>(Top::context()->global());
7064 }
7065
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007066 // Set the property, but ignore if read_only variable on the context
7067 // extension object itself.
7068 if ((attributes & READ_ONLY) == 0 ||
7069 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007070 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
7071 if (set.is_null()) {
7072 // Failure::Exception is converted to a null handle in the
7073 // handle-based methods such as SetProperty. We therefore need
7074 // to convert null handles back to exceptions.
7075 ASSERT(Top::has_pending_exception());
7076 return Failure::Exception();
7077 }
7078 }
7079 return *value;
7080}
7081
7082
7083static Object* Runtime_Throw(Arguments args) {
7084 HandleScope scope;
7085 ASSERT(args.length() == 1);
7086
7087 return Top::Throw(args[0]);
7088}
7089
7090
7091static Object* Runtime_ReThrow(Arguments args) {
7092 HandleScope scope;
7093 ASSERT(args.length() == 1);
7094
7095 return Top::ReThrow(args[0]);
7096}
7097
7098
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007099static Object* Runtime_PromoteScheduledException(Arguments args) {
7100 ASSERT_EQ(0, args.length());
7101 return Top::PromoteScheduledException();
7102}
7103
7104
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007105static Object* Runtime_ThrowReferenceError(Arguments args) {
7106 HandleScope scope;
7107 ASSERT(args.length() == 1);
7108
7109 Handle<Object> name(args[0]);
7110 Handle<Object> reference_error =
7111 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
7112 return Top::Throw(*reference_error);
7113}
7114
7115
7116static Object* Runtime_StackOverflow(Arguments args) {
7117 NoHandleAllocation na;
7118 return Top::StackOverflow();
7119}
7120
7121
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007122static Object* Runtime_StackGuard(Arguments args) {
7123 ASSERT(args.length() == 1);
7124
7125 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007126 if (StackGuard::IsStackOverflow()) {
7127 return Runtime_StackOverflow(args);
7128 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007129
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007130 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007131}
7132
7133
7134// NOTE: These PrintXXX functions are defined for all builds (not just
7135// DEBUG builds) because we may want to be able to trace function
7136// calls in all modes.
7137static void PrintString(String* str) {
7138 // not uncommon to have empty strings
7139 if (str->length() > 0) {
7140 SmartPointer<char> s =
7141 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
7142 PrintF("%s", *s);
7143 }
7144}
7145
7146
7147static void PrintObject(Object* obj) {
7148 if (obj->IsSmi()) {
7149 PrintF("%d", Smi::cast(obj)->value());
7150 } else if (obj->IsString() || obj->IsSymbol()) {
7151 PrintString(String::cast(obj));
7152 } else if (obj->IsNumber()) {
7153 PrintF("%g", obj->Number());
7154 } else if (obj->IsFailure()) {
7155 PrintF("<failure>");
7156 } else if (obj->IsUndefined()) {
7157 PrintF("<undefined>");
7158 } else if (obj->IsNull()) {
7159 PrintF("<null>");
7160 } else if (obj->IsTrue()) {
7161 PrintF("<true>");
7162 } else if (obj->IsFalse()) {
7163 PrintF("<false>");
7164 } else {
7165 PrintF("%p", obj);
7166 }
7167}
7168
7169
7170static int StackSize() {
7171 int n = 0;
7172 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
7173 return n;
7174}
7175
7176
7177static void PrintTransition(Object* result) {
7178 // indentation
7179 { const int nmax = 80;
7180 int n = StackSize();
7181 if (n <= nmax)
7182 PrintF("%4d:%*s", n, n, "");
7183 else
7184 PrintF("%4d:%*s", n, nmax, "...");
7185 }
7186
7187 if (result == NULL) {
7188 // constructor calls
7189 JavaScriptFrameIterator it;
7190 JavaScriptFrame* frame = it.frame();
7191 if (frame->IsConstructor()) PrintF("new ");
7192 // function name
7193 Object* fun = frame->function();
7194 if (fun->IsJSFunction()) {
7195 PrintObject(JSFunction::cast(fun)->shared()->name());
7196 } else {
7197 PrintObject(fun);
7198 }
7199 // function arguments
7200 // (we are intentionally only printing the actually
7201 // supplied parameters, not all parameters required)
7202 PrintF("(this=");
7203 PrintObject(frame->receiver());
7204 const int length = frame->GetProvidedParametersCount();
7205 for (int i = 0; i < length; i++) {
7206 PrintF(", ");
7207 PrintObject(frame->GetParameter(i));
7208 }
7209 PrintF(") {\n");
7210
7211 } else {
7212 // function result
7213 PrintF("} -> ");
7214 PrintObject(result);
7215 PrintF("\n");
7216 }
7217}
7218
7219
7220static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007221 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007222 NoHandleAllocation ha;
7223 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007224 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007225}
7226
7227
7228static Object* Runtime_TraceExit(Arguments args) {
7229 NoHandleAllocation ha;
7230 PrintTransition(args[0]);
7231 return args[0]; // return TOS
7232}
7233
7234
7235static Object* Runtime_DebugPrint(Arguments args) {
7236 NoHandleAllocation ha;
7237 ASSERT(args.length() == 1);
7238
7239#ifdef DEBUG
7240 if (args[0]->IsString()) {
7241 // If we have a string, assume it's a code "marker"
7242 // and print some interesting cpu debugging info.
7243 JavaScriptFrameIterator it;
7244 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007245 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
7246 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007247 } else {
7248 PrintF("DebugPrint: ");
7249 }
7250 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007251 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007252 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007253 HeapObject::cast(args[0])->map()->Print();
7254 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007255#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007256 // ShortPrint is available in release mode. Print is not.
7257 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007258#endif
7259 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00007260 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007261
7262 return args[0]; // return TOS
7263}
7264
7265
7266static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007267 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007268 NoHandleAllocation ha;
7269 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007270 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007271}
7272
7273
mads.s.ager31e71382008-08-13 09:32:07 +00007274static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007275 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007276 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007277
7278 // According to ECMA-262, section 15.9.1, page 117, the precision of
7279 // the number in a Date object representing a particular instant in
7280 // time is milliseconds. Therefore, we floor the result of getting
7281 // the OS time.
7282 double millis = floor(OS::TimeCurrentMillis());
7283 return Heap::NumberFromDouble(millis);
7284}
7285
7286
7287static Object* Runtime_DateParseString(Arguments args) {
7288 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007289 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007290
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007291 CONVERT_ARG_CHECKED(String, str, 0);
7292 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007293
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007294 CONVERT_ARG_CHECKED(JSArray, output, 1);
7295 RUNTIME_ASSERT(output->HasFastElements());
7296
7297 AssertNoAllocation no_allocation;
7298
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007299 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007300 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
7301 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007302 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007303 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007304 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007305 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007306 result = DateParser::Parse(str->ToUC16Vector(), output_array);
7307 }
7308
7309 if (result) {
7310 return *output;
7311 } else {
7312 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007313 }
7314}
7315
7316
7317static Object* Runtime_DateLocalTimezone(Arguments args) {
7318 NoHandleAllocation ha;
7319 ASSERT(args.length() == 1);
7320
7321 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007322 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007323 return Heap::AllocateStringFromUtf8(CStrVector(zone));
7324}
7325
7326
7327static Object* Runtime_DateLocalTimeOffset(Arguments args) {
7328 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007329 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007330
7331 return Heap::NumberFromDouble(OS::LocalTimeOffset());
7332}
7333
7334
7335static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
7336 NoHandleAllocation ha;
7337 ASSERT(args.length() == 1);
7338
7339 CONVERT_DOUBLE_CHECKED(x, args[0]);
7340 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
7341}
7342
7343
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007344static Object* Runtime_GlobalReceiver(Arguments args) {
7345 ASSERT(args.length() == 1);
7346 Object* global = args[0];
7347 if (!global->IsJSGlobalObject()) return Heap::null_value();
7348 return JSGlobalObject::cast(global)->global_receiver();
7349}
7350
7351
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007352static Object* Runtime_CompileString(Arguments args) {
7353 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007354 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007355 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007356 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007357
ager@chromium.org381abbb2009-02-25 13:23:22 +00007358 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007359 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007360 Compiler::ValidationState validate = (is_json->IsTrue())
7361 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007362 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
7363 context,
7364 true,
7365 validate);
7366 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007367 Handle<JSFunction> fun =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007368 Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007369 return *fun;
7370}
7371
7372
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007373static ObjectPair CompileGlobalEval(Handle<String> source,
7374 Handle<Object> receiver) {
7375 // Deal with a normal eval call with a string argument. Compile it
7376 // and return the compiled function bound in the local context.
7377 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
7378 source,
7379 Handle<Context>(Top::context()),
7380 Top::context()->IsGlobalContext(),
7381 Compiler::DONT_VALIDATE_JSON);
7382 if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
7383 Handle<JSFunction> compiled = Factory::NewFunctionFromSharedFunctionInfo(
7384 shared,
7385 Handle<Context>(Top::context()),
7386 NOT_TENURED);
7387 return MakePair(*compiled, *receiver);
7388}
7389
7390
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007391static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
7392 ASSERT(args.length() == 3);
7393 if (!args[0]->IsJSFunction()) {
7394 return MakePair(Top::ThrowIllegalOperation(), NULL);
7395 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007396
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007397 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007398 Handle<JSFunction> callee = args.at<JSFunction>(0);
7399 Handle<Object> receiver; // Will be overwritten.
7400
7401 // Compute the calling context.
7402 Handle<Context> context = Handle<Context>(Top::context());
7403#ifdef DEBUG
7404 // Make sure Top::context() agrees with the old code that traversed
7405 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007406 StackFrameLocator locator;
7407 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007408 ASSERT(Context::cast(frame->context()) == *context);
7409#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007410
7411 // Find where the 'eval' symbol is bound. It is unaliased only if
7412 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007413 int index = -1;
7414 PropertyAttributes attributes = ABSENT;
7415 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007416 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
7417 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007418 // Stop search when eval is found or when the global context is
7419 // reached.
7420 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007421 if (context->is_function_context()) {
7422 context = Handle<Context>(Context::cast(context->closure()->context()));
7423 } else {
7424 context = Handle<Context>(context->previous());
7425 }
7426 }
7427
iposva@chromium.org245aa852009-02-10 00:49:54 +00007428 // If eval could not be resolved, it has been deleted and we need to
7429 // throw a reference error.
7430 if (attributes == ABSENT) {
7431 Handle<Object> name = Factory::eval_symbol();
7432 Handle<Object> reference_error =
7433 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007434 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007435 }
7436
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007437 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007438 // 'eval' is not bound in the global context. Just call the function
7439 // with the given arguments. This is not necessarily the global eval.
7440 if (receiver->IsContext()) {
7441 context = Handle<Context>::cast(receiver);
7442 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007443 } else if (receiver->IsJSContextExtensionObject()) {
7444 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007445 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007446 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007447 }
7448
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007449 // 'eval' is bound in the global context, but it may have been overwritten.
7450 // Compare it to the builtin 'GlobalEval' function to make sure.
7451 if (*callee != Top::global_context()->global_eval_fun() ||
7452 !args[1]->IsString()) {
7453 return MakePair(*callee, Top::context()->global()->global_receiver());
7454 }
7455
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007456 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
7457}
7458
7459
7460static ObjectPair Runtime_ResolvePossiblyDirectEvalNoLookup(Arguments args) {
7461 ASSERT(args.length() == 3);
7462 if (!args[0]->IsJSFunction()) {
7463 return MakePair(Top::ThrowIllegalOperation(), NULL);
7464 }
7465
7466 HandleScope scope;
7467 Handle<JSFunction> callee = args.at<JSFunction>(0);
7468
7469 // 'eval' is bound in the global context, but it may have been overwritten.
7470 // Compare it to the builtin 'GlobalEval' function to make sure.
7471 if (*callee != Top::global_context()->global_eval_fun() ||
7472 !args[1]->IsString()) {
7473 return MakePair(*callee, Top::context()->global()->global_receiver());
7474 }
7475
7476 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007477}
7478
7479
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007480static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
7481 // This utility adjusts the property attributes for newly created Function
7482 // object ("new Function(...)") by changing the map.
7483 // All it does is changing the prototype property to enumerable
7484 // as specified in ECMA262, 15.3.5.2.
7485 HandleScope scope;
7486 ASSERT(args.length() == 1);
7487 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7488 ASSERT(func->map()->instance_type() ==
7489 Top::function_instance_map()->instance_type());
7490 ASSERT(func->map()->instance_size() ==
7491 Top::function_instance_map()->instance_size());
7492 func->set_map(*Top::function_instance_map());
7493 return *func;
7494}
7495
7496
lrn@chromium.orgc4e51ac2010-08-09 09:47:21 +00007497static Object* Runtime_AllocateInNewSpace(Arguments args) {
7498 // Allocate a block of memory in NewSpace (filled with a filler).
7499 // Use as fallback for allocation in generated code when NewSpace
7500 // is full.
7501 ASSERT(args.length() == 1);
7502 CONVERT_ARG_CHECKED(Smi, size_smi, 0);
7503 int size = size_smi->value();
7504 RUNTIME_ASSERT(IsAligned(size, kPointerSize));
7505 RUNTIME_ASSERT(size > 0);
7506 static const int kMinFreeNewSpaceAfterGC =
7507 Heap::InitialSemiSpaceSize() * 3/4;
7508 RUNTIME_ASSERT(size <= kMinFreeNewSpaceAfterGC);
7509 Object* allocation = Heap::new_space()->AllocateRaw(size);
7510 if (!allocation->IsFailure()) {
7511 Heap::CreateFillerObjectAt(HeapObject::cast(allocation)->address(), size);
7512 }
7513 return allocation;
7514}
7515
7516
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007517// Push an array unto an array of arrays if it is not already in the
7518// array. Returns true if the element was pushed on the stack and
7519// false otherwise.
7520static Object* Runtime_PushIfAbsent(Arguments args) {
7521 ASSERT(args.length() == 2);
7522 CONVERT_CHECKED(JSArray, array, args[0]);
7523 CONVERT_CHECKED(JSArray, element, args[1]);
7524 RUNTIME_ASSERT(array->HasFastElements());
7525 int length = Smi::cast(array->length())->value();
7526 FixedArray* elements = FixedArray::cast(array->elements());
7527 for (int i = 0; i < length; i++) {
7528 if (elements->get(i) == element) return Heap::false_value();
7529 }
7530 Object* obj = array->SetFastElement(length, element);
7531 if (obj->IsFailure()) return obj;
7532 return Heap::true_value();
7533}
7534
7535
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007536/**
7537 * A simple visitor visits every element of Array's.
7538 * The backend storage can be a fixed array for fast elements case,
7539 * or a dictionary for sparse array. Since Dictionary is a subtype
7540 * of FixedArray, the class can be used by both fast and slow cases.
7541 * The second parameter of the constructor, fast_elements, specifies
7542 * whether the storage is a FixedArray or Dictionary.
7543 *
7544 * An index limit is used to deal with the situation that a result array
7545 * length overflows 32-bit non-negative integer.
7546 */
7547class ArrayConcatVisitor {
7548 public:
7549 ArrayConcatVisitor(Handle<FixedArray> storage,
7550 uint32_t index_limit,
7551 bool fast_elements) :
7552 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007553 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007554
7555 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007556 if (i >= index_limit_ - index_offset_) return;
7557 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007558
7559 if (fast_elements_) {
7560 ASSERT(index < static_cast<uint32_t>(storage_->length()));
7561 storage_->set(index, *elm);
7562
7563 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007564 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
7565 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007566 Factory::DictionaryAtNumberPut(dict, index, elm);
7567 if (!result.is_identical_to(dict))
7568 storage_ = result;
7569 }
7570 }
7571
7572 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007573 if (index_limit_ - index_offset_ < delta) {
7574 index_offset_ = index_limit_;
7575 } else {
7576 index_offset_ += delta;
7577 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007578 }
7579
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007580 Handle<FixedArray> storage() { return storage_; }
7581
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007582 private:
7583 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007584 // Limit on the accepted indices. Elements with indices larger than the
7585 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007586 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007587 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007588 uint32_t index_offset_;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007589 const bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007590};
7591
7592
ager@chromium.org3811b432009-10-28 14:53:37 +00007593template<class ExternalArrayClass, class ElementType>
7594static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
7595 bool elements_are_ints,
7596 bool elements_are_guaranteed_smis,
7597 uint32_t range,
7598 ArrayConcatVisitor* visitor) {
7599 Handle<ExternalArrayClass> array(
7600 ExternalArrayClass::cast(receiver->elements()));
7601 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
7602
7603 if (visitor != NULL) {
7604 if (elements_are_ints) {
7605 if (elements_are_guaranteed_smis) {
7606 for (uint32_t j = 0; j < len; j++) {
7607 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
7608 visitor->visit(j, e);
7609 }
7610 } else {
7611 for (uint32_t j = 0; j < len; j++) {
7612 int64_t val = static_cast<int64_t>(array->get(j));
7613 if (Smi::IsValid(static_cast<intptr_t>(val))) {
7614 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
7615 visitor->visit(j, e);
7616 } else {
7617 Handle<Object> e(
7618 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
7619 visitor->visit(j, e);
7620 }
7621 }
7622 }
7623 } else {
7624 for (uint32_t j = 0; j < len; j++) {
7625 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
7626 visitor->visit(j, e);
7627 }
7628 }
7629 }
7630
7631 return len;
7632}
7633
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007634/**
7635 * A helper function that visits elements of a JSObject. Only elements
7636 * whose index between 0 and range (exclusive) are visited.
7637 *
7638 * If the third parameter, visitor, is not NULL, the visitor is called
7639 * with parameters, 'visitor_index_offset + element index' and the element.
7640 *
7641 * It returns the number of visisted elements.
7642 */
7643static uint32_t IterateElements(Handle<JSObject> receiver,
7644 uint32_t range,
7645 ArrayConcatVisitor* visitor) {
7646 uint32_t num_of_elements = 0;
7647
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007648 switch (receiver->GetElementsKind()) {
7649 case JSObject::FAST_ELEMENTS: {
7650 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
7651 uint32_t len = elements->length();
7652 if (range < len) {
7653 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007654 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007655
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007656 for (uint32_t j = 0; j < len; j++) {
7657 Handle<Object> e(elements->get(j));
7658 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007659 num_of_elements++;
7660 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007661 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007662 }
7663 }
7664 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007665 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007666 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007667 case JSObject::PIXEL_ELEMENTS: {
7668 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
7669 uint32_t len = pixels->length();
7670 if (range < len) {
7671 len = range;
7672 }
7673
7674 for (uint32_t j = 0; j < len; j++) {
7675 num_of_elements++;
7676 if (visitor != NULL) {
7677 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
7678 visitor->visit(j, e);
7679 }
7680 }
7681 break;
7682 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007683 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
7684 num_of_elements =
7685 IterateExternalArrayElements<ExternalByteArray, int8_t>(
7686 receiver, true, true, range, visitor);
7687 break;
7688 }
7689 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7690 num_of_elements =
7691 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
7692 receiver, true, true, range, visitor);
7693 break;
7694 }
7695 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
7696 num_of_elements =
7697 IterateExternalArrayElements<ExternalShortArray, int16_t>(
7698 receiver, true, true, range, visitor);
7699 break;
7700 }
7701 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7702 num_of_elements =
7703 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
7704 receiver, true, true, range, visitor);
7705 break;
7706 }
7707 case JSObject::EXTERNAL_INT_ELEMENTS: {
7708 num_of_elements =
7709 IterateExternalArrayElements<ExternalIntArray, int32_t>(
7710 receiver, true, false, range, visitor);
7711 break;
7712 }
7713 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7714 num_of_elements =
7715 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
7716 receiver, true, false, range, visitor);
7717 break;
7718 }
7719 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
7720 num_of_elements =
7721 IterateExternalArrayElements<ExternalFloatArray, float>(
7722 receiver, false, false, range, visitor);
7723 break;
7724 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007725 case JSObject::DICTIONARY_ELEMENTS: {
7726 Handle<NumberDictionary> dict(receiver->element_dictionary());
7727 uint32_t capacity = dict->Capacity();
7728 for (uint32_t j = 0; j < capacity; j++) {
7729 Handle<Object> k(dict->KeyAt(j));
7730 if (dict->IsKey(*k)) {
7731 ASSERT(k->IsNumber());
7732 uint32_t index = static_cast<uint32_t>(k->Number());
7733 if (index < range) {
7734 num_of_elements++;
7735 if (visitor) {
7736 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
7737 }
7738 }
7739 }
7740 }
7741 break;
7742 }
7743 default:
7744 UNREACHABLE();
7745 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007746 }
7747
7748 return num_of_elements;
7749}
7750
7751
7752/**
7753 * A helper function that visits elements of an Array object, and elements
7754 * on its prototypes.
7755 *
7756 * Elements on prototypes are visited first, and only elements whose indices
7757 * less than Array length are visited.
7758 *
7759 * If a ArrayConcatVisitor object is given, the visitor is called with
7760 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007761 *
7762 * The returned number of elements is an upper bound on the actual number
7763 * of elements added. If the same element occurs in more than one object
7764 * in the array's prototype chain, it will be counted more than once, but
7765 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007766 */
7767static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
7768 ArrayConcatVisitor* visitor) {
7769 uint32_t range = static_cast<uint32_t>(array->length()->Number());
7770 Handle<Object> obj = array;
7771
7772 static const int kEstimatedPrototypes = 3;
7773 List< Handle<JSObject> > objects(kEstimatedPrototypes);
7774
7775 // Visit prototype first. If an element on the prototype is shadowed by
7776 // the inheritor using the same index, the ArrayConcatVisitor visits
7777 // the prototype element before the shadowing element.
7778 // The visitor can simply overwrite the old value by new value using
7779 // the same index. This follows Array::concat semantics.
7780 while (!obj->IsNull()) {
7781 objects.Add(Handle<JSObject>::cast(obj));
7782 obj = Handle<Object>(obj->GetPrototype());
7783 }
7784
7785 uint32_t nof_elements = 0;
7786 for (int i = objects.length() - 1; i >= 0; i--) {
7787 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007788 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007789 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007790
7791 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
7792 nof_elements = JSObject::kMaxElementCount;
7793 } else {
7794 nof_elements += encountered_elements;
7795 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007796 }
7797
7798 return nof_elements;
7799}
7800
7801
7802/**
7803 * A helper function of Runtime_ArrayConcat.
7804 *
7805 * The first argument is an Array of arrays and objects. It is the
7806 * same as the arguments array of Array::concat JS function.
7807 *
7808 * If an argument is an Array object, the function visits array
7809 * elements. If an argument is not an Array object, the function
7810 * visits the object as if it is an one-element array.
7811 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007812 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007813 * non-negative number is used as new length. For example, if one
7814 * array length is 2^32 - 1, second array length is 1, the
7815 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007816 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
7817 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007818 */
7819static uint32_t IterateArguments(Handle<JSArray> arguments,
7820 ArrayConcatVisitor* visitor) {
7821 uint32_t visited_elements = 0;
7822 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7823
7824 for (uint32_t i = 0; i < num_of_args; i++) {
7825 Handle<Object> obj(arguments->GetElement(i));
7826 if (obj->IsJSArray()) {
7827 Handle<JSArray> array = Handle<JSArray>::cast(obj);
7828 uint32_t len = static_cast<uint32_t>(array->length()->Number());
7829 uint32_t nof_elements =
7830 IterateArrayAndPrototypeElements(array, visitor);
7831 // Total elements of array and its prototype chain can be more than
7832 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007833 // the array length number of elements. We use the length as an estimate
7834 // for the actual number of elements added.
7835 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
7836 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
7837 visited_elements = JSArray::kMaxElementCount;
7838 } else {
7839 visited_elements += added_elements;
7840 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007841 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007842 } else {
7843 if (visitor) {
7844 visitor->visit(0, obj);
7845 visitor->increase_index_offset(1);
7846 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007847 if (visited_elements < JSArray::kMaxElementCount) {
7848 visited_elements++;
7849 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007850 }
7851 }
7852 return visited_elements;
7853}
7854
7855
7856/**
7857 * Array::concat implementation.
7858 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007859 * TODO(lrn): Fix non-compliance for very large concatenations and update to
7860 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007861 */
7862static Object* Runtime_ArrayConcat(Arguments args) {
7863 ASSERT(args.length() == 1);
7864 HandleScope handle_scope;
7865
7866 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
7867 Handle<JSArray> arguments(arg_arrays);
7868
7869 // Pass 1: estimate the number of elements of the result
7870 // (it could be more than real numbers if prototype has elements).
7871 uint32_t result_length = 0;
7872 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7873
7874 { AssertNoAllocation nogc;
7875 for (uint32_t i = 0; i < num_of_args; i++) {
7876 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007877 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007878 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007879 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007880 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
7881 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007882 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007883 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007884 if (JSObject::kMaxElementCount - result_length < length_estimate) {
7885 result_length = JSObject::kMaxElementCount;
7886 break;
7887 }
7888 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007889 }
7890 }
7891
7892 // Allocate an empty array, will set length and content later.
7893 Handle<JSArray> result = Factory::NewJSArray(0);
7894
7895 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
7896 // If estimated number of elements is more than half of length, a
7897 // fixed array (fast case) is more time and space-efficient than a
7898 // dictionary.
7899 bool fast_case = (estimate_nof_elements * 2) >= result_length;
7900
7901 Handle<FixedArray> storage;
7902 if (fast_case) {
7903 // The backing storage array must have non-existing elements to
7904 // preserve holes across concat operations.
7905 storage = Factory::NewFixedArrayWithHoles(result_length);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007906 result->set_map(*Factory::GetFastElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007907 } else {
7908 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
7909 uint32_t at_least_space_for = estimate_nof_elements +
7910 (estimate_nof_elements >> 2);
7911 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007912 Factory::NewNumberDictionary(at_least_space_for));
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007913 result->set_map(*Factory::GetSlowElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007914 }
7915
7916 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
7917
7918 ArrayConcatVisitor visitor(storage, result_length, fast_case);
7919
7920 IterateArguments(arguments, &visitor);
7921
7922 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007923 // Please note the storage might have changed in the visitor.
7924 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007925
7926 return *result;
7927}
7928
7929
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007930// This will not allocate (flatten the string), but it may run
7931// very slowly for very deeply nested ConsStrings. For debugging use only.
7932static Object* Runtime_GlobalPrint(Arguments args) {
7933 NoHandleAllocation ha;
7934 ASSERT(args.length() == 1);
7935
7936 CONVERT_CHECKED(String, string, args[0]);
7937 StringInputBuffer buffer(string);
7938 while (buffer.has_more()) {
7939 uint16_t character = buffer.GetNext();
7940 PrintF("%c", character);
7941 }
7942 return string;
7943}
7944
ager@chromium.org5ec48922009-05-05 07:25:34 +00007945// Moves all own elements of an object, that are below a limit, to positions
7946// starting at zero. All undefined values are placed after non-undefined values,
7947// and are followed by non-existing element. Does not change the length
7948// property.
7949// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007950static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007951 ASSERT(args.length() == 2);
7952 CONVERT_CHECKED(JSObject, object, args[0]);
7953 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
7954 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007955}
7956
7957
7958// Move contents of argument 0 (an array) to argument 1 (an array)
7959static Object* Runtime_MoveArrayContents(Arguments args) {
7960 ASSERT(args.length() == 2);
7961 CONVERT_CHECKED(JSArray, from, args[0]);
7962 CONVERT_CHECKED(JSArray, to, args[1]);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007963 HeapObject* new_elements = from->elements();
7964 Object* new_map;
ricow@chromium.org0b9f8502010-08-18 07:45:01 +00007965 if (new_elements->map() == Heap::fixed_array_map() ||
7966 new_elements->map() == Heap::fixed_cow_array_map()) {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007967 new_map = to->map()->GetFastElementsMap();
7968 } else {
7969 new_map = to->map()->GetSlowElementsMap();
7970 }
7971 if (new_map->IsFailure()) return new_map;
7972 to->set_map(Map::cast(new_map));
7973 to->set_elements(new_elements);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007974 to->set_length(from->length());
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007975 Object* obj = from->ResetElements();
7976 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007977 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007978 return to;
7979}
7980
7981
7982// How many elements does this array have?
7983static Object* Runtime_EstimateNumberOfElements(Arguments args) {
7984 ASSERT(args.length() == 1);
7985 CONVERT_CHECKED(JSArray, array, args[0]);
7986 HeapObject* elements = array->elements();
7987 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007988 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007989 } else {
7990 return array->length();
7991 }
7992}
7993
7994
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007995static Object* Runtime_SwapElements(Arguments args) {
7996 HandleScope handle_scope;
7997
7998 ASSERT_EQ(3, args.length());
7999
ager@chromium.orgac091b72010-05-05 07:34:42 +00008000 CONVERT_ARG_CHECKED(JSObject, object, 0);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008001 Handle<Object> key1 = args.at<Object>(1);
8002 Handle<Object> key2 = args.at<Object>(2);
8003
8004 uint32_t index1, index2;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008005 if (!key1->ToArrayIndex(&index1)
8006 || !key2->ToArrayIndex(&index2)) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00008007 return Top::ThrowIllegalOperation();
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008008 }
8009
ager@chromium.orgac091b72010-05-05 07:34:42 +00008010 Handle<JSObject> jsobject = Handle<JSObject>::cast(object);
8011 Handle<Object> tmp1 = GetElement(jsobject, index1);
8012 Handle<Object> tmp2 = GetElement(jsobject, index2);
8013
8014 SetElement(jsobject, index1, tmp2);
8015 SetElement(jsobject, index2, tmp1);
8016
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008017 return Heap::undefined_value();
8018}
8019
8020
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008021// Returns an array that tells you where in the [0, length) interval an array
8022// might have elements. Can either return keys or intervals. Keys can have
8023// gaps in (undefined). Intervals can also span over some undefined keys.
8024static Object* Runtime_GetArrayKeys(Arguments args) {
8025 ASSERT(args.length() == 2);
8026 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00008027 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008028 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008029 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008030 // Create an array and get all the keys into it, then remove all the
8031 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008032 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008033 int keys_length = keys->length();
8034 for (int i = 0; i < keys_length; i++) {
8035 Object* key = keys->get(i);
8036 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008037 if (!key->ToArrayIndex(&index) || index >= length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008038 // Zap invalid keys.
8039 keys->set_undefined(i);
8040 }
8041 }
8042 return *Factory::NewJSArrayWithElements(keys);
8043 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008044 ASSERT(array->HasFastElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008045 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
8046 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008047 single_interval->set(0, Smi::FromInt(-1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008048 uint32_t actual_length =
8049 static_cast<uint32_t>(FixedArray::cast(array->elements())->length());
ager@chromium.org5ec48922009-05-05 07:25:34 +00008050 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008051 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00008052 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008053 single_interval->set(1, *length_object);
8054 return *Factory::NewJSArrayWithElements(single_interval);
8055 }
8056}
8057
8058
8059// DefineAccessor takes an optional final argument which is the
8060// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
8061// to the way accessors are implemented, it is set for both the getter
8062// and setter on the first call to DefineAccessor and ignored on
8063// subsequent calls.
8064static Object* Runtime_DefineAccessor(Arguments args) {
8065 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
8066 // Compute attributes.
8067 PropertyAttributes attributes = NONE;
8068 if (args.length() == 5) {
8069 CONVERT_CHECKED(Smi, attrs, args[4]);
8070 int value = attrs->value();
8071 // Only attribute bits should be set.
8072 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
8073 attributes = static_cast<PropertyAttributes>(value);
8074 }
8075
8076 CONVERT_CHECKED(JSObject, obj, args[0]);
8077 CONVERT_CHECKED(String, name, args[1]);
8078 CONVERT_CHECKED(Smi, flag, args[2]);
8079 CONVERT_CHECKED(JSFunction, fun, args[3]);
8080 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
8081}
8082
8083
8084static Object* Runtime_LookupAccessor(Arguments args) {
8085 ASSERT(args.length() == 3);
8086 CONVERT_CHECKED(JSObject, obj, args[0]);
8087 CONVERT_CHECKED(String, name, args[1]);
8088 CONVERT_CHECKED(Smi, flag, args[2]);
8089 return obj->LookupAccessor(name, flag->value() == 0);
8090}
8091
8092
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008093#ifdef ENABLE_DEBUGGER_SUPPORT
8094static Object* Runtime_DebugBreak(Arguments args) {
8095 ASSERT(args.length() == 0);
8096 return Execution::DebugBreakHelper();
8097}
8098
8099
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008100// Helper functions for wrapping and unwrapping stack frame ids.
8101static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008102 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008103 return Smi::FromInt(id >> 2);
8104}
8105
8106
8107static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
8108 return static_cast<StackFrame::Id>(wrapped->value() << 2);
8109}
8110
8111
8112// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00008113// args[0]: debug event listener function to set or null or undefined for
8114// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008115// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00008116static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008117 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00008118 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
8119 args[0]->IsUndefined() ||
8120 args[0]->IsNull());
8121 Handle<Object> callback = args.at<Object>(0);
8122 Handle<Object> data = args.at<Object>(1);
8123 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008124
8125 return Heap::undefined_value();
8126}
8127
8128
8129static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00008130 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008131 StackGuard::DebugBreak();
8132 return Heap::undefined_value();
8133}
8134
8135
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008136static Object* DebugLookupResultValue(Object* receiver, String* name,
8137 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00008138 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008139 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008140 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008141 case NORMAL:
8142 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008143 if (value->IsTheHole()) {
8144 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008145 }
8146 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008147 case FIELD:
8148 value =
8149 JSObject::cast(
8150 result->holder())->FastPropertyAt(result->GetFieldIndex());
8151 if (value->IsTheHole()) {
8152 return Heap::undefined_value();
8153 }
8154 return value;
8155 case CONSTANT_FUNCTION:
8156 return result->GetConstantFunction();
8157 case CALLBACKS: {
8158 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008159 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008160 value = receiver->GetPropertyWithCallback(
8161 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00008162 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008163 value = Top::pending_exception();
8164 Top::clear_pending_exception();
8165 if (caught_exception != NULL) {
8166 *caught_exception = true;
8167 }
8168 }
8169 return value;
8170 } else {
8171 return Heap::undefined_value();
8172 }
8173 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008174 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008175 case MAP_TRANSITION:
8176 case CONSTANT_TRANSITION:
8177 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008178 return Heap::undefined_value();
8179 default:
8180 UNREACHABLE();
8181 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008182 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008183 return Heap::undefined_value();
8184}
8185
8186
ager@chromium.org32912102009-01-16 10:38:43 +00008187// Get debugger related details for an object property.
8188// args[0]: object holding property
8189// args[1]: name of the property
8190//
8191// The array returned contains the following information:
8192// 0: Property value
8193// 1: Property details
8194// 2: Property value is exception
8195// 3: Getter function if defined
8196// 4: Setter function if defined
8197// Items 2-4 are only filled if the property has either a getter or a setter
8198// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00008199static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008200 HandleScope scope;
8201
8202 ASSERT(args.length() == 2);
8203
8204 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8205 CONVERT_ARG_CHECKED(String, name, 1);
8206
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008207 // Make sure to set the current context to the context before the debugger was
8208 // entered (if the debugger is entered). The reason for switching context here
8209 // is that for some property lookups (accessors and interceptors) callbacks
8210 // into the embedding application can occour, and the embedding application
8211 // could have the assumption that its own global context is the current
8212 // context and not some internal debugger context.
8213 SaveContext save;
8214 if (Debug::InDebugger()) {
8215 Top::set_context(*Debug::debugger_entry()->GetContext());
8216 }
8217
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008218 // Skip the global proxy as it has no properties and always delegates to the
8219 // real global object.
8220 if (obj->IsJSGlobalProxy()) {
8221 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
8222 }
8223
8224
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008225 // Check if the name is trivially convertible to an index and get the element
8226 // if so.
8227 uint32_t index;
8228 if (name->AsArrayIndex(&index)) {
8229 Handle<FixedArray> details = Factory::NewFixedArray(2);
8230 details->set(0, Runtime::GetElementOrCharAt(obj, index));
8231 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
8232 return *Factory::NewJSArrayWithElements(details);
8233 }
8234
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008235 // Find the number of objects making up this.
8236 int length = LocalPrototypeChainLength(*obj);
8237
8238 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008239 Handle<JSObject> jsproto = obj;
8240 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008241 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008242 jsproto->LocalLookup(*name, &result);
8243 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008244 // LookupResult is not GC safe as it holds raw object pointers.
8245 // GC can happen later in this code so put the required fields into
8246 // local variables using handles when required for later use.
8247 PropertyType result_type = result.type();
8248 Handle<Object> result_callback_obj;
8249 if (result_type == CALLBACKS) {
8250 result_callback_obj = Handle<Object>(result.GetCallbackObject());
8251 }
8252 Smi* property_details = result.GetPropertyDetails().AsSmi();
8253 // DebugLookupResultValue can cause GC so details from LookupResult needs
8254 // to be copied to handles before this.
8255 bool caught_exception = false;
8256 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
8257 &caught_exception);
8258 if (raw_value->IsFailure()) return raw_value;
8259 Handle<Object> value(raw_value);
8260
8261 // If the callback object is a fixed array then it contains JavaScript
8262 // getter and/or setter.
8263 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
8264 result_callback_obj->IsFixedArray();
8265 Handle<FixedArray> details =
8266 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
8267 details->set(0, *value);
8268 details->set(1, property_details);
8269 if (hasJavaScriptAccessors) {
8270 details->set(2,
8271 caught_exception ? Heap::true_value()
8272 : Heap::false_value());
8273 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
8274 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
8275 }
8276
8277 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008278 }
8279 if (i < length - 1) {
8280 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
8281 }
8282 }
8283
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008284 return Heap::undefined_value();
8285}
8286
8287
8288static Object* Runtime_DebugGetProperty(Arguments args) {
8289 HandleScope scope;
8290
8291 ASSERT(args.length() == 2);
8292
8293 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8294 CONVERT_ARG_CHECKED(String, name, 1);
8295
8296 LookupResult result;
8297 obj->Lookup(*name, &result);
8298 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008299 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008300 }
8301 return Heap::undefined_value();
8302}
8303
8304
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008305// Return the property type calculated from the property details.
8306// args[0]: smi with property details.
8307static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
8308 ASSERT(args.length() == 1);
8309 CONVERT_CHECKED(Smi, details, args[0]);
8310 PropertyType type = PropertyDetails(details).type();
8311 return Smi::FromInt(static_cast<int>(type));
8312}
8313
8314
8315// Return the property attribute calculated from the property details.
8316// args[0]: smi with property details.
8317static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
8318 ASSERT(args.length() == 1);
8319 CONVERT_CHECKED(Smi, details, args[0]);
8320 PropertyAttributes attributes = PropertyDetails(details).attributes();
8321 return Smi::FromInt(static_cast<int>(attributes));
8322}
8323
8324
8325// Return the property insertion index calculated from the property details.
8326// args[0]: smi with property details.
8327static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
8328 ASSERT(args.length() == 1);
8329 CONVERT_CHECKED(Smi, details, args[0]);
8330 int index = PropertyDetails(details).index();
8331 return Smi::FromInt(index);
8332}
8333
8334
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008335// Return property value from named interceptor.
8336// args[0]: object
8337// args[1]: property name
8338static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
8339 HandleScope scope;
8340 ASSERT(args.length() == 2);
8341 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8342 RUNTIME_ASSERT(obj->HasNamedInterceptor());
8343 CONVERT_ARG_CHECKED(String, name, 1);
8344
8345 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008346 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008347}
8348
8349
8350// Return element value from indexed interceptor.
8351// args[0]: object
8352// args[1]: index
8353static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
8354 HandleScope scope;
8355 ASSERT(args.length() == 2);
8356 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8357 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
8358 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
8359
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008360 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008361}
8362
8363
8364static Object* Runtime_CheckExecutionState(Arguments args) {
8365 ASSERT(args.length() >= 1);
8366 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00008367 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008368 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008369 return Top::Throw(Heap::illegal_execution_state_symbol());
8370 }
8371
8372 return Heap::true_value();
8373}
8374
8375
8376static Object* Runtime_GetFrameCount(Arguments args) {
8377 HandleScope scope;
8378 ASSERT(args.length() == 1);
8379
8380 // Check arguments.
8381 Object* result = Runtime_CheckExecutionState(args);
8382 if (result->IsFailure()) return result;
8383
8384 // Count all frames which are relevant to debugging stack trace.
8385 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008386 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008387 if (id == StackFrame::NO_ID) {
8388 // If there is no JavaScript stack frame count is 0.
8389 return Smi::FromInt(0);
8390 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008391 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
8392 return Smi::FromInt(n);
8393}
8394
8395
8396static const int kFrameDetailsFrameIdIndex = 0;
8397static const int kFrameDetailsReceiverIndex = 1;
8398static const int kFrameDetailsFunctionIndex = 2;
8399static const int kFrameDetailsArgumentCountIndex = 3;
8400static const int kFrameDetailsLocalCountIndex = 4;
8401static const int kFrameDetailsSourcePositionIndex = 5;
8402static const int kFrameDetailsConstructCallIndex = 6;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008403static const int kFrameDetailsAtReturnIndex = 7;
8404static const int kFrameDetailsDebuggerFrameIndex = 8;
8405static const int kFrameDetailsFirstDynamicIndex = 9;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008406
8407// Return an array with frame details
8408// args[0]: number: break id
8409// args[1]: number: frame index
8410//
8411// The array returned contains the following information:
8412// 0: Frame id
8413// 1: Receiver
8414// 2: Function
8415// 3: Argument count
8416// 4: Local count
8417// 5: Source position
8418// 6: Constructor call
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008419// 7: Is at return
8420// 8: Debugger frame
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008421// Arguments name, value
8422// Locals name, value
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008423// Return value if any
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008424static Object* Runtime_GetFrameDetails(Arguments args) {
8425 HandleScope scope;
8426 ASSERT(args.length() == 2);
8427
8428 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008429 Object* check = Runtime_CheckExecutionState(args);
8430 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008431 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8432
8433 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008434 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008435 if (id == StackFrame::NO_ID) {
8436 // If there are no JavaScript stack frames return undefined.
8437 return Heap::undefined_value();
8438 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008439 int count = 0;
8440 JavaScriptFrameIterator it(id);
8441 for (; !it.done(); it.Advance()) {
8442 if (count == index) break;
8443 count++;
8444 }
8445 if (it.done()) return Heap::undefined_value();
8446
8447 // Traverse the saved contexts chain to find the active context for the
8448 // selected frame.
8449 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008450 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008451 save = save->prev();
8452 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008453 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008454
8455 // Get the frame id.
8456 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
8457
8458 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008459 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008460
8461 // Check for constructor frame.
8462 bool constructor = it.frame()->IsConstructor();
8463
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008464 // Get scope info and read from it for local variable information.
8465 Handle<JSFunction> function(JSFunction::cast(it.frame()->function()));
ager@chromium.orgb5737492010-07-15 09:29:43 +00008466 Handle<SerializedScopeInfo> scope_info(function->shared()->scope_info());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008467 ScopeInfo<> info(*scope_info);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008468
8469 // Get the context.
8470 Handle<Context> context(Context::cast(it.frame()->context()));
8471
8472 // Get the locals names and values into a temporary array.
8473 //
8474 // TODO(1240907): Hide compiler-introduced stack variables
8475 // (e.g. .result)? For users of the debugger, they will probably be
8476 // confusing.
8477 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
8478 for (int i = 0; i < info.NumberOfLocals(); i++) {
8479 // Name of the local.
8480 locals->set(i * 2, *info.LocalName(i));
8481
8482 // Fetch the value of the local - either from the stack or from a
8483 // heap-allocated context.
8484 if (i < info.number_of_stack_slots()) {
8485 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
8486 } else {
8487 Handle<String> name = info.LocalName(i);
8488 // Traverse the context chain to the function context as all local
8489 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008490 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008491 context = Handle<Context>(context->previous());
8492 }
8493 ASSERT(context->is_function_context());
8494 locals->set(i * 2 + 1,
ager@chromium.orgb5737492010-07-15 09:29:43 +00008495 context->get(scope_info->ContextSlotIndex(*name, NULL)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008496 }
8497 }
8498
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008499 // Check whether this frame is positioned at return.
8500 int at_return = (index == 0) ? Debug::IsBreakAtReturn(it.frame()) : false;
8501
8502 // If positioned just before return find the value to be returned and add it
8503 // to the frame information.
8504 Handle<Object> return_value = Factory::undefined_value();
8505 if (at_return) {
8506 StackFrameIterator it2;
8507 Address internal_frame_sp = NULL;
8508 while (!it2.done()) {
8509 if (it2.frame()->is_internal()) {
8510 internal_frame_sp = it2.frame()->sp();
8511 } else {
8512 if (it2.frame()->is_java_script()) {
8513 if (it2.frame()->id() == it.frame()->id()) {
8514 // The internal frame just before the JavaScript frame contains the
8515 // value to return on top. A debug break at return will create an
8516 // internal frame to store the return value (eax/rax/r0) before
8517 // entering the debug break exit frame.
8518 if (internal_frame_sp != NULL) {
8519 return_value =
8520 Handle<Object>(Memory::Object_at(internal_frame_sp));
8521 break;
8522 }
8523 }
8524 }
8525
8526 // Indicate that the previous frame was not an internal frame.
8527 internal_frame_sp = NULL;
8528 }
8529 it2.Advance();
8530 }
8531 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008532
8533 // Now advance to the arguments adapter frame (if any). It contains all
8534 // the provided parameters whereas the function frame always have the number
8535 // of arguments matching the functions parameters. The rest of the
8536 // information (except for what is collected above) is the same.
8537 it.AdvanceToArgumentsFrame();
8538
8539 // Find the number of arguments to fill. At least fill the number of
8540 // parameters for the function and fill more if more parameters are provided.
8541 int argument_count = info.number_of_parameters();
8542 if (argument_count < it.frame()->GetProvidedParametersCount()) {
8543 argument_count = it.frame()->GetProvidedParametersCount();
8544 }
8545
8546 // Calculate the size of the result.
8547 int details_size = kFrameDetailsFirstDynamicIndex +
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008548 2 * (argument_count + info.NumberOfLocals()) +
8549 (at_return ? 1 : 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008550 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8551
8552 // Add the frame id.
8553 details->set(kFrameDetailsFrameIdIndex, *frame_id);
8554
8555 // Add the function (same as in function frame).
8556 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
8557
8558 // Add the arguments count.
8559 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
8560
8561 // Add the locals count
8562 details->set(kFrameDetailsLocalCountIndex,
8563 Smi::FromInt(info.NumberOfLocals()));
8564
8565 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00008566 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008567 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
8568 } else {
8569 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
8570 }
8571
8572 // Add the constructor information.
8573 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
8574
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008575 // Add the at return information.
8576 details->set(kFrameDetailsAtReturnIndex, Heap::ToBoolean(at_return));
8577
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008578 // Add information on whether this frame is invoked in the debugger context.
8579 details->set(kFrameDetailsDebuggerFrameIndex,
8580 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
8581
8582 // Fill the dynamic part.
8583 int details_index = kFrameDetailsFirstDynamicIndex;
8584
8585 // Add arguments name and value.
8586 for (int i = 0; i < argument_count; i++) {
8587 // Name of the argument.
8588 if (i < info.number_of_parameters()) {
8589 details->set(details_index++, *info.parameter_name(i));
8590 } else {
8591 details->set(details_index++, Heap::undefined_value());
8592 }
8593
8594 // Parameter value.
8595 if (i < it.frame()->GetProvidedParametersCount()) {
8596 details->set(details_index++, it.frame()->GetParameter(i));
8597 } else {
8598 details->set(details_index++, Heap::undefined_value());
8599 }
8600 }
8601
8602 // Add locals name and value from the temporary copy from the function frame.
8603 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
8604 details->set(details_index++, locals->get(i));
8605 }
8606
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008607 // Add the value being returned.
8608 if (at_return) {
8609 details->set(details_index++, *return_value);
8610 }
8611
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008612 // Add the receiver (same as in function frame).
8613 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
8614 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
8615 Handle<Object> receiver(it.frame()->receiver());
8616 if (!receiver->IsJSObject()) {
8617 // If the receiver is NOT a JSObject we have hit an optimization
8618 // where a value object is not converted into a wrapped JS objects.
8619 // To hide this optimization from the debugger, we wrap the receiver
8620 // by creating correct wrapper object based on the calling frame's
8621 // global context.
8622 it.Advance();
8623 Handle<Context> calling_frames_global_context(
8624 Context::cast(Context::cast(it.frame()->context())->global_context()));
8625 receiver = Factory::ToObject(receiver, calling_frames_global_context);
8626 }
8627 details->set(kFrameDetailsReceiverIndex, *receiver);
8628
8629 ASSERT_EQ(details_size, details_index);
8630 return *Factory::NewJSArrayWithElements(details);
8631}
8632
8633
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008634// Copy all the context locals into an object used to materialize a scope.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008635static void CopyContextLocalsToScopeObject(
8636 Handle<SerializedScopeInfo> serialized_scope_info,
8637 ScopeInfo<>& scope_info,
8638 Handle<Context> context,
8639 Handle<JSObject> scope_object) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008640 // Fill all context locals to the context extension.
8641 for (int i = Context::MIN_CONTEXT_SLOTS;
8642 i < scope_info.number_of_context_slots();
8643 i++) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00008644 int context_index = serialized_scope_info->ContextSlotIndex(
8645 *scope_info.context_slot_name(i), NULL);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008646
8647 // Don't include the arguments shadow (.arguments) context variable.
8648 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
8649 SetProperty(scope_object,
8650 scope_info.context_slot_name(i),
8651 Handle<Object>(context->get(context_index)), NONE);
8652 }
8653 }
8654}
8655
8656
8657// Create a plain JSObject which materializes the local scope for the specified
8658// frame.
8659static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
8660 Handle<JSFunction> function(JSFunction::cast(frame->function()));
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008661 Handle<SharedFunctionInfo> shared(function->shared());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008662 Handle<SerializedScopeInfo> serialized_scope_info(shared->scope_info());
8663 ScopeInfo<> scope_info(*serialized_scope_info);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008664
8665 // Allocate and initialize a JSObject with all the arguments, stack locals
8666 // heap locals and extension properties of the debugged function.
8667 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
8668
8669 // First fill all parameters.
8670 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8671 SetProperty(local_scope,
8672 scope_info.parameter_name(i),
8673 Handle<Object>(frame->GetParameter(i)), NONE);
8674 }
8675
8676 // Second fill all stack locals.
8677 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
8678 SetProperty(local_scope,
8679 scope_info.stack_slot_name(i),
8680 Handle<Object>(frame->GetExpression(i)), NONE);
8681 }
8682
8683 // Third fill all context locals.
8684 Handle<Context> frame_context(Context::cast(frame->context()));
8685 Handle<Context> function_context(frame_context->fcontext());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008686 CopyContextLocalsToScopeObject(serialized_scope_info, scope_info,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008687 function_context, local_scope);
8688
8689 // Finally copy any properties from the function context extension. This will
8690 // be variables introduced by eval.
8691 if (function_context->closure() == *function) {
8692 if (function_context->has_extension() &&
8693 !function_context->IsGlobalContext()) {
8694 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008695 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008696 for (int i = 0; i < keys->length(); i++) {
8697 // Names of variables introduced by eval are strings.
8698 ASSERT(keys->get(i)->IsString());
8699 Handle<String> key(String::cast(keys->get(i)));
8700 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
8701 }
8702 }
8703 }
8704 return local_scope;
8705}
8706
8707
8708// Create a plain JSObject which materializes the closure content for the
8709// context.
8710static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
8711 ASSERT(context->is_function_context());
8712
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008713 Handle<SharedFunctionInfo> shared(context->closure()->shared());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008714 Handle<SerializedScopeInfo> serialized_scope_info(shared->scope_info());
8715 ScopeInfo<> scope_info(*serialized_scope_info);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008716
8717 // Allocate and initialize a JSObject with all the content of theis function
8718 // closure.
8719 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
8720
8721 // Check whether the arguments shadow object exists.
8722 int arguments_shadow_index =
ager@chromium.orgb5737492010-07-15 09:29:43 +00008723 shared->scope_info()->ContextSlotIndex(Heap::arguments_shadow_symbol(),
8724 NULL);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008725 if (arguments_shadow_index >= 0) {
8726 // In this case all the arguments are available in the arguments shadow
8727 // object.
8728 Handle<JSObject> arguments_shadow(
8729 JSObject::cast(context->get(arguments_shadow_index)));
8730 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8731 SetProperty(closure_scope,
8732 scope_info.parameter_name(i),
8733 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
8734 }
8735 }
8736
8737 // Fill all context locals to the context extension.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008738 CopyContextLocalsToScopeObject(serialized_scope_info, scope_info,
8739 context, closure_scope);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008740
8741 // Finally copy any properties from the function context extension. This will
8742 // be variables introduced by eval.
8743 if (context->has_extension()) {
8744 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008745 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008746 for (int i = 0; i < keys->length(); i++) {
8747 // Names of variables introduced by eval are strings.
8748 ASSERT(keys->get(i)->IsString());
8749 Handle<String> key(String::cast(keys->get(i)));
8750 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
8751 }
8752 }
8753
8754 return closure_scope;
8755}
8756
8757
8758// Iterate over the actual scopes visible from a stack frame. All scopes are
8759// backed by an actual context except the local scope, which is inserted
8760// "artifically" in the context chain.
8761class ScopeIterator {
8762 public:
8763 enum ScopeType {
8764 ScopeTypeGlobal = 0,
8765 ScopeTypeLocal,
8766 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00008767 ScopeTypeClosure,
8768 // Every catch block contains an implicit with block (its parameter is
8769 // a JSContextExtensionObject) that extends current scope with a variable
8770 // holding exception object. Such with blocks are treated as scopes of their
8771 // own type.
8772 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008773 };
8774
8775 explicit ScopeIterator(JavaScriptFrame* frame)
8776 : frame_(frame),
8777 function_(JSFunction::cast(frame->function())),
8778 context_(Context::cast(frame->context())),
8779 local_done_(false),
8780 at_local_(false) {
8781
8782 // Check whether the first scope is actually a local scope.
8783 if (context_->IsGlobalContext()) {
8784 // If there is a stack slot for .result then this local scope has been
8785 // created for evaluating top level code and it is not a real local scope.
8786 // Checking for the existence of .result seems fragile, but the scope info
8787 // saved with the code object does not otherwise have that information.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008788 int index = function_->shared()->scope_info()->
8789 StackSlotIndex(Heap::result_symbol());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008790 at_local_ = index < 0;
8791 } else if (context_->is_function_context()) {
8792 at_local_ = true;
8793 }
8794 }
8795
8796 // More scopes?
8797 bool Done() { return context_.is_null(); }
8798
8799 // Move to the next scope.
8800 void Next() {
8801 // If at a local scope mark the local scope as passed.
8802 if (at_local_) {
8803 at_local_ = false;
8804 local_done_ = true;
8805
8806 // If the current context is not associated with the local scope the
8807 // current context is the next real scope, so don't move to the next
8808 // context in this case.
8809 if (context_->closure() != *function_) {
8810 return;
8811 }
8812 }
8813
8814 // The global scope is always the last in the chain.
8815 if (context_->IsGlobalContext()) {
8816 context_ = Handle<Context>();
8817 return;
8818 }
8819
8820 // Move to the next context.
8821 if (context_->is_function_context()) {
8822 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
8823 } else {
8824 context_ = Handle<Context>(context_->previous());
8825 }
8826
8827 // If passing the local scope indicate that the current scope is now the
8828 // local scope.
8829 if (!local_done_ &&
8830 (context_->IsGlobalContext() || (context_->is_function_context()))) {
8831 at_local_ = true;
8832 }
8833 }
8834
8835 // Return the type of the current scope.
8836 int Type() {
8837 if (at_local_) {
8838 return ScopeTypeLocal;
8839 }
8840 if (context_->IsGlobalContext()) {
8841 ASSERT(context_->global()->IsGlobalObject());
8842 return ScopeTypeGlobal;
8843 }
8844 if (context_->is_function_context()) {
8845 return ScopeTypeClosure;
8846 }
8847 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00008848 // Current scope is either an explicit with statement or a with statement
8849 // implicitely generated for a catch block.
8850 // If the extension object here is a JSContextExtensionObject then
8851 // current with statement is one frome a catch block otherwise it's a
8852 // regular with statement.
8853 if (context_->extension()->IsJSContextExtensionObject()) {
8854 return ScopeTypeCatch;
8855 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008856 return ScopeTypeWith;
8857 }
8858
8859 // Return the JavaScript object with the content of the current scope.
8860 Handle<JSObject> ScopeObject() {
8861 switch (Type()) {
8862 case ScopeIterator::ScopeTypeGlobal:
8863 return Handle<JSObject>(CurrentContext()->global());
8864 break;
8865 case ScopeIterator::ScopeTypeLocal:
8866 // Materialize the content of the local scope into a JSObject.
8867 return MaterializeLocalScope(frame_);
8868 break;
8869 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00008870 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008871 // Return the with object.
8872 return Handle<JSObject>(CurrentContext()->extension());
8873 break;
8874 case ScopeIterator::ScopeTypeClosure:
8875 // Materialize the content of the closure scope into a JSObject.
8876 return MaterializeClosure(CurrentContext());
8877 break;
8878 }
8879 UNREACHABLE();
8880 return Handle<JSObject>();
8881 }
8882
8883 // Return the context for this scope. For the local context there might not
8884 // be an actual context.
8885 Handle<Context> CurrentContext() {
8886 if (at_local_ && context_->closure() != *function_) {
8887 return Handle<Context>();
8888 }
8889 return context_;
8890 }
8891
8892#ifdef DEBUG
8893 // Debug print of the content of the current scope.
8894 void DebugPrint() {
8895 switch (Type()) {
8896 case ScopeIterator::ScopeTypeGlobal:
8897 PrintF("Global:\n");
8898 CurrentContext()->Print();
8899 break;
8900
8901 case ScopeIterator::ScopeTypeLocal: {
8902 PrintF("Local:\n");
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008903 ScopeInfo<> scope_info(function_->shared()->scope_info());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008904 scope_info.Print();
8905 if (!CurrentContext().is_null()) {
8906 CurrentContext()->Print();
8907 if (CurrentContext()->has_extension()) {
8908 Handle<JSObject> extension =
8909 Handle<JSObject>(CurrentContext()->extension());
8910 if (extension->IsJSContextExtensionObject()) {
8911 extension->Print();
8912 }
8913 }
8914 }
8915 break;
8916 }
8917
8918 case ScopeIterator::ScopeTypeWith: {
8919 PrintF("With:\n");
8920 Handle<JSObject> extension =
8921 Handle<JSObject>(CurrentContext()->extension());
8922 extension->Print();
8923 break;
8924 }
8925
ager@chromium.orga1645e22009-09-09 19:27:10 +00008926 case ScopeIterator::ScopeTypeCatch: {
8927 PrintF("Catch:\n");
8928 Handle<JSObject> extension =
8929 Handle<JSObject>(CurrentContext()->extension());
8930 extension->Print();
8931 break;
8932 }
8933
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008934 case ScopeIterator::ScopeTypeClosure: {
8935 PrintF("Closure:\n");
8936 CurrentContext()->Print();
8937 if (CurrentContext()->has_extension()) {
8938 Handle<JSObject> extension =
8939 Handle<JSObject>(CurrentContext()->extension());
8940 if (extension->IsJSContextExtensionObject()) {
8941 extension->Print();
8942 }
8943 }
8944 break;
8945 }
8946
8947 default:
8948 UNREACHABLE();
8949 }
8950 PrintF("\n");
8951 }
8952#endif
8953
8954 private:
8955 JavaScriptFrame* frame_;
8956 Handle<JSFunction> function_;
8957 Handle<Context> context_;
8958 bool local_done_;
8959 bool at_local_;
8960
8961 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
8962};
8963
8964
8965static Object* Runtime_GetScopeCount(Arguments args) {
8966 HandleScope scope;
8967 ASSERT(args.length() == 2);
8968
8969 // Check arguments.
8970 Object* check = Runtime_CheckExecutionState(args);
8971 if (check->IsFailure()) return check;
8972 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8973
8974 // Get the frame where the debugging is performed.
8975 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8976 JavaScriptFrameIterator it(id);
8977 JavaScriptFrame* frame = it.frame();
8978
8979 // Count the visible scopes.
8980 int n = 0;
8981 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8982 n++;
8983 }
8984
8985 return Smi::FromInt(n);
8986}
8987
8988
8989static const int kScopeDetailsTypeIndex = 0;
8990static const int kScopeDetailsObjectIndex = 1;
8991static const int kScopeDetailsSize = 2;
8992
8993// Return an array with scope details
8994// args[0]: number: break id
8995// args[1]: number: frame index
8996// args[2]: number: scope index
8997//
8998// The array returned contains the following information:
8999// 0: Scope type
9000// 1: Scope object
9001static Object* Runtime_GetScopeDetails(Arguments args) {
9002 HandleScope scope;
9003 ASSERT(args.length() == 3);
9004
9005 // Check arguments.
9006 Object* check = Runtime_CheckExecutionState(args);
9007 if (check->IsFailure()) return check;
9008 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9009 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
9010
9011 // Get the frame where the debugging is performed.
9012 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9013 JavaScriptFrameIterator frame_it(id);
9014 JavaScriptFrame* frame = frame_it.frame();
9015
9016 // Find the requested scope.
9017 int n = 0;
9018 ScopeIterator it(frame);
9019 for (; !it.Done() && n < index; it.Next()) {
9020 n++;
9021 }
9022 if (it.Done()) {
9023 return Heap::undefined_value();
9024 }
9025
9026 // Calculate the size of the result.
9027 int details_size = kScopeDetailsSize;
9028 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
9029
9030 // Fill in scope details.
9031 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
9032 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
9033
9034 return *Factory::NewJSArrayWithElements(details);
9035}
9036
9037
9038static Object* Runtime_DebugPrintScopes(Arguments args) {
9039 HandleScope scope;
9040 ASSERT(args.length() == 0);
9041
9042#ifdef DEBUG
9043 // Print the scopes for the top frame.
9044 StackFrameLocator locator;
9045 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
9046 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
9047 it.DebugPrint();
9048 }
9049#endif
9050 return Heap::undefined_value();
9051}
9052
9053
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009054static Object* Runtime_GetCFrames(Arguments args) {
9055 HandleScope scope;
9056 ASSERT(args.length() == 1);
9057 Object* result = Runtime_CheckExecutionState(args);
9058 if (result->IsFailure()) return result;
9059
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00009060#if V8_HOST_ARCH_64_BIT
9061 UNIMPLEMENTED();
9062 return Heap::undefined_value();
9063#else
9064
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009065 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009066 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
9067 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009068 if (frames_count == OS::kStackWalkError) {
9069 return Heap::undefined_value();
9070 }
9071
9072 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
9073 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
9074 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
9075 for (int i = 0; i < frames_count; i++) {
9076 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
9077 frame_value->SetProperty(
9078 *address_str,
9079 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
9080 NONE);
9081
9082 // Get the stack walk text for this frame.
9083 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009084 int frame_text_length = StrLength(frames[i].text);
9085 if (frame_text_length > 0) {
9086 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009087 frame_text = Factory::NewStringFromAscii(str);
9088 }
9089
9090 if (!frame_text.is_null()) {
9091 frame_value->SetProperty(*text_str, *frame_text, NONE);
9092 }
9093
9094 frames_array->set(i, *frame_value);
9095 }
9096 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00009097#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009098}
9099
9100
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00009101static Object* Runtime_GetThreadCount(Arguments args) {
9102 HandleScope scope;
9103 ASSERT(args.length() == 1);
9104
9105 // Check arguments.
9106 Object* result = Runtime_CheckExecutionState(args);
9107 if (result->IsFailure()) return result;
9108
9109 // Count all archived V8 threads.
9110 int n = 0;
9111 for (ThreadState* thread = ThreadState::FirstInUse();
9112 thread != NULL;
9113 thread = thread->Next()) {
9114 n++;
9115 }
9116
9117 // Total number of threads is current thread and archived threads.
9118 return Smi::FromInt(n + 1);
9119}
9120
9121
9122static const int kThreadDetailsCurrentThreadIndex = 0;
9123static const int kThreadDetailsThreadIdIndex = 1;
9124static const int kThreadDetailsSize = 2;
9125
9126// Return an array with thread details
9127// args[0]: number: break id
9128// args[1]: number: thread index
9129//
9130// The array returned contains the following information:
9131// 0: Is current thread?
9132// 1: Thread id
9133static Object* Runtime_GetThreadDetails(Arguments args) {
9134 HandleScope scope;
9135 ASSERT(args.length() == 2);
9136
9137 // Check arguments.
9138 Object* check = Runtime_CheckExecutionState(args);
9139 if (check->IsFailure()) return check;
9140 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
9141
9142 // Allocate array for result.
9143 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
9144
9145 // Thread index 0 is current thread.
9146 if (index == 0) {
9147 // Fill the details.
9148 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
9149 details->set(kThreadDetailsThreadIdIndex,
9150 Smi::FromInt(ThreadManager::CurrentId()));
9151 } else {
9152 // Find the thread with the requested index.
9153 int n = 1;
9154 ThreadState* thread = ThreadState::FirstInUse();
9155 while (index != n && thread != NULL) {
9156 thread = thread->Next();
9157 n++;
9158 }
9159 if (thread == NULL) {
9160 return Heap::undefined_value();
9161 }
9162
9163 // Fill the details.
9164 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
9165 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
9166 }
9167
9168 // Convert to JS array and return.
9169 return *Factory::NewJSArrayWithElements(details);
9170}
9171
9172
whesse@chromium.orge90029b2010-08-02 11:52:17 +00009173// Sets the disable break state
9174// args[0]: disable break state
9175static Object* Runtime_SetDisableBreak(Arguments args) {
9176 HandleScope scope;
9177 ASSERT(args.length() == 1);
9178 CONVERT_BOOLEAN_CHECKED(disable_break, args[0]);
9179 Debug::set_disable_break(disable_break);
9180 return Heap::undefined_value();
9181}
9182
9183
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009184static Object* Runtime_GetBreakLocations(Arguments args) {
9185 HandleScope scope;
9186 ASSERT(args.length() == 1);
9187
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009188 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9189 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009190 // Find the number of break points
9191 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
9192 if (break_locations->IsUndefined()) return Heap::undefined_value();
9193 // Return array as JS array
9194 return *Factory::NewJSArrayWithElements(
9195 Handle<FixedArray>::cast(break_locations));
9196}
9197
9198
9199// Set a break point in a function
9200// args[0]: function
9201// args[1]: number: break source position (within the function source)
9202// args[2]: number: break point object
9203static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
9204 HandleScope scope;
9205 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009206 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9207 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009208 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9209 RUNTIME_ASSERT(source_position >= 0);
9210 Handle<Object> break_point_object_arg = args.at<Object>(2);
9211
9212 // Set break point.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009213 Debug::SetBreakPoint(shared, break_point_object_arg, &source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009214
lrn@chromium.org32d961d2010-06-30 09:09:34 +00009215 return Smi::FromInt(source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009216}
9217
9218
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009219Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
9220 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009221 // Iterate the heap looking for SharedFunctionInfo generated from the
9222 // script. The inner most SharedFunctionInfo containing the source position
9223 // for the requested break point is found.
9224 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
9225 // which is found is not compiled it is compiled and the heap is iterated
9226 // again as the compilation might create inner functions from the newly
9227 // compiled function and the actual requested break point might be in one of
9228 // these functions.
9229 bool done = false;
9230 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00009231 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009232 Handle<SharedFunctionInfo> target;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009233 while (!done) {
9234 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009235 for (HeapObject* obj = iterator.next();
9236 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009237 if (obj->IsSharedFunctionInfo()) {
9238 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
9239 if (shared->script() == *script) {
9240 // If the SharedFunctionInfo found has the requested script data and
9241 // contains the source position it is a candidate.
9242 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00009243 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009244 start_position = shared->start_position();
9245 }
9246 if (start_position <= position &&
9247 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00009248 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009249 // candidate this is the new candidate.
9250 if (target.is_null()) {
9251 target_start_position = start_position;
9252 target = shared;
9253 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00009254 if (target_start_position == start_position &&
9255 shared->end_position() == target->end_position()) {
9256 // If a top-level function contain only one function
9257 // declartion the source for the top-level and the function is
9258 // the same. In that case prefer the non top-level function.
9259 if (!shared->is_toplevel()) {
9260 target_start_position = start_position;
9261 target = shared;
9262 }
9263 } else if (target_start_position <= start_position &&
9264 shared->end_position() <= target->end_position()) {
9265 // This containment check includes equality as a function inside
9266 // a top-level function can share either start or end position
9267 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009268 target_start_position = start_position;
9269 target = shared;
9270 }
9271 }
9272 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009273 }
9274 }
9275 }
9276
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009277 if (target.is_null()) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009278 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009279 }
9280
9281 // If the candidate found is compiled we are done. NOTE: when lazy
9282 // compilation of inner functions is introduced some additional checking
9283 // needs to be done here to compile inner functions.
9284 done = target->is_compiled();
9285 if (!done) {
9286 // If the candidate is not compiled compile it to reveal any inner
9287 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009288 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009289 }
9290 }
9291
9292 return *target;
9293}
9294
9295
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009296// Changes the state of a break point in a script and returns source position
9297// where break point was set. NOTE: Regarding performance see the NOTE for
9298// GetScriptFromScriptData.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009299// args[0]: script to set break point in
9300// args[1]: number: break source position (within the script source)
9301// args[2]: number: break point object
9302static Object* Runtime_SetScriptBreakPoint(Arguments args) {
9303 HandleScope scope;
9304 ASSERT(args.length() == 3);
9305 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
9306 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9307 RUNTIME_ASSERT(source_position >= 0);
9308 Handle<Object> break_point_object_arg = args.at<Object>(2);
9309
9310 // Get the script from the script wrapper.
9311 RUNTIME_ASSERT(wrapper->value()->IsScript());
9312 Handle<Script> script(Script::cast(wrapper->value()));
9313
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009314 Object* result = Runtime::FindSharedFunctionInfoInScript(
9315 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009316 if (!result->IsUndefined()) {
9317 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
9318 // Find position within function. The script position might be before the
9319 // source position of the first function.
9320 int position;
9321 if (shared->start_position() > source_position) {
9322 position = 0;
9323 } else {
9324 position = source_position - shared->start_position();
9325 }
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009326 Debug::SetBreakPoint(shared, break_point_object_arg, &position);
9327 position += shared->start_position();
9328 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009329 }
9330 return Heap::undefined_value();
9331}
9332
9333
9334// Clear a break point
9335// args[0]: number: break point object
9336static Object* Runtime_ClearBreakPoint(Arguments args) {
9337 HandleScope scope;
9338 ASSERT(args.length() == 1);
9339 Handle<Object> break_point_object_arg = args.at<Object>(0);
9340
9341 // Clear break point.
9342 Debug::ClearBreakPoint(break_point_object_arg);
9343
9344 return Heap::undefined_value();
9345}
9346
9347
9348// Change the state of break on exceptions
9349// args[0]: boolean indicating uncaught exceptions
9350// args[1]: boolean indicating on/off
9351static Object* Runtime_ChangeBreakOnException(Arguments args) {
9352 HandleScope scope;
9353 ASSERT(args.length() == 2);
9354 ASSERT(args[0]->IsNumber());
9355 ASSERT(args[1]->IsBoolean());
9356
9357 // Update break point state
9358 ExceptionBreakType type =
9359 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
9360 bool enable = args[1]->ToBoolean()->IsTrue();
9361 Debug::ChangeBreakOnException(type, enable);
9362 return Heap::undefined_value();
9363}
9364
9365
9366// Prepare for stepping
9367// args[0]: break id for checking execution state
9368// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00009369// args[2]: number of times to perform the step, for step out it is the number
9370// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009371static Object* Runtime_PrepareStep(Arguments args) {
9372 HandleScope scope;
9373 ASSERT(args.length() == 3);
9374 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009375 Object* check = Runtime_CheckExecutionState(args);
9376 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009377 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
9378 return Top::Throw(Heap::illegal_argument_symbol());
9379 }
9380
9381 // Get the step action and check validity.
9382 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
9383 if (step_action != StepIn &&
9384 step_action != StepNext &&
9385 step_action != StepOut &&
9386 step_action != StepInMin &&
9387 step_action != StepMin) {
9388 return Top::Throw(Heap::illegal_argument_symbol());
9389 }
9390
9391 // Get the number of steps.
9392 int step_count = NumberToInt32(args[2]);
9393 if (step_count < 1) {
9394 return Top::Throw(Heap::illegal_argument_symbol());
9395 }
9396
ager@chromium.orga1645e22009-09-09 19:27:10 +00009397 // Clear all current stepping setup.
9398 Debug::ClearStepping();
9399
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009400 // Prepare step.
9401 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
9402 return Heap::undefined_value();
9403}
9404
9405
9406// Clear all stepping set by PrepareStep.
9407static Object* Runtime_ClearStepping(Arguments args) {
9408 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009409 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009410 Debug::ClearStepping();
9411 return Heap::undefined_value();
9412}
9413
9414
9415// Creates a copy of the with context chain. The copy of the context chain is
9416// is linked to the function context supplied.
9417static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
9418 Handle<Context> function_context) {
9419 // At the bottom of the chain. Return the function context to link to.
9420 if (context_chain->is_function_context()) {
9421 return function_context;
9422 }
9423
9424 // Recursively copy the with contexts.
9425 Handle<Context> previous(context_chain->previous());
9426 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
9427 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009428 CopyWithContextChain(function_context, previous),
9429 extension,
9430 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009431}
9432
9433
9434// Helper function to find or create the arguments object for
9435// Runtime_DebugEvaluate.
9436static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
9437 Handle<JSFunction> function,
ager@chromium.orgb5737492010-07-15 09:29:43 +00009438 Handle<SerializedScopeInfo> scope_info,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009439 const ScopeInfo<>* sinfo,
9440 Handle<Context> function_context) {
9441 // Try to find the value of 'arguments' to pass as parameter. If it is not
9442 // found (that is the debugged function does not reference 'arguments' and
9443 // does not support eval) then create an 'arguments' object.
9444 int index;
9445 if (sinfo->number_of_stack_slots() > 0) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00009446 index = scope_info->StackSlotIndex(Heap::arguments_symbol());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009447 if (index != -1) {
9448 return Handle<Object>(frame->GetExpression(index));
9449 }
9450 }
9451
9452 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00009453 index = scope_info->ContextSlotIndex(Heap::arguments_symbol(), NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009454 if (index != -1) {
9455 return Handle<Object>(function_context->get(index));
9456 }
9457 }
9458
9459 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009460 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
9461 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009462
9463 AssertNoAllocation no_gc;
9464 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009465 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009466 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009467 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009468 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009469 return arguments;
9470}
9471
9472
9473// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00009474// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009475// extension part has all the parameters and locals of the function on the
9476// stack frame. A function which calls eval with the code to evaluate is then
9477// compiled in this context and called in this context. As this context
9478// replaces the context of the function on the stack frame a new (empty)
9479// function is created as well to be used as the closure for the context.
9480// This function and the context acts as replacements for the function on the
9481// stack frame presenting the same view of the values of parameters and
9482// local variables as if the piece of JavaScript was evaluated at the point
9483// where the function on the stack frame is currently stopped.
9484static Object* Runtime_DebugEvaluate(Arguments args) {
9485 HandleScope scope;
9486
9487 // Check the execution state and decode arguments frame and source to be
9488 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009489 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009490 Object* check_result = Runtime_CheckExecutionState(args);
9491 if (check_result->IsFailure()) return check_result;
9492 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9493 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009494 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
9495
9496 // Handle the processing of break.
9497 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009498
9499 // Get the frame where the debugging is performed.
9500 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9501 JavaScriptFrameIterator it(id);
9502 JavaScriptFrame* frame = it.frame();
9503 Handle<JSFunction> function(JSFunction::cast(frame->function()));
ager@chromium.orgb5737492010-07-15 09:29:43 +00009504 Handle<SerializedScopeInfo> scope_info(function->shared()->scope_info());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009505 ScopeInfo<> sinfo(*scope_info);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009506
9507 // Traverse the saved contexts chain to find the active context for the
9508 // selected frame.
9509 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009510 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009511 save = save->prev();
9512 }
9513 ASSERT(save != NULL);
9514 SaveContext savex;
9515 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009516
9517 // Create the (empty) function replacing the function on the stack frame for
9518 // the purpose of evaluating in the context created below. It is important
9519 // that this function does not describe any parameters and local variables
9520 // in the context. If it does then this will cause problems with the lookup
9521 // in Context::Lookup, where context slots for parameters and local variables
9522 // are looked at before the extension object.
9523 Handle<JSFunction> go_between =
9524 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
9525 go_between->set_context(function->context());
9526#ifdef DEBUG
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009527 ScopeInfo<> go_between_sinfo(go_between->shared()->scope_info());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009528 ASSERT(go_between_sinfo.number_of_parameters() == 0);
9529 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
9530#endif
9531
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009532 // Materialize the content of the local scope into a JSObject.
9533 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009534
9535 // Allocate a new context for the debug evaluation and set the extension
9536 // object build.
9537 Handle<Context> context =
9538 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009539 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009540 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009541 Handle<Context> frame_context(Context::cast(frame->context()));
9542 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009543 context = CopyWithContextChain(frame_context, context);
9544
9545 // Wrap the evaluation statement in a new function compiled in the newly
9546 // created context. The function has one parameter which has to be called
9547 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00009548 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009549 // function(arguments,__source__) {return eval(__source__);}
9550 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00009551 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009552 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009553 Handle<String> function_source =
9554 Factory::NewStringFromAscii(Vector<const char>(source_str,
9555 source_str_length));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009556 Handle<SharedFunctionInfo> shared =
ager@chromium.org381abbb2009-02-25 13:23:22 +00009557 Compiler::CompileEval(function_source,
9558 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00009559 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00009560 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009561 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009562 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009563 Factory::NewFunctionFromSharedFunctionInfo(shared, context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009564
9565 // Invoke the result of the compilation to get the evaluation function.
9566 bool has_pending_exception;
9567 Handle<Object> receiver(frame->receiver());
9568 Handle<Object> evaluation_function =
9569 Execution::Call(compiled_function, receiver, 0, NULL,
9570 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009571 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009572
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009573 Handle<Object> arguments = GetArgumentsObject(frame, function, scope_info,
9574 &sinfo, function_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009575
9576 // Invoke the evaluation function and return the result.
9577 const int argc = 2;
9578 Object** argv[argc] = { arguments.location(),
9579 Handle<Object>::cast(source).location() };
9580 Handle<Object> result =
9581 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
9582 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009583 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009584
9585 // Skip the global proxy as it has no properties and always delegates to the
9586 // real global object.
9587 if (result->IsJSGlobalProxy()) {
9588 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
9589 }
9590
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009591 return *result;
9592}
9593
9594
9595static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
9596 HandleScope scope;
9597
9598 // Check the execution state and decode arguments frame and source to be
9599 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009600 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009601 Object* check_result = Runtime_CheckExecutionState(args);
9602 if (check_result->IsFailure()) return check_result;
9603 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009604 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
9605
9606 // Handle the processing of break.
9607 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009608
9609 // Enter the top context from before the debugger was invoked.
9610 SaveContext save;
9611 SaveContext* top = &save;
9612 while (top != NULL && *top->context() == *Debug::debug_context()) {
9613 top = top->prev();
9614 }
9615 if (top != NULL) {
9616 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009617 }
9618
9619 // Get the global context now set to the top context from before the
9620 // debugger was invoked.
9621 Handle<Context> context = Top::global_context();
9622
9623 // Compile the source to be evaluated.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009624 Handle<SharedFunctionInfo> shared =
9625 Compiler::CompileEval(source,
9626 context,
9627 true,
9628 Compiler::DONT_VALIDATE_JSON);
9629 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009630 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009631 Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
9632 context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009633
9634 // Invoke the result of the compilation to get the evaluation function.
9635 bool has_pending_exception;
9636 Handle<Object> receiver = Top::global();
9637 Handle<Object> result =
9638 Execution::Call(compiled_function, receiver, 0, NULL,
9639 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009640 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009641 return *result;
9642}
9643
9644
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009645static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
9646 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009647 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009648
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009649 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009650 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009651
9652 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009653 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00009654 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
9655 // Get the script wrapper in a local handle before calling GetScriptWrapper,
9656 // because using
9657 // instances->set(i, *GetScriptWrapper(script))
9658 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
9659 // already have deferenced the instances handle.
9660 Handle<JSValue> wrapper = GetScriptWrapper(script);
9661 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009662 }
9663
9664 // Return result as a JS array.
9665 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
9666 Handle<JSArray>::cast(result)->SetContent(*instances);
9667 return *result;
9668}
9669
9670
9671// Helper function used by Runtime_DebugReferencedBy below.
9672static int DebugReferencedBy(JSObject* target,
9673 Object* instance_filter, int max_references,
9674 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009675 JSFunction* arguments_function) {
9676 NoHandleAllocation ha;
9677 AssertNoAllocation no_alloc;
9678
9679 // Iterate the heap.
9680 int count = 0;
9681 JSObject* last = NULL;
9682 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009683 HeapObject* heap_obj = NULL;
9684 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009685 (max_references == 0 || count < max_references)) {
9686 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009687 if (heap_obj->IsJSObject()) {
9688 // Skip context extension objects and argument arrays as these are
9689 // checked in the context of functions using them.
9690 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00009691 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009692 obj->map()->constructor() == arguments_function) {
9693 continue;
9694 }
9695
9696 // Check if the JS object has a reference to the object looked for.
9697 if (obj->ReferencesObject(target)) {
9698 // Check instance filter if supplied. This is normally used to avoid
9699 // references from mirror objects (see Runtime_IsInPrototypeChain).
9700 if (!instance_filter->IsUndefined()) {
9701 Object* V = obj;
9702 while (true) {
9703 Object* prototype = V->GetPrototype();
9704 if (prototype->IsNull()) {
9705 break;
9706 }
9707 if (instance_filter == prototype) {
9708 obj = NULL; // Don't add this object.
9709 break;
9710 }
9711 V = prototype;
9712 }
9713 }
9714
9715 if (obj != NULL) {
9716 // Valid reference found add to instance array if supplied an update
9717 // count.
9718 if (instances != NULL && count < instances_size) {
9719 instances->set(count, obj);
9720 }
9721 last = obj;
9722 count++;
9723 }
9724 }
9725 }
9726 }
9727
9728 // Check for circular reference only. This can happen when the object is only
9729 // referenced from mirrors and has a circular reference in which case the
9730 // object is not really alive and would have been garbage collected if not
9731 // referenced from the mirror.
9732 if (count == 1 && last == target) {
9733 count = 0;
9734 }
9735
9736 // Return the number of referencing objects found.
9737 return count;
9738}
9739
9740
9741// Scan the heap for objects with direct references to an object
9742// args[0]: the object to find references to
9743// args[1]: constructor function for instances to exclude (Mirror)
9744// args[2]: the the maximum number of objects to return
9745static Object* Runtime_DebugReferencedBy(Arguments args) {
9746 ASSERT(args.length() == 3);
9747
9748 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009749 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009750
9751 // Check parameters.
9752 CONVERT_CHECKED(JSObject, target, args[0]);
9753 Object* instance_filter = args[1];
9754 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
9755 instance_filter->IsJSObject());
9756 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
9757 RUNTIME_ASSERT(max_references >= 0);
9758
9759 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009760 JSObject* arguments_boilerplate =
9761 Top::context()->global_context()->arguments_boilerplate();
9762 JSFunction* arguments_function =
9763 JSFunction::cast(arguments_boilerplate->map()->constructor());
9764
9765 // Get the number of referencing objects.
9766 int count;
9767 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009768 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009769
9770 // Allocate an array to hold the result.
9771 Object* object = Heap::AllocateFixedArray(count);
9772 if (object->IsFailure()) return object;
9773 FixedArray* instances = FixedArray::cast(object);
9774
9775 // Fill the referencing objects.
9776 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009777 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009778
9779 // Return result as JS array.
9780 Object* result =
9781 Heap::AllocateJSObject(
9782 Top::context()->global_context()->array_function());
9783 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9784 return result;
9785}
9786
9787
9788// Helper function used by Runtime_DebugConstructedBy below.
9789static int DebugConstructedBy(JSFunction* constructor, int max_references,
9790 FixedArray* instances, int instances_size) {
9791 AssertNoAllocation no_alloc;
9792
9793 // Iterate the heap.
9794 int count = 0;
9795 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009796 HeapObject* heap_obj = NULL;
9797 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009798 (max_references == 0 || count < max_references)) {
9799 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009800 if (heap_obj->IsJSObject()) {
9801 JSObject* obj = JSObject::cast(heap_obj);
9802 if (obj->map()->constructor() == constructor) {
9803 // Valid reference found add to instance array if supplied an update
9804 // count.
9805 if (instances != NULL && count < instances_size) {
9806 instances->set(count, obj);
9807 }
9808 count++;
9809 }
9810 }
9811 }
9812
9813 // Return the number of referencing objects found.
9814 return count;
9815}
9816
9817
9818// Scan the heap for objects constructed by a specific function.
9819// args[0]: the constructor to find instances of
9820// args[1]: the the maximum number of objects to return
9821static Object* Runtime_DebugConstructedBy(Arguments args) {
9822 ASSERT(args.length() == 2);
9823
9824 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009825 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009826
9827 // Check parameters.
9828 CONVERT_CHECKED(JSFunction, constructor, args[0]);
9829 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
9830 RUNTIME_ASSERT(max_references >= 0);
9831
9832 // Get the number of referencing objects.
9833 int count;
9834 count = DebugConstructedBy(constructor, max_references, NULL, 0);
9835
9836 // Allocate an array to hold the result.
9837 Object* object = Heap::AllocateFixedArray(count);
9838 if (object->IsFailure()) return object;
9839 FixedArray* instances = FixedArray::cast(object);
9840
9841 // Fill the referencing objects.
9842 count = DebugConstructedBy(constructor, max_references, instances, count);
9843
9844 // Return result as JS array.
9845 Object* result =
9846 Heap::AllocateJSObject(
9847 Top::context()->global_context()->array_function());
9848 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9849 return result;
9850}
9851
9852
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009853// Find the effective prototype object as returned by __proto__.
9854// args[0]: the object to find the prototype for.
9855static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009856 ASSERT(args.length() == 1);
9857
9858 CONVERT_CHECKED(JSObject, obj, args[0]);
9859
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009860 // Use the __proto__ accessor.
9861 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009862}
9863
9864
9865static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009866 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009867 CPU::DebugBreak();
9868 return Heap::undefined_value();
9869}
9870
9871
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009872static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009873#ifdef DEBUG
9874 HandleScope scope;
9875 ASSERT(args.length() == 1);
9876 // Get the function and make sure it is compiled.
9877 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009878 Handle<SharedFunctionInfo> shared(func->shared());
9879 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009880 return Failure::Exception();
9881 }
9882 func->code()->PrintLn();
9883#endif // DEBUG
9884 return Heap::undefined_value();
9885}
ager@chromium.org9085a012009-05-11 19:22:57 +00009886
9887
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009888static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
9889#ifdef DEBUG
9890 HandleScope scope;
9891 ASSERT(args.length() == 1);
9892 // Get the function and make sure it is compiled.
9893 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009894 Handle<SharedFunctionInfo> shared(func->shared());
9895 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009896 return Failure::Exception();
9897 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009898 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009899#endif // DEBUG
9900 return Heap::undefined_value();
9901}
9902
9903
ager@chromium.org9085a012009-05-11 19:22:57 +00009904static Object* Runtime_FunctionGetInferredName(Arguments args) {
9905 NoHandleAllocation ha;
9906 ASSERT(args.length() == 1);
9907
9908 CONVERT_CHECKED(JSFunction, f, args[0]);
9909 return f->shared()->inferred_name();
9910}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009911
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009912
9913static int FindSharedFunctionInfosForScript(Script* script,
9914 FixedArray* buffer) {
9915 AssertNoAllocation no_allocations;
9916
9917 int counter = 0;
9918 int buffer_size = buffer->length();
9919 HeapIterator iterator;
9920 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
9921 ASSERT(obj != NULL);
9922 if (!obj->IsSharedFunctionInfo()) {
9923 continue;
9924 }
9925 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
9926 if (shared->script() != script) {
9927 continue;
9928 }
9929 if (counter < buffer_size) {
9930 buffer->set(counter, shared);
9931 }
9932 counter++;
9933 }
9934 return counter;
9935}
9936
9937// For a script finds all SharedFunctionInfo's in the heap that points
9938// to this script. Returns JSArray of SharedFunctionInfo wrapped
9939// in OpaqueReferences.
9940static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
9941 Arguments args) {
9942 ASSERT(args.length() == 1);
9943 HandleScope scope;
9944 CONVERT_CHECKED(JSValue, script_value, args[0]);
9945
9946 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
9947
9948 const int kBufferSize = 32;
9949
9950 Handle<FixedArray> array;
9951 array = Factory::NewFixedArray(kBufferSize);
9952 int number = FindSharedFunctionInfosForScript(*script, *array);
9953 if (number > kBufferSize) {
9954 array = Factory::NewFixedArray(number);
9955 FindSharedFunctionInfosForScript(*script, *array);
9956 }
9957
9958 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
9959 result->set_length(Smi::FromInt(number));
9960
9961 LiveEdit::WrapSharedFunctionInfos(result);
9962
9963 return *result;
9964}
9965
9966// For a script calculates compilation information about all its functions.
9967// The script source is explicitly specified by the second argument.
9968// The source of the actual script is not used, however it is important that
9969// all generated code keeps references to this particular instance of script.
9970// Returns a JSArray of compilation infos. The array is ordered so that
9971// each function with all its descendant is always stored in a continues range
9972// with the function itself going first. The root function is a script function.
9973static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
9974 ASSERT(args.length() == 2);
9975 HandleScope scope;
9976 CONVERT_CHECKED(JSValue, script, args[0]);
9977 CONVERT_ARG_CHECKED(String, source, 1);
9978 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
9979
9980 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
9981
9982 if (Top::has_pending_exception()) {
9983 return Failure::Exception();
9984 }
9985
9986 return result;
9987}
9988
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009989// Changes the source of the script to a new_source.
9990// If old_script_name is provided (i.e. is a String), also creates a copy of
9991// the script with its original source and sends notification to debugger.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009992static Object* Runtime_LiveEditReplaceScript(Arguments args) {
9993 ASSERT(args.length() == 3);
9994 HandleScope scope;
9995 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
9996 CONVERT_ARG_CHECKED(String, new_source, 1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009997 Handle<Object> old_script_name(args[2]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009998
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009999 CONVERT_CHECKED(Script, original_script_pointer,
10000 original_script_value->value());
10001 Handle<Script> original_script(original_script_pointer);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010002
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010003 Object* old_script = LiveEdit::ChangeScriptSource(original_script,
10004 new_source,
10005 old_script_name);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010006
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010007 if (old_script->IsScript()) {
10008 Handle<Script> script_handle(Script::cast(old_script));
10009 return *(GetScriptWrapper(script_handle));
10010 } else {
10011 return Heap::null_value();
10012 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010013}
10014
10015// Replaces code of SharedFunctionInfo with a new one.
10016static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
10017 ASSERT(args.length() == 2);
10018 HandleScope scope;
10019 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
10020 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
10021
ager@chromium.orgac091b72010-05-05 07:34:42 +000010022 return LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010023}
10024
10025// Connects SharedFunctionInfo to another script.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010026static Object* Runtime_LiveEditFunctionSetScript(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010027 ASSERT(args.length() == 2);
10028 HandleScope scope;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010029 Handle<Object> function_object(args[0]);
10030 Handle<Object> script_object(args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010031
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010032 if (function_object->IsJSValue()) {
10033 Handle<JSValue> function_wrapper = Handle<JSValue>::cast(function_object);
10034 if (script_object->IsJSValue()) {
10035 CONVERT_CHECKED(Script, script, JSValue::cast(*script_object)->value());
10036 script_object = Handle<Object>(script);
10037 }
10038
10039 LiveEdit::SetFunctionScript(function_wrapper, script_object);
10040 } else {
10041 // Just ignore this. We may not have a SharedFunctionInfo for some functions
10042 // and we check it in this function.
10043 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010044
10045 return Heap::undefined_value();
10046}
10047
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010048
10049// In a code of a parent function replaces original function as embedded object
10050// with a substitution one.
10051static Object* Runtime_LiveEditReplaceRefToNestedFunction(Arguments args) {
10052 ASSERT(args.length() == 3);
10053 HandleScope scope;
10054
10055 CONVERT_ARG_CHECKED(JSValue, parent_wrapper, 0);
10056 CONVERT_ARG_CHECKED(JSValue, orig_wrapper, 1);
10057 CONVERT_ARG_CHECKED(JSValue, subst_wrapper, 2);
10058
10059 LiveEdit::ReplaceRefToNestedFunction(parent_wrapper, orig_wrapper,
10060 subst_wrapper);
10061
10062 return Heap::undefined_value();
10063}
10064
10065
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010066// Updates positions of a shared function info (first parameter) according
10067// to script source change. Text change is described in second parameter as
10068// array of groups of 3 numbers:
10069// (change_begin, change_end, change_end_new_position).
10070// Each group describes a change in text; groups are sorted by change_begin.
10071static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
10072 ASSERT(args.length() == 2);
10073 HandleScope scope;
10074 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
10075 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
10076
ager@chromium.orgac091b72010-05-05 07:34:42 +000010077 return LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010078}
10079
10080
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010081// For array of SharedFunctionInfo's (each wrapped in JSValue)
10082// checks that none of them have activations on stacks (of any thread).
10083// Returns array of the same length with corresponding results of
10084// LiveEdit::FunctionPatchabilityStatus type.
ager@chromium.org357bf652010-04-12 11:30:10 +000010085static Object* Runtime_LiveEditCheckAndDropActivations(Arguments args) {
10086 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010087 HandleScope scope;
10088 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +000010089 CONVERT_BOOLEAN_CHECKED(do_drop, args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010090
ager@chromium.org357bf652010-04-12 11:30:10 +000010091 return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010092}
10093
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010094// Compares 2 strings line-by-line and returns diff in form of JSArray of
fschneider@chromium.org013f3e12010-04-26 13:27:52 +000010095// triplets (pos1, pos1_end, pos2_end) describing list of diff chunks.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010096static Object* Runtime_LiveEditCompareStringsLinewise(Arguments args) {
10097 ASSERT(args.length() == 2);
10098 HandleScope scope;
10099 CONVERT_ARG_CHECKED(String, s1, 0);
10100 CONVERT_ARG_CHECKED(String, s2, 1);
10101
10102 return *LiveEdit::CompareStringsLinewise(s1, s2);
10103}
10104
10105
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010106
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010107// A testing entry. Returns statement position which is the closest to
10108// source_position.
10109static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
10110 ASSERT(args.length() == 2);
10111 HandleScope scope;
10112 CONVERT_ARG_CHECKED(JSFunction, function, 0);
10113 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
10114
10115 Handle<Code> code(function->code());
10116
10117 RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
10118 int closest_pc = 0;
10119 int distance = kMaxInt;
10120 while (!it.done()) {
10121 int statement_position = static_cast<int>(it.rinfo()->data());
10122 // Check if this break point is closer that what was previously found.
10123 if (source_position <= statement_position &&
10124 statement_position - source_position < distance) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +000010125 closest_pc =
10126 static_cast<int>(it.rinfo()->pc() - code->instruction_start());
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010127 distance = statement_position - source_position;
10128 // Check whether we can't get any closer.
10129 if (distance == 0) break;
10130 }
10131 it.next();
10132 }
10133
10134 return Smi::FromInt(closest_pc);
10135}
10136
10137
ager@chromium.org357bf652010-04-12 11:30:10 +000010138// Calls specified function with or without entering the debugger.
10139// This is used in unit tests to run code as if debugger is entered or simply
10140// to have a stack with C++ frame in the middle.
10141static Object* Runtime_ExecuteInDebugContext(Arguments args) {
10142 ASSERT(args.length() == 2);
10143 HandleScope scope;
10144 CONVERT_ARG_CHECKED(JSFunction, function, 0);
10145 CONVERT_BOOLEAN_CHECKED(without_debugger, args[1]);
10146
10147 Handle<Object> result;
10148 bool pending_exception;
10149 {
10150 if (without_debugger) {
10151 result = Execution::Call(function, Top::global(), 0, NULL,
10152 &pending_exception);
10153 } else {
10154 EnterDebugger enter_debugger;
10155 result = Execution::Call(function, Top::global(), 0, NULL,
10156 &pending_exception);
10157 }
10158 }
10159 if (!pending_exception) {
10160 return *result;
10161 } else {
10162 return Failure::Exception();
10163 }
10164}
10165
10166
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010167#endif // ENABLE_DEBUGGER_SUPPORT
10168
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010169#ifdef ENABLE_LOGGING_AND_PROFILING
10170
10171static Object* Runtime_ProfilerResume(Arguments args) {
10172 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010173 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010174
10175 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010176 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10177 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010178 return Heap::undefined_value();
10179}
10180
10181
10182static Object* Runtime_ProfilerPause(Arguments args) {
10183 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010184 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010185
10186 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010187 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10188 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010189 return Heap::undefined_value();
10190}
10191
10192#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010193
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010194// Finds the script object from the script data. NOTE: This operation uses
10195// heap traversal to find the function generated for the source position
10196// for the requested break point. For lazily compiled functions several heap
10197// traversals might be required rendering this operation as a rather slow
10198// operation. However for setting break points which is normally done through
10199// some kind of user interaction the performance is not crucial.
10200static Handle<Object> Runtime_GetScriptFromScriptName(
10201 Handle<String> script_name) {
10202 // Scan the heap for Script objects to find the script with the requested
10203 // script data.
10204 Handle<Script> script;
10205 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010206 HeapObject* obj = NULL;
10207 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010208 // If a script is found check if it has the script data requested.
10209 if (obj->IsScript()) {
10210 if (Script::cast(obj)->name()->IsString()) {
10211 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
10212 script = Handle<Script>(Script::cast(obj));
10213 }
10214 }
10215 }
10216 }
10217
10218 // If no script with the requested script data is found return undefined.
10219 if (script.is_null()) return Factory::undefined_value();
10220
10221 // Return the script found.
10222 return GetScriptWrapper(script);
10223}
10224
10225
10226// Get the script object from script data. NOTE: Regarding performance
10227// see the NOTE for GetScriptFromScriptData.
10228// args[0]: script data for the script to find the source for
10229static Object* Runtime_GetScript(Arguments args) {
10230 HandleScope scope;
10231
10232 ASSERT(args.length() == 1);
10233
10234 CONVERT_CHECKED(String, script_name, args[0]);
10235
10236 // Find the requested script.
10237 Handle<Object> result =
10238 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
10239 return *result;
10240}
10241
10242
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010243// Determines whether the given stack frame should be displayed in
10244// a stack trace. The caller is the error constructor that asked
10245// for the stack trace to be collected. The first time a construct
10246// call to this function is encountered it is skipped. The seen_caller
10247// in/out parameter is used to remember if the caller has been seen
10248// yet.
10249static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
10250 bool* seen_caller) {
10251 // Only display JS frames.
10252 if (!raw_frame->is_java_script())
10253 return false;
10254 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
10255 Object* raw_fun = frame->function();
10256 // Not sure when this can happen but skip it just in case.
10257 if (!raw_fun->IsJSFunction())
10258 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010259 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010260 *seen_caller = true;
10261 return false;
10262 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010263 // Skip all frames until we've seen the caller. Also, skip the most
10264 // obvious builtin calls. Some builtin calls (such as Number.ADD
10265 // which is invoked using 'call') are very difficult to recognize
10266 // so we're leaving them in for now.
10267 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010268}
10269
10270
10271// Collect the raw data for a stack trace. Returns an array of three
10272// element segments each containing a receiver, function and native
10273// code offset.
10274static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010275 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010276 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010277 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
10278
10279 HandleScope scope;
10280
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000010281 limit = Max(limit, 0); // Ensure that limit is not negative.
10282 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010283 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010284
10285 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010286 // If the caller parameter is a function we skip frames until we're
10287 // under it before starting to collect.
10288 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010289 int cursor = 0;
10290 int frames_seen = 0;
10291 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010292 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010293 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010294 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010295 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010296 Object* recv = frame->receiver();
10297 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010298 Address pc = frame->pc();
10299 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010300 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010301 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010302 if (cursor + 2 < elements->length()) {
10303 elements->set(cursor++, recv);
10304 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010305 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010306 } else {
10307 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010308 Handle<Object> recv_handle(recv);
10309 Handle<Object> fun_handle(fun);
10310 SetElement(result, cursor++, recv_handle);
10311 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010312 SetElement(result, cursor++, Handle<Smi>(offset));
10313 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010314 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010315 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010316 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010317
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010318 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010319 return *result;
10320}
10321
10322
ager@chromium.org3811b432009-10-28 14:53:37 +000010323// Returns V8 version as a string.
10324static Object* Runtime_GetV8Version(Arguments args) {
10325 ASSERT_EQ(args.length(), 0);
10326
10327 NoHandleAllocation ha;
10328
10329 const char* version_string = v8::V8::GetVersion();
10330
10331 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
10332}
10333
10334
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010335static Object* Runtime_Abort(Arguments args) {
10336 ASSERT(args.length() == 2);
10337 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
10338 Smi::cast(args[1])->value());
10339 Top::PrintStack();
10340 OS::Abort();
10341 UNREACHABLE();
10342 return NULL;
10343}
10344
10345
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010346static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
10347 ASSERT(args.length() == 0);
10348 HandleScope::DeleteExtensions();
10349 return Heap::undefined_value();
10350}
10351
10352
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010353static Object* CacheMiss(FixedArray* cache_obj, int index, Object* key_obj) {
10354 ASSERT(index % 2 == 0); // index of the key
10355 ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
10356 ASSERT(index < cache_obj->length());
10357
10358 HandleScope scope;
10359
10360 Handle<FixedArray> cache(cache_obj);
10361 Handle<Object> key(key_obj);
10362 Handle<JSFunction> factory(JSFunction::cast(
10363 cache->get(JSFunctionResultCache::kFactoryIndex)));
10364 // TODO(antonm): consider passing a receiver when constructing a cache.
10365 Handle<Object> receiver(Top::global_context()->global());
10366
10367 Handle<Object> value;
10368 {
10369 // This handle is nor shared, nor used later, so it's safe.
10370 Object** argv[] = { key.location() };
10371 bool pending_exception = false;
10372 value = Execution::Call(factory,
10373 receiver,
10374 1,
10375 argv,
10376 &pending_exception);
10377 if (pending_exception) return Failure::Exception();
10378 }
10379
10380 cache->set(index, *key);
10381 cache->set(index + 1, *value);
10382 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(index));
10383
10384 return *value;
10385}
10386
10387
10388static Object* Runtime_GetFromCache(Arguments args) {
10389 // This is only called from codegen, so checks might be more lax.
10390 CONVERT_CHECKED(FixedArray, cache, args[0]);
10391 Object* key = args[1];
10392
10393 const int finger_index =
10394 Smi::cast(cache->get(JSFunctionResultCache::kFingerIndex))->value();
10395
10396 Object* o = cache->get(finger_index);
10397 if (o == key) {
10398 // The fastest case: hit the same place again.
10399 return cache->get(finger_index + 1);
10400 }
10401
10402 for (int i = finger_index - 2;
10403 i >= JSFunctionResultCache::kEntriesIndex;
10404 i -= 2) {
10405 o = cache->get(i);
10406 if (o == key) {
10407 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10408 return cache->get(i + 1);
10409 }
10410 }
10411
10412 const int size =
10413 Smi::cast(cache->get(JSFunctionResultCache::kCacheSizeIndex))->value();
10414 ASSERT(size <= cache->length());
10415
10416 for (int i = size - 2; i > finger_index; i -= 2) {
10417 o = cache->get(i);
10418 if (o == key) {
10419 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10420 return cache->get(i + 1);
10421 }
10422 }
10423
10424 // Cache miss. If we have spare room, put new data into it, otherwise
10425 // evict post finger entry which must be least recently used.
10426 if (size < cache->length()) {
10427 cache->set(JSFunctionResultCache::kCacheSizeIndex, Smi::FromInt(size + 2));
10428 return CacheMiss(cache, size, key);
10429 } else {
antonm@chromium.org397e23c2010-04-21 12:00:05 +000010430 int target_index = finger_index + JSFunctionResultCache::kEntrySize;
10431 if (target_index == cache->length()) {
10432 target_index = JSFunctionResultCache::kEntriesIndex;
10433 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010434 return CacheMiss(cache, target_index, key);
10435 }
10436}
10437
kasper.lund44510672008-07-25 07:37:58 +000010438#ifdef DEBUG
10439// ListNatives is ONLY used by the fuzz-natives.js in debug mode
10440// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010441static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +000010442 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010443 HandleScope scope;
10444 Handle<JSArray> result = Factory::NewJSArray(0);
10445 int index = 0;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010446 bool inline_runtime_functions = false;
ager@chromium.orga1645e22009-09-09 19:27:10 +000010447#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010448 { \
10449 HandleScope inner; \
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010450 Handle<String> name; \
10451 /* Inline runtime functions have an underscore in front of the name. */ \
10452 if (inline_runtime_functions) { \
10453 name = Factory::NewStringFromAscii( \
10454 Vector<const char>("_" #Name, StrLength("_" #Name))); \
10455 } else { \
10456 name = Factory::NewStringFromAscii( \
10457 Vector<const char>(#Name, StrLength(#Name))); \
10458 } \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010459 Handle<JSArray> pair = Factory::NewJSArray(0); \
10460 SetElement(pair, 0, name); \
10461 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
10462 SetElement(result, index++, pair); \
10463 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010464 inline_runtime_functions = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010465 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010466 inline_runtime_functions = true;
10467 INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010468#undef ADD_ENTRY
10469 return *result;
10470}
kasper.lund44510672008-07-25 07:37:58 +000010471#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010472
10473
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010474static Object* Runtime_Log(Arguments args) {
10475 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +000010476 CONVERT_CHECKED(String, format, args[0]);
10477 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010478 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010479 Logger::LogRuntime(chars, elms);
10480 return Heap::undefined_value();
10481}
10482
10483
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010484static Object* Runtime_IS_VAR(Arguments args) {
10485 UNREACHABLE(); // implemented as macro in the parser
10486 return NULL;
10487}
10488
10489
10490// ----------------------------------------------------------------------------
10491// Implementation of Runtime
10492
ager@chromium.orga1645e22009-09-09 19:27:10 +000010493#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010494 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +000010495 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010496
10497static Runtime::Function Runtime_functions[] = {
10498 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010499 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010500};
10501
10502#undef F
10503
10504
10505Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
10506 ASSERT(0 <= fid && fid < kNofFunctions);
10507 return &Runtime_functions[fid];
10508}
10509
10510
ricow@chromium.org65fae842010-08-25 15:26:24 +000010511Runtime::Function* Runtime::FunctionForName(Vector<const char> name) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010512 for (Function* f = Runtime_functions; f->name != NULL; f++) {
ricow@chromium.org65fae842010-08-25 15:26:24 +000010513 if (strncmp(f->name, name.start(), name.length()) == 0
10514 && f->name[name.length()] == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010515 return f;
10516 }
10517 }
10518 return NULL;
10519}
10520
10521
10522void Runtime::PerformGC(Object* result) {
10523 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010524 if (failure->IsRetryAfterGC()) {
10525 // Try to do a garbage collection; ignore it if it fails. The C
10526 // entry stub will throw an out-of-memory exception in that case.
10527 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
10528 } else {
10529 // Handle last resort GC and make sure to allow future allocations
10530 // to grow the heap without causing GCs (if possible).
10531 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +000010532 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010533 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010534}
10535
10536
10537} } // namespace v8::internal