blob: c7ec6bfb0dcaea6839434b5e0df79853d3ef31c3 [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.
4680 uint32_t hash = StringHasher::MakeCachedArrayIndex(d, len);
4681#ifdef DEBUG
4682 ASSERT((hash & String::kContainsCachedArrayIndexMask) == 0);
4683 subject->Hash(); // Force hash calculation.
4684 ASSERT_EQ(static_cast<int>(subject->hash_field()),
4685 static_cast<int>(hash));
4686#endif
4687 subject->set_hash_field(hash);
lrn@chromium.org25156de2010-04-06 13:10:27 +00004688 }
4689 return Smi::FromInt(d);
4690 }
4691 }
4692
4693 // Slower case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004694 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
4695}
4696
4697
4698static Object* Runtime_StringFromCharCodeArray(Arguments args) {
4699 NoHandleAllocation ha;
4700 ASSERT(args.length() == 1);
4701
4702 CONVERT_CHECKED(JSArray, codes, args[0]);
4703 int length = Smi::cast(codes->length())->value();
4704
4705 // Check if the string can be ASCII.
4706 int i;
4707 for (i = 0; i < length; i++) {
4708 Object* element = codes->GetElement(i);
4709 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
4710 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
4711 break;
4712 }
4713
4714 Object* object = NULL;
4715 if (i == length) { // The string is ASCII.
4716 object = Heap::AllocateRawAsciiString(length);
4717 } else { // The string is not ASCII.
4718 object = Heap::AllocateRawTwoByteString(length);
4719 }
4720
4721 if (object->IsFailure()) return object;
4722 String* result = String::cast(object);
4723 for (int i = 0; i < length; i++) {
4724 Object* element = codes->GetElement(i);
4725 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004726 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004727 }
4728 return result;
4729}
4730
4731
4732// kNotEscaped is generated by the following:
4733//
4734// #!/bin/perl
4735// for (my $i = 0; $i < 256; $i++) {
4736// print "\n" if $i % 16 == 0;
4737// my $c = chr($i);
4738// my $escaped = 1;
4739// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
4740// print $escaped ? "0, " : "1, ";
4741// }
4742
4743
4744static bool IsNotEscaped(uint16_t character) {
4745 // Only for 8 bit characters, the rest are always escaped (in a different way)
4746 ASSERT(character < 256);
4747 static const char kNotEscaped[256] = {
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, 0, 0, 0, 0, 0, 0,
4750 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
4751 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
4752 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4753 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
4754 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4755 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4764 };
4765 return kNotEscaped[character] != 0;
4766}
4767
4768
4769static Object* Runtime_URIEscape(Arguments args) {
4770 const char hex_chars[] = "0123456789ABCDEF";
4771 NoHandleAllocation ha;
4772 ASSERT(args.length() == 1);
4773 CONVERT_CHECKED(String, source, args[0]);
4774
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004775 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004776
4777 int escaped_length = 0;
4778 int length = source->length();
4779 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004780 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004781 buffer->Reset(source);
4782 while (buffer->has_more()) {
4783 uint16_t character = buffer->GetNext();
4784 if (character >= 256) {
4785 escaped_length += 6;
4786 } else if (IsNotEscaped(character)) {
4787 escaped_length++;
4788 } else {
4789 escaped_length += 3;
4790 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004791 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004792 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004793 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004794 Top::context()->mark_out_of_memory();
4795 return Failure::OutOfMemoryException();
4796 }
4797 }
4798 }
4799 // No length change implies no change. Return original string if no change.
4800 if (escaped_length == length) {
4801 return source;
4802 }
4803 Object* o = Heap::AllocateRawAsciiString(escaped_length);
4804 if (o->IsFailure()) return o;
4805 String* destination = String::cast(o);
4806 int dest_position = 0;
4807
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004808 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004809 buffer->Rewind();
4810 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00004811 uint16_t chr = buffer->GetNext();
4812 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004813 destination->Set(dest_position, '%');
4814 destination->Set(dest_position+1, 'u');
4815 destination->Set(dest_position+2, hex_chars[chr >> 12]);
4816 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
4817 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
4818 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004819 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00004820 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004821 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004822 dest_position++;
4823 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004824 destination->Set(dest_position, '%');
4825 destination->Set(dest_position+1, hex_chars[chr >> 4]);
4826 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004827 dest_position += 3;
4828 }
4829 }
4830 return destination;
4831}
4832
4833
4834static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
4835 static const signed char kHexValue['g'] = {
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 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4839 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
4840 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4841 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4842 -1, 10, 11, 12, 13, 14, 15 };
4843
4844 if (character1 > 'f') return -1;
4845 int hi = kHexValue[character1];
4846 if (hi == -1) return -1;
4847 if (character2 > 'f') return -1;
4848 int lo = kHexValue[character2];
4849 if (lo == -1) return -1;
4850 return (hi << 4) + lo;
4851}
4852
4853
ager@chromium.org870a0b62008-11-04 11:43:05 +00004854static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004855 int i,
4856 int length,
4857 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004858 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00004859 int32_t hi = 0;
4860 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004861 if (character == '%' &&
4862 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004863 source->Get(i + 1) == 'u' &&
4864 (hi = TwoDigitHex(source->Get(i + 2),
4865 source->Get(i + 3))) != -1 &&
4866 (lo = TwoDigitHex(source->Get(i + 4),
4867 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004868 *step = 6;
4869 return (hi << 8) + lo;
4870 } else if (character == '%' &&
4871 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004872 (lo = TwoDigitHex(source->Get(i + 1),
4873 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004874 *step = 3;
4875 return lo;
4876 } else {
4877 *step = 1;
4878 return character;
4879 }
4880}
4881
4882
4883static Object* Runtime_URIUnescape(Arguments args) {
4884 NoHandleAllocation ha;
4885 ASSERT(args.length() == 1);
4886 CONVERT_CHECKED(String, source, args[0]);
4887
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004888 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004889
4890 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004891 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004892
4893 int unescaped_length = 0;
4894 for (int i = 0; i < length; unescaped_length++) {
4895 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004896 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004897 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004898 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004899 i += step;
4900 }
4901
4902 // No length change implies no change. Return original string if no change.
4903 if (unescaped_length == length)
4904 return source;
4905
4906 Object* o = ascii ?
4907 Heap::AllocateRawAsciiString(unescaped_length) :
4908 Heap::AllocateRawTwoByteString(unescaped_length);
4909 if (o->IsFailure()) return o;
4910 String* destination = String::cast(o);
4911
4912 int dest_position = 0;
4913 for (int i = 0; i < length; dest_position++) {
4914 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004915 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004916 i += step;
4917 }
4918 return destination;
4919}
4920
4921
4922static Object* Runtime_StringParseInt(Arguments args) {
4923 NoHandleAllocation ha;
4924
4925 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004926 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004927
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004928 s->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004929
lrn@chromium.org25156de2010-04-06 13:10:27 +00004930 RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
4931 double value = StringToInt(s, radix);
4932 return Heap::NumberFromDouble(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004933 return Heap::nan_value();
4934}
4935
4936
4937static Object* Runtime_StringParseFloat(Arguments args) {
4938 NoHandleAllocation ha;
4939 CONVERT_CHECKED(String, str, args[0]);
4940
4941 // ECMA-262 section 15.1.2.3, empty string is NaN
4942 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
4943
4944 // Create a number object from the value.
4945 return Heap::NumberFromDouble(value);
4946}
4947
4948
4949static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
4950static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
4951
4952
4953template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004954static Object* ConvertCaseHelper(String* s,
4955 int length,
4956 int input_string_length,
4957 unibrow::Mapping<Converter, 128>* mapping) {
4958 // We try this twice, once with the assumption that the result is no longer
4959 // than the input and, if that assumption breaks, again with the exact
4960 // length. This may not be pretty, but it is nicer than what was here before
4961 // and I hereby claim my vaffel-is.
4962 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004963 // Allocate the resulting string.
4964 //
4965 // NOTE: This assumes that the upper/lower case of an ascii
4966 // character is also ascii. This is currently the case, but it
4967 // might break in the future if we implement more context and locale
4968 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00004969 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004970 ? Heap::AllocateRawAsciiString(length)
4971 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004972 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004973 String* result = String::cast(o);
4974 bool has_changed_character = false;
4975
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004976 // Convert all characters to upper case, assuming that they will fit
4977 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004978 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004979 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004980 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004981 // We can assume that the string is not empty
4982 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004983 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00004984 bool has_next = buffer->has_more();
4985 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004986 int char_length = mapping->get(current, next, chars);
4987 if (char_length == 0) {
4988 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004989 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004990 i++;
4991 } else if (char_length == 1) {
4992 // Common case: converting the letter resulted in one character.
4993 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004994 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004995 has_changed_character = true;
4996 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004997 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004998 // We've assumed that the result would be as long as the
4999 // input but here is a character that converts to several
5000 // characters. No matter, we calculate the exact length
5001 // of the result and try the whole thing again.
5002 //
5003 // Note that this leaves room for optimization. We could just
5004 // memcpy what we already have to the result string. Also,
5005 // the result string is the last object allocated we could
5006 // "realloc" it and probably, in the vast majority of cases,
5007 // extend the existing string to be able to hold the full
5008 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00005009 int next_length = 0;
5010 if (has_next) {
5011 next_length = mapping->get(next, 0, chars);
5012 if (next_length == 0) next_length = 1;
5013 }
5014 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005015 while (buffer->has_more()) {
5016 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00005017 // NOTE: we use 0 as the next character here because, while
5018 // the next character may affect what a character converts to,
5019 // it does not in any case affect the length of what it convert
5020 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005021 int char_length = mapping->get(current, 0, chars);
5022 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00005023 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005024 if (current_length > Smi::kMaxValue) {
5025 Top::context()->mark_out_of_memory();
5026 return Failure::OutOfMemoryException();
5027 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005028 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005029 // Try again with the real length.
5030 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005031 } else {
5032 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005033 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005034 i++;
5035 }
5036 has_changed_character = true;
5037 }
5038 current = next;
5039 }
5040 if (has_changed_character) {
5041 return result;
5042 } else {
5043 // If we didn't actually change anything in doing the conversion
5044 // we simple return the result and let the converted string
5045 // become garbage; there is no reason to keep two identical strings
5046 // alive.
5047 return s;
5048 }
5049}
5050
5051
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005052namespace {
5053
5054struct ToLowerTraits {
5055 typedef unibrow::ToLowercase UnibrowConverter;
5056
5057 static bool ConvertAscii(char* dst, char* src, int length) {
5058 bool changed = false;
5059 for (int i = 0; i < length; ++i) {
5060 char c = src[i];
5061 if ('A' <= c && c <= 'Z') {
5062 c += ('a' - 'A');
5063 changed = true;
5064 }
5065 dst[i] = c;
5066 }
5067 return changed;
5068 }
5069};
5070
5071
5072struct ToUpperTraits {
5073 typedef unibrow::ToUppercase UnibrowConverter;
5074
5075 static bool ConvertAscii(char* dst, char* src, int length) {
5076 bool changed = false;
5077 for (int i = 0; i < length; ++i) {
5078 char c = src[i];
5079 if ('a' <= c && c <= 'z') {
5080 c -= ('a' - 'A');
5081 changed = true;
5082 }
5083 dst[i] = c;
5084 }
5085 return changed;
5086 }
5087};
5088
5089} // namespace
5090
5091
5092template <typename ConvertTraits>
5093static Object* ConvertCase(
5094 Arguments args,
5095 unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005096 NoHandleAllocation ha;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005097 CONVERT_CHECKED(String, s, args[0]);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005098 s = s->TryFlattenGetString();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005099
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005100 const int length = s->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005101 // Assume that the string is not empty; we need this assumption later
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005102 if (length == 0) return s;
5103
5104 // Simpler handling of ascii strings.
5105 //
5106 // NOTE: This assumes that the upper/lower case of an ascii
5107 // character is also ascii. This is currently the case, but it
5108 // might break in the future if we implement more context and locale
5109 // dependent upper/lower conversions.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005110 if (s->IsSeqAsciiString()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005111 Object* o = Heap::AllocateRawAsciiString(length);
5112 if (o->IsFailure()) return o;
5113 SeqAsciiString* result = SeqAsciiString::cast(o);
5114 bool has_changed_character = ConvertTraits::ConvertAscii(
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005115 result->GetChars(), SeqAsciiString::cast(s)->GetChars(), length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005116 return has_changed_character ? result : s;
5117 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005118
5119 Object* answer = ConvertCaseHelper(s, length, length, mapping);
5120 if (answer->IsSmi()) {
5121 // Retry with correct length.
5122 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
5123 }
5124 return answer; // This may be a failure.
5125}
5126
5127
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005128static Object* Runtime_StringToLowerCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005129 return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005130}
5131
5132
5133static Object* Runtime_StringToUpperCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005134 return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005135}
5136
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005137
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005138static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
5139 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
5140}
5141
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005142
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005143static Object* Runtime_StringTrim(Arguments args) {
5144 NoHandleAllocation ha;
5145 ASSERT(args.length() == 3);
5146
5147 CONVERT_CHECKED(String, s, args[0]);
5148 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
5149 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
5150
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005151 s->TryFlatten();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005152 int length = s->length();
5153
5154 int left = 0;
5155 if (trimLeft) {
5156 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
5157 left++;
5158 }
5159 }
5160
5161 int right = length;
5162 if (trimRight) {
5163 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
5164 right--;
5165 }
5166 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005167 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005168}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005169
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005170
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005171template <typename schar, typename pchar>
5172void FindStringIndices(Vector<const schar> subject,
5173 Vector<const pchar> pattern,
5174 ZoneList<int>* indices,
5175 unsigned int limit) {
5176 ASSERT(limit > 0);
5177 // Collect indices of pattern in subject, and the end-of-string index.
5178 // Stop after finding at most limit values.
5179 StringSearchStrategy strategy =
5180 InitializeStringSearch(pattern, sizeof(schar) == 1);
5181 switch (strategy) {
5182 case SEARCH_FAIL: return;
5183 case SEARCH_SHORT: {
5184 int pattern_length = pattern.length();
5185 int index = 0;
5186 while (limit > 0) {
5187 index = SimpleIndexOf(subject, pattern, index);
5188 if (index < 0) return;
5189 indices->Add(index);
5190 index += pattern_length;
5191 limit--;
5192 }
5193 return;
5194 }
5195 case SEARCH_LONG: {
5196 int pattern_length = pattern.length();
5197 int index = 0;
5198 while (limit > 0) {
5199 index = ComplexIndexOf(subject, pattern, index);
5200 if (index < 0) return;
5201 indices->Add(index);
5202 index += pattern_length;
5203 limit--;
5204 }
5205 return;
5206 }
5207 default:
5208 UNREACHABLE();
5209 return;
5210 }
5211}
5212
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005213
5214static Object* Runtime_StringSplit(Arguments args) {
5215 ASSERT(args.length() == 3);
5216 HandleScope handle_scope;
5217 CONVERT_ARG_CHECKED(String, subject, 0);
5218 CONVERT_ARG_CHECKED(String, pattern, 1);
5219 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
5220
5221 int subject_length = subject->length();
5222 int pattern_length = pattern->length();
5223 RUNTIME_ASSERT(pattern_length > 0);
5224
5225 // The limit can be very large (0xffffffffu), but since the pattern
5226 // isn't empty, we can never create more parts than ~half the length
5227 // of the subject.
5228
5229 if (!subject->IsFlat()) FlattenString(subject);
5230
5231 static const int kMaxInitialListCapacity = 16;
5232
5233 ZoneScope scope(DELETE_ON_EXIT);
5234
5235 // Find (up to limit) indices of separator and end-of-string in subject
5236 int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
5237 ZoneList<int> indices(initial_capacity);
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00005238 if (!pattern->IsFlat()) FlattenString(pattern);
5239
5240 // No allocation block.
5241 {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005242 AssertNoAllocation nogc;
5243 if (subject->IsAsciiRepresentation()) {
5244 Vector<const char> subject_vector = subject->ToAsciiVector();
5245 if (pattern->IsAsciiRepresentation()) {
5246 FindStringIndices(subject_vector,
5247 pattern->ToAsciiVector(),
5248 &indices,
5249 limit);
5250 } else {
5251 FindStringIndices(subject_vector,
5252 pattern->ToUC16Vector(),
5253 &indices,
5254 limit);
5255 }
5256 } else {
5257 Vector<const uc16> subject_vector = subject->ToUC16Vector();
5258 if (pattern->IsAsciiRepresentation()) {
5259 FindStringIndices(subject_vector,
5260 pattern->ToAsciiVector(),
5261 &indices,
5262 limit);
5263 } else {
5264 FindStringIndices(subject_vector,
5265 pattern->ToUC16Vector(),
5266 &indices,
5267 limit);
5268 }
5269 }
5270 }
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00005271
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005272 if (static_cast<uint32_t>(indices.length()) < limit) {
5273 indices.Add(subject_length);
5274 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005275
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00005276 // The list indices now contains the end of each part to create.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005277
5278 // Create JSArray of substrings separated by separator.
5279 int part_count = indices.length();
5280
5281 Handle<JSArray> result = Factory::NewJSArray(part_count);
5282 result->set_length(Smi::FromInt(part_count));
5283
5284 ASSERT(result->HasFastElements());
5285
5286 if (part_count == 1 && indices.at(0) == subject_length) {
5287 FixedArray::cast(result->elements())->set(0, *subject);
5288 return *result;
5289 }
5290
5291 Handle<FixedArray> elements(FixedArray::cast(result->elements()));
5292 int part_start = 0;
5293 for (int i = 0; i < part_count; i++) {
5294 HandleScope local_loop_handle;
5295 int part_end = indices.at(i);
5296 Handle<String> substring =
5297 Factory::NewSubString(subject, part_start, part_end);
5298 elements->set(i, *substring);
5299 part_start = part_end + pattern_length;
5300 }
5301
5302 return *result;
5303}
5304
5305
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005306// Copies ascii characters to the given fixed array looking up
5307// one-char strings in the cache. Gives up on the first char that is
5308// not in the cache and fills the remainder with smi zeros. Returns
5309// the length of the successfully copied prefix.
5310static int CopyCachedAsciiCharsToArray(const char* chars,
5311 FixedArray* elements,
5312 int length) {
5313 AssertNoAllocation nogc;
5314 FixedArray* ascii_cache = Heap::single_character_string_cache();
5315 Object* undefined = Heap::undefined_value();
5316 int i;
5317 for (i = 0; i < length; ++i) {
5318 Object* value = ascii_cache->get(chars[i]);
5319 if (value == undefined) break;
5320 ASSERT(!Heap::InNewSpace(value));
5321 elements->set(i, value, SKIP_WRITE_BARRIER);
5322 }
5323 if (i < length) {
5324 ASSERT(Smi::FromInt(0) == 0);
5325 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
5326 }
5327#ifdef DEBUG
5328 for (int j = 0; j < length; ++j) {
5329 Object* element = elements->get(j);
5330 ASSERT(element == Smi::FromInt(0) ||
5331 (element->IsString() && String::cast(element)->LooksValid()));
5332 }
5333#endif
5334 return i;
5335}
5336
5337
5338// Converts a String to JSArray.
5339// For example, "foo" => ["f", "o", "o"].
5340static Object* Runtime_StringToArray(Arguments args) {
5341 HandleScope scope;
5342 ASSERT(args.length() == 1);
5343 CONVERT_ARG_CHECKED(String, s, 0);
5344
5345 s->TryFlatten();
5346 const int length = s->length();
5347
5348 Handle<FixedArray> elements;
5349 if (s->IsFlat() && s->IsAsciiRepresentation()) {
5350 Object* obj = Heap::AllocateUninitializedFixedArray(length);
5351 if (obj->IsFailure()) return obj;
5352 elements = Handle<FixedArray>(FixedArray::cast(obj));
5353
5354 Vector<const char> chars = s->ToAsciiVector();
5355 // Note, this will initialize all elements (not only the prefix)
5356 // to prevent GC from seeing partially initialized array.
5357 int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
5358 *elements,
5359 length);
5360
5361 for (int i = num_copied_from_cache; i < length; ++i) {
5362 elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
5363 }
5364 } else {
5365 elements = Factory::NewFixedArray(length);
5366 for (int i = 0; i < length; ++i) {
5367 elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
5368 }
5369 }
5370
5371#ifdef DEBUG
5372 for (int i = 0; i < length; ++i) {
5373 ASSERT(String::cast(elements->get(i))->length() == 1);
5374 }
5375#endif
5376
5377 return *Factory::NewJSArrayWithElements(elements);
5378}
5379
5380
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00005381static Object* Runtime_NewStringWrapper(Arguments args) {
5382 NoHandleAllocation ha;
5383 ASSERT(args.length() == 1);
5384 CONVERT_CHECKED(String, value, args[0]);
5385 return value->ToObject();
5386}
5387
5388
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00005389bool Runtime::IsUpperCaseChar(uint16_t ch) {
5390 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
5391 int char_length = to_upper_mapping.get(ch, 0, chars);
5392 return char_length == 0;
5393}
5394
5395
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005396static Object* Runtime_NumberToString(Arguments args) {
5397 NoHandleAllocation ha;
5398 ASSERT(args.length() == 1);
5399
5400 Object* number = args[0];
5401 RUNTIME_ASSERT(number->IsNumber());
5402
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005403 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005404}
5405
5406
ager@chromium.org357bf652010-04-12 11:30:10 +00005407static Object* Runtime_NumberToStringSkipCache(Arguments args) {
5408 NoHandleAllocation ha;
5409 ASSERT(args.length() == 1);
5410
5411 Object* number = args[0];
5412 RUNTIME_ASSERT(number->IsNumber());
5413
5414 return Heap::NumberToString(number, false);
5415}
5416
5417
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005418static Object* Runtime_NumberToInteger(Arguments args) {
5419 NoHandleAllocation ha;
5420 ASSERT(args.length() == 1);
5421
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005422 CONVERT_DOUBLE_CHECKED(number, args[0]);
5423
5424 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5425 if (number > 0 && number <= Smi::kMaxValue) {
5426 return Smi::FromInt(static_cast<int>(number));
5427 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005428 return Heap::NumberFromDouble(DoubleToInteger(number));
5429}
5430
5431
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005432static Object* Runtime_NumberToIntegerMapMinusZero(Arguments args) {
5433 NoHandleAllocation ha;
5434 ASSERT(args.length() == 1);
5435
5436 CONVERT_DOUBLE_CHECKED(number, args[0]);
5437
5438 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5439 if (number > 0 && number <= Smi::kMaxValue) {
5440 return Smi::FromInt(static_cast<int>(number));
5441 }
5442
5443 double double_value = DoubleToInteger(number);
5444 // Map both -0 and +0 to +0.
5445 if (double_value == 0) double_value = 0;
5446
5447 return Heap::NumberFromDouble(double_value);
5448}
5449
5450
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005451static Object* Runtime_NumberToJSUint32(Arguments args) {
5452 NoHandleAllocation ha;
5453 ASSERT(args.length() == 1);
5454
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005455 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005456 return Heap::NumberFromUint32(number);
5457}
5458
5459
5460static Object* Runtime_NumberToJSInt32(Arguments args) {
5461 NoHandleAllocation ha;
5462 ASSERT(args.length() == 1);
5463
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005464 CONVERT_DOUBLE_CHECKED(number, args[0]);
5465
5466 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5467 if (number > 0 && number <= Smi::kMaxValue) {
5468 return Smi::FromInt(static_cast<int>(number));
5469 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005470 return Heap::NumberFromInt32(DoubleToInt32(number));
5471}
5472
5473
ager@chromium.org870a0b62008-11-04 11:43:05 +00005474// Converts a Number to a Smi, if possible. Returns NaN if the number is not
5475// a small integer.
5476static Object* Runtime_NumberToSmi(Arguments args) {
5477 NoHandleAllocation ha;
5478 ASSERT(args.length() == 1);
5479
5480 Object* obj = args[0];
5481 if (obj->IsSmi()) {
5482 return obj;
5483 }
5484 if (obj->IsHeapNumber()) {
5485 double value = HeapNumber::cast(obj)->value();
5486 int int_value = FastD2I(value);
5487 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
5488 return Smi::FromInt(int_value);
5489 }
5490 }
5491 return Heap::nan_value();
5492}
5493
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005494
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005495static Object* Runtime_NumberAdd(Arguments args) {
5496 NoHandleAllocation ha;
5497 ASSERT(args.length() == 2);
5498
5499 CONVERT_DOUBLE_CHECKED(x, args[0]);
5500 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005501 return Heap::NumberFromDouble(x + y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005502}
5503
5504
5505static Object* Runtime_NumberSub(Arguments args) {
5506 NoHandleAllocation ha;
5507 ASSERT(args.length() == 2);
5508
5509 CONVERT_DOUBLE_CHECKED(x, args[0]);
5510 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005511 return Heap::NumberFromDouble(x - y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005512}
5513
5514
5515static Object* Runtime_NumberMul(Arguments args) {
5516 NoHandleAllocation ha;
5517 ASSERT(args.length() == 2);
5518
5519 CONVERT_DOUBLE_CHECKED(x, args[0]);
5520 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005521 return Heap::NumberFromDouble(x * y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005522}
5523
5524
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005525static Object* Runtime_NumberUnaryMinus(Arguments args) {
5526 NoHandleAllocation ha;
5527 ASSERT(args.length() == 1);
5528
5529 CONVERT_DOUBLE_CHECKED(x, args[0]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005530 return Heap::NumberFromDouble(-x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005531}
5532
5533
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00005534static Object* Runtime_NumberAlloc(Arguments args) {
5535 NoHandleAllocation ha;
5536 ASSERT(args.length() == 0);
5537
5538 return Heap::NumberFromDouble(9876543210.0);
5539}
5540
5541
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005542static Object* Runtime_NumberDiv(Arguments args) {
5543 NoHandleAllocation ha;
5544 ASSERT(args.length() == 2);
5545
5546 CONVERT_DOUBLE_CHECKED(x, args[0]);
5547 CONVERT_DOUBLE_CHECKED(y, args[1]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005548 return Heap::NumberFromDouble(x / y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005549}
5550
5551
5552static Object* Runtime_NumberMod(Arguments args) {
5553 NoHandleAllocation ha;
5554 ASSERT(args.length() == 2);
5555
5556 CONVERT_DOUBLE_CHECKED(x, args[0]);
5557 CONVERT_DOUBLE_CHECKED(y, args[1]);
5558
ager@chromium.org3811b432009-10-28 14:53:37 +00005559 x = modulo(x, y);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005560 // NumberFromDouble may return a Smi instead of a Number object
5561 return Heap::NumberFromDouble(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005562}
5563
5564
5565static Object* Runtime_StringAdd(Arguments args) {
5566 NoHandleAllocation ha;
5567 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005568 CONVERT_CHECKED(String, str1, args[0]);
5569 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005570 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00005571 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005572}
5573
5574
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005575template <typename sinkchar>
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005576static inline void StringBuilderConcatHelper(String* special,
5577 sinkchar* sink,
5578 FixedArray* fixed_array,
5579 int array_length) {
5580 int position = 0;
5581 for (int i = 0; i < array_length; i++) {
5582 Object* element = fixed_array->get(i);
5583 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005584 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005585 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005586 int pos;
5587 int len;
5588 if (encoded_slice > 0) {
5589 // Position and length encoded in one smi.
5590 pos = StringBuilderSubstringPosition::decode(encoded_slice);
5591 len = StringBuilderSubstringLength::decode(encoded_slice);
5592 } else {
5593 // Position and length encoded in two smis.
5594 Object* obj = fixed_array->get(++i);
5595 ASSERT(obj->IsSmi());
5596 pos = Smi::cast(obj)->value();
5597 len = -encoded_slice;
5598 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00005599 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00005600 sink + position,
5601 pos,
5602 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005603 position += len;
5604 } else {
5605 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005606 int element_length = string->length();
5607 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005608 position += element_length;
5609 }
5610 }
5611}
5612
5613
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005614static Object* Runtime_StringBuilderConcat(Arguments args) {
5615 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005616 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005617 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005618 if (!args[1]->IsSmi()) {
5619 Top::context()->mark_out_of_memory();
5620 return Failure::OutOfMemoryException();
5621 }
5622 int array_length = Smi::cast(args[1])->value();
5623 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005624
5625 // This assumption is used by the slice encoding in one or two smis.
5626 ASSERT(Smi::kMaxValue >= String::kMaxLength);
5627
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005628 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005629 if (!array->HasFastElements()) {
5630 return Top::Throw(Heap::illegal_argument_symbol());
5631 }
5632 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005633 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005634 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005635 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005636
5637 if (array_length == 0) {
5638 return Heap::empty_string();
5639 } else if (array_length == 1) {
5640 Object* first = fixed_array->get(0);
5641 if (first->IsString()) return first;
5642 }
5643
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005644 bool ascii = special->HasOnlyAsciiChars();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005645 int position = 0;
5646 for (int i = 0; i < array_length; i++) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005647 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005648 Object* elt = fixed_array->get(i);
5649 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005650 // Smi encoding of position and length.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005651 int smi_value = Smi::cast(elt)->value();
5652 int pos;
5653 int len;
5654 if (smi_value > 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005655 // Position and length encoded in one smi.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005656 pos = StringBuilderSubstringPosition::decode(smi_value);
5657 len = StringBuilderSubstringLength::decode(smi_value);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005658 } else {
5659 // Position and length encoded in two smis.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005660 len = -smi_value;
5661 // Get the position and check that it is a positive smi.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005662 i++;
5663 if (i >= array_length) {
5664 return Top::Throw(Heap::illegal_argument_symbol());
5665 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005666 Object* next_smi = fixed_array->get(i);
5667 if (!next_smi->IsSmi()) {
5668 return Top::Throw(Heap::illegal_argument_symbol());
5669 }
5670 pos = Smi::cast(next_smi)->value();
5671 if (pos < 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005672 return Top::Throw(Heap::illegal_argument_symbol());
5673 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005674 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005675 ASSERT(pos >= 0);
5676 ASSERT(len >= 0);
5677 if (pos > special_length || len > special_length - pos) {
5678 return Top::Throw(Heap::illegal_argument_symbol());
5679 }
5680 increment = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005681 } else if (elt->IsString()) {
5682 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005683 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005684 increment = element_length;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005685 if (ascii && !element->HasOnlyAsciiChars()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005686 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005687 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005688 } else {
5689 return Top::Throw(Heap::illegal_argument_symbol());
5690 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005691 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005692 Top::context()->mark_out_of_memory();
5693 return Failure::OutOfMemoryException();
5694 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005695 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005696 }
5697
5698 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005699 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005700
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005701 if (ascii) {
5702 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005703 if (object->IsFailure()) return object;
5704 SeqAsciiString* answer = SeqAsciiString::cast(object);
5705 StringBuilderConcatHelper(special,
5706 answer->GetChars(),
5707 fixed_array,
5708 array_length);
5709 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005710 } else {
5711 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005712 if (object->IsFailure()) return object;
5713 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
5714 StringBuilderConcatHelper(special,
5715 answer->GetChars(),
5716 fixed_array,
5717 array_length);
5718 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005719 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005720}
5721
5722
5723static Object* Runtime_NumberOr(Arguments args) {
5724 NoHandleAllocation ha;
5725 ASSERT(args.length() == 2);
5726
5727 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5728 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5729 return Heap::NumberFromInt32(x | y);
5730}
5731
5732
5733static Object* Runtime_NumberAnd(Arguments args) {
5734 NoHandleAllocation ha;
5735 ASSERT(args.length() == 2);
5736
5737 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5738 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5739 return Heap::NumberFromInt32(x & y);
5740}
5741
5742
5743static Object* Runtime_NumberXor(Arguments args) {
5744 NoHandleAllocation ha;
5745 ASSERT(args.length() == 2);
5746
5747 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5748 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5749 return Heap::NumberFromInt32(x ^ y);
5750}
5751
5752
5753static Object* Runtime_NumberNot(Arguments args) {
5754 NoHandleAllocation ha;
5755 ASSERT(args.length() == 1);
5756
5757 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5758 return Heap::NumberFromInt32(~x);
5759}
5760
5761
5762static Object* Runtime_NumberShl(Arguments args) {
5763 NoHandleAllocation ha;
5764 ASSERT(args.length() == 2);
5765
5766 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5767 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5768 return Heap::NumberFromInt32(x << (y & 0x1f));
5769}
5770
5771
5772static Object* Runtime_NumberShr(Arguments args) {
5773 NoHandleAllocation ha;
5774 ASSERT(args.length() == 2);
5775
5776 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
5777 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5778 return Heap::NumberFromUint32(x >> (y & 0x1f));
5779}
5780
5781
5782static Object* Runtime_NumberSar(Arguments args) {
5783 NoHandleAllocation ha;
5784 ASSERT(args.length() == 2);
5785
5786 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5787 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5788 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
5789}
5790
5791
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005792static Object* Runtime_NumberEquals(Arguments args) {
5793 NoHandleAllocation ha;
5794 ASSERT(args.length() == 2);
5795
5796 CONVERT_DOUBLE_CHECKED(x, args[0]);
5797 CONVERT_DOUBLE_CHECKED(y, args[1]);
5798 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
5799 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
5800 if (x == y) return Smi::FromInt(EQUAL);
5801 Object* result;
5802 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
5803 result = Smi::FromInt(EQUAL);
5804 } else {
5805 result = Smi::FromInt(NOT_EQUAL);
5806 }
5807 return result;
5808}
5809
5810
5811static Object* Runtime_StringEquals(Arguments args) {
5812 NoHandleAllocation ha;
5813 ASSERT(args.length() == 2);
5814
5815 CONVERT_CHECKED(String, x, args[0]);
5816 CONVERT_CHECKED(String, y, args[1]);
5817
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005818 bool not_equal = !x->Equals(y);
5819 // This is slightly convoluted because the value that signifies
5820 // equality is 0 and inequality is 1 so we have to negate the result
5821 // from String::Equals.
5822 ASSERT(not_equal == 0 || not_equal == 1);
5823 STATIC_CHECK(EQUAL == 0);
5824 STATIC_CHECK(NOT_EQUAL == 1);
5825 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005826}
5827
5828
5829static Object* Runtime_NumberCompare(Arguments args) {
5830 NoHandleAllocation ha;
5831 ASSERT(args.length() == 3);
5832
5833 CONVERT_DOUBLE_CHECKED(x, args[0]);
5834 CONVERT_DOUBLE_CHECKED(y, args[1]);
5835 if (isnan(x) || isnan(y)) return args[2];
5836 if (x == y) return Smi::FromInt(EQUAL);
5837 if (isless(x, y)) return Smi::FromInt(LESS);
5838 return Smi::FromInt(GREATER);
5839}
5840
5841
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005842// Compare two Smis as if they were converted to strings and then
5843// compared lexicographically.
5844static Object* Runtime_SmiLexicographicCompare(Arguments args) {
5845 NoHandleAllocation ha;
5846 ASSERT(args.length() == 2);
5847
5848 // Arrays for the individual characters of the two Smis. Smis are
5849 // 31 bit integers and 10 decimal digits are therefore enough.
5850 static int x_elms[10];
5851 static int y_elms[10];
5852
5853 // Extract the integer values from the Smis.
5854 CONVERT_CHECKED(Smi, x, args[0]);
5855 CONVERT_CHECKED(Smi, y, args[1]);
5856 int x_value = x->value();
5857 int y_value = y->value();
5858
5859 // If the integers are equal so are the string representations.
5860 if (x_value == y_value) return Smi::FromInt(EQUAL);
5861
5862 // If one of the integers are zero the normal integer order is the
5863 // same as the lexicographic order of the string representations.
5864 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
5865
ager@chromium.org32912102009-01-16 10:38:43 +00005866 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005867 // smallest because the char code of '-' is less than the char code
5868 // of any digit. Otherwise, we make both values positive.
5869 if (x_value < 0 || y_value < 0) {
5870 if (y_value >= 0) return Smi::FromInt(LESS);
5871 if (x_value >= 0) return Smi::FromInt(GREATER);
5872 x_value = -x_value;
5873 y_value = -y_value;
5874 }
5875
5876 // Convert the integers to arrays of their decimal digits.
5877 int x_index = 0;
5878 int y_index = 0;
5879 while (x_value > 0) {
5880 x_elms[x_index++] = x_value % 10;
5881 x_value /= 10;
5882 }
5883 while (y_value > 0) {
5884 y_elms[y_index++] = y_value % 10;
5885 y_value /= 10;
5886 }
5887
5888 // Loop through the arrays of decimal digits finding the first place
5889 // where they differ.
5890 while (--x_index >= 0 && --y_index >= 0) {
5891 int diff = x_elms[x_index] - y_elms[y_index];
5892 if (diff != 0) return Smi::FromInt(diff);
5893 }
5894
5895 // If one array is a suffix of the other array, the longest array is
5896 // the representation of the largest of the Smis in the
5897 // lexicographic ordering.
5898 return Smi::FromInt(x_index - y_index);
5899}
5900
5901
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005902static Object* StringInputBufferCompare(String* x, String* y) {
5903 static StringInputBuffer bufx;
5904 static StringInputBuffer bufy;
5905 bufx.Reset(x);
5906 bufy.Reset(y);
5907 while (bufx.has_more() && bufy.has_more()) {
5908 int d = bufx.GetNext() - bufy.GetNext();
5909 if (d < 0) return Smi::FromInt(LESS);
5910 else if (d > 0) return Smi::FromInt(GREATER);
5911 }
5912
5913 // x is (non-trivial) prefix of y:
5914 if (bufy.has_more()) return Smi::FromInt(LESS);
5915 // y is prefix of x:
5916 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
5917}
5918
5919
5920static Object* FlatStringCompare(String* x, String* y) {
5921 ASSERT(x->IsFlat());
5922 ASSERT(y->IsFlat());
5923 Object* equal_prefix_result = Smi::FromInt(EQUAL);
5924 int prefix_length = x->length();
5925 if (y->length() < prefix_length) {
5926 prefix_length = y->length();
5927 equal_prefix_result = Smi::FromInt(GREATER);
5928 } else if (y->length() > prefix_length) {
5929 equal_prefix_result = Smi::FromInt(LESS);
5930 }
5931 int r;
5932 if (x->IsAsciiRepresentation()) {
5933 Vector<const char> x_chars = x->ToAsciiVector();
5934 if (y->IsAsciiRepresentation()) {
5935 Vector<const char> y_chars = y->ToAsciiVector();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005936 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005937 } else {
5938 Vector<const uc16> y_chars = y->ToUC16Vector();
5939 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5940 }
5941 } else {
5942 Vector<const uc16> x_chars = x->ToUC16Vector();
5943 if (y->IsAsciiRepresentation()) {
5944 Vector<const char> y_chars = y->ToAsciiVector();
5945 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5946 } else {
5947 Vector<const uc16> y_chars = y->ToUC16Vector();
5948 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5949 }
5950 }
5951 Object* result;
5952 if (r == 0) {
5953 result = equal_prefix_result;
5954 } else {
5955 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
5956 }
5957 ASSERT(result == StringInputBufferCompare(x, y));
5958 return result;
5959}
5960
5961
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005962static Object* Runtime_StringCompare(Arguments args) {
5963 NoHandleAllocation ha;
5964 ASSERT(args.length() == 2);
5965
5966 CONVERT_CHECKED(String, x, args[0]);
5967 CONVERT_CHECKED(String, y, args[1]);
5968
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005969 Counters::string_compare_runtime.Increment();
5970
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005971 // A few fast case tests before we flatten.
5972 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005973 if (y->length() == 0) {
5974 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005975 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005976 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005977 return Smi::FromInt(LESS);
5978 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005979
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005980 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005981 if (d < 0) return Smi::FromInt(LESS);
5982 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005983
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005984 Object* obj = Heap::PrepareForCompare(x);
5985 if (obj->IsFailure()) return obj;
5986 obj = Heap::PrepareForCompare(y);
5987 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005988
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005989 return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
5990 : StringInputBufferCompare(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005991}
5992
5993
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005994static Object* Runtime_Math_acos(Arguments args) {
5995 NoHandleAllocation ha;
5996 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005997 Counters::math_acos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005998
5999 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006000 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006001}
6002
6003
6004static Object* Runtime_Math_asin(Arguments args) {
6005 NoHandleAllocation ha;
6006 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006007 Counters::math_asin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006008
6009 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006010 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006011}
6012
6013
6014static Object* Runtime_Math_atan(Arguments args) {
6015 NoHandleAllocation ha;
6016 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006017 Counters::math_atan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006018
6019 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006020 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006021}
6022
6023
6024static Object* Runtime_Math_atan2(Arguments args) {
6025 NoHandleAllocation ha;
6026 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006027 Counters::math_atan2.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006028
6029 CONVERT_DOUBLE_CHECKED(x, args[0]);
6030 CONVERT_DOUBLE_CHECKED(y, args[1]);
6031 double result;
6032 if (isinf(x) && isinf(y)) {
6033 // Make sure that the result in case of two infinite arguments
6034 // is a multiple of Pi / 4. The sign of the result is determined
6035 // by the first argument (x) and the sign of the second argument
6036 // determines the multiplier: one or three.
6037 static double kPiDividedBy4 = 0.78539816339744830962;
6038 int multiplier = (x < 0) ? -1 : 1;
6039 if (y < 0) multiplier *= 3;
6040 result = multiplier * kPiDividedBy4;
6041 } else {
6042 result = atan2(x, y);
6043 }
6044 return Heap::AllocateHeapNumber(result);
6045}
6046
6047
6048static Object* Runtime_Math_ceil(Arguments args) {
6049 NoHandleAllocation ha;
6050 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006051 Counters::math_ceil.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006052
6053 CONVERT_DOUBLE_CHECKED(x, args[0]);
6054 return Heap::NumberFromDouble(ceiling(x));
6055}
6056
6057
6058static Object* Runtime_Math_cos(Arguments args) {
6059 NoHandleAllocation ha;
6060 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006061 Counters::math_cos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006062
6063 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006064 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006065}
6066
6067
6068static Object* Runtime_Math_exp(Arguments args) {
6069 NoHandleAllocation ha;
6070 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006071 Counters::math_exp.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006072
6073 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006074 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006075}
6076
6077
6078static Object* Runtime_Math_floor(Arguments args) {
6079 NoHandleAllocation ha;
6080 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006081 Counters::math_floor.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006082
6083 CONVERT_DOUBLE_CHECKED(x, args[0]);
6084 return Heap::NumberFromDouble(floor(x));
6085}
6086
6087
6088static Object* Runtime_Math_log(Arguments args) {
6089 NoHandleAllocation ha;
6090 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006091 Counters::math_log.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006092
6093 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006094 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006095}
6096
6097
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006098// Helper function to compute x^y, where y is known to be an
6099// integer. Uses binary decomposition to limit the number of
6100// multiplications; see the discussion in "Hacker's Delight" by Henry
6101// S. Warren, Jr., figure 11-6, page 213.
6102static double powi(double x, int y) {
6103 ASSERT(y != kMinInt);
6104 unsigned n = (y < 0) ? -y : y;
6105 double m = x;
6106 double p = 1;
6107 while (true) {
6108 if ((n & 1) != 0) p *= m;
6109 n >>= 1;
6110 if (n == 0) {
6111 if (y < 0) {
6112 // Unfortunately, we have to be careful when p has reached
6113 // infinity in the computation, because sometimes the higher
6114 // internal precision in the pow() implementation would have
6115 // given us a finite p. This happens very rarely.
6116 double result = 1.0 / p;
6117 return (result == 0 && isinf(p))
6118 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
6119 : result;
6120 } else {
6121 return p;
6122 }
6123 }
6124 m *= m;
6125 }
6126}
6127
6128
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006129static Object* Runtime_Math_pow(Arguments args) {
6130 NoHandleAllocation ha;
6131 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006132 Counters::math_pow.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006133
6134 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006135
6136 // If the second argument is a smi, it is much faster to call the
6137 // custom powi() function than the generic pow().
6138 if (args[1]->IsSmi()) {
6139 int y = Smi::cast(args[1])->value();
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00006140 return Heap::NumberFromDouble(powi(x, y));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006141 }
6142
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006143 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00006144
6145 if (!isinf(x)) {
6146 if (y == 0.5) {
6147 // It's not uncommon to use Math.pow(x, 0.5) to compute the
6148 // square root of a number. To speed up such computations, we
6149 // explictly check for this case and use the sqrt() function
6150 // which is faster than pow().
6151 return Heap::AllocateHeapNumber(sqrt(x));
6152 } else if (y == -0.5) {
6153 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
6154 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
6155 }
6156 }
6157
6158 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006159 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006160 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6161 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006162 } else {
6163 return Heap::AllocateHeapNumber(pow(x, y));
6164 }
6165}
6166
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006167// Fast version of Math.pow if we know that y is not an integer and
6168// y is not -0.5 or 0.5. Used as slowcase from codegen.
6169static Object* Runtime_Math_pow_cfunction(Arguments args) {
6170 NoHandleAllocation ha;
6171 ASSERT(args.length() == 2);
6172 CONVERT_DOUBLE_CHECKED(x, args[0]);
6173 CONVERT_DOUBLE_CHECKED(y, args[1]);
6174 if (y == 0) {
6175 return Smi::FromInt(1);
6176 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6177 return Heap::nan_value();
6178 } else {
6179 return Heap::AllocateHeapNumber(pow(x, y));
6180 }
6181}
6182
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006183
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006184static Object* Runtime_RoundNumber(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006185 NoHandleAllocation ha;
6186 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006187 Counters::math_round.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006188
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006189 if (!args[0]->IsHeapNumber()) {
6190 // Must be smi. Return the argument unchanged for all the other types
6191 // to make fuzz-natives test happy.
6192 return args[0];
6193 }
6194
6195 HeapNumber* number = reinterpret_cast<HeapNumber*>(args[0]);
6196
6197 double value = number->value();
6198 int exponent = number->get_exponent();
6199 int sign = number->get_sign();
6200
6201 // We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
6202 // should be rounded to 2^30, which is not smi.
6203 if (!sign && exponent <= kSmiValueSize - 3) {
6204 return Smi::FromInt(static_cast<int>(value + 0.5));
6205 }
6206
6207 // If the magnitude is big enough, there's no place for fraction part. If we
6208 // try to add 0.5 to this number, 1.0 will be added instead.
6209 if (exponent >= 52) {
6210 return number;
6211 }
6212
6213 if (sign && value >= -0.5) return Heap::minus_zero_value();
6214
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006215 // Do not call NumberFromDouble() to avoid extra checks.
6216 return Heap::AllocateHeapNumber(floor(value + 0.5));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006217}
6218
6219
6220static Object* Runtime_Math_sin(Arguments args) {
6221 NoHandleAllocation ha;
6222 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006223 Counters::math_sin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006224
6225 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006226 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006227}
6228
6229
6230static Object* Runtime_Math_sqrt(Arguments args) {
6231 NoHandleAllocation ha;
6232 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006233 Counters::math_sqrt.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006234
6235 CONVERT_DOUBLE_CHECKED(x, args[0]);
6236 return Heap::AllocateHeapNumber(sqrt(x));
6237}
6238
6239
6240static Object* Runtime_Math_tan(Arguments args) {
6241 NoHandleAllocation ha;
6242 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006243 Counters::math_tan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006244
6245 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006246 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006247}
6248
6249
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006250static int MakeDay(int year, int month, int day) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006251 static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
6252 181, 212, 243, 273, 304, 334};
6253 static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
6254 182, 213, 244, 274, 305, 335};
6255
6256 year += month / 12;
6257 month %= 12;
6258 if (month < 0) {
6259 year--;
6260 month += 12;
6261 }
6262
6263 ASSERT(month >= 0);
6264 ASSERT(month < 12);
6265
6266 // year_delta is an arbitrary number such that:
6267 // a) year_delta = -1 (mod 400)
6268 // b) year + year_delta > 0 for years in the range defined by
6269 // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
6270 // Jan 1 1970. This is required so that we don't run into integer
6271 // division of negative numbers.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006272 // c) there shouldn't be an overflow for 32-bit integers in the following
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006273 // operations.
6274 static const int year_delta = 399999;
6275 static const int base_day = 365 * (1970 + year_delta) +
6276 (1970 + year_delta) / 4 -
6277 (1970 + year_delta) / 100 +
6278 (1970 + year_delta) / 400;
6279
6280 int year1 = year + year_delta;
6281 int day_from_year = 365 * year1 +
6282 year1 / 4 -
6283 year1 / 100 +
6284 year1 / 400 -
6285 base_day;
6286
6287 if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006288 return day_from_year + day_from_month[month] + day - 1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006289 }
6290
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006291 return day_from_year + day_from_month_leap[month] + day - 1;
6292}
6293
6294
6295static Object* Runtime_DateMakeDay(Arguments args) {
6296 NoHandleAllocation ha;
6297 ASSERT(args.length() == 3);
6298
6299 CONVERT_SMI_CHECKED(year, args[0]);
6300 CONVERT_SMI_CHECKED(month, args[1]);
6301 CONVERT_SMI_CHECKED(date, args[2]);
6302
6303 return Smi::FromInt(MakeDay(year, month, date));
6304}
6305
6306
6307static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
6308static const int kDaysIn4Years = 4 * 365 + 1;
6309static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
6310static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
6311static const int kDays1970to2000 = 30 * 365 + 7;
6312static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
6313 kDays1970to2000;
6314static const int kYearsOffset = 400000;
6315
6316static const char kDayInYear[] = {
6317 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6318 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6319 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6320 22, 23, 24, 25, 26, 27, 28,
6321 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6322 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6323 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6324 22, 23, 24, 25, 26, 27, 28, 29, 30,
6325 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6326 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6327 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6328 22, 23, 24, 25, 26, 27, 28, 29, 30,
6329 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6330 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6331 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6332 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6333 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6334 22, 23, 24, 25, 26, 27, 28, 29, 30,
6335 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6336 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6337 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6338 22, 23, 24, 25, 26, 27, 28, 29, 30,
6339 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6340 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6341
6342 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6343 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6344 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6345 22, 23, 24, 25, 26, 27, 28,
6346 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6347 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6348 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6349 22, 23, 24, 25, 26, 27, 28, 29, 30,
6350 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6351 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6352 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6353 22, 23, 24, 25, 26, 27, 28, 29, 30,
6354 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6355 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6356 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6357 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6358 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6359 22, 23, 24, 25, 26, 27, 28, 29, 30,
6360 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6361 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6362 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6363 22, 23, 24, 25, 26, 27, 28, 29, 30,
6364 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6365 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6366
6367 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6368 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6369 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6370 22, 23, 24, 25, 26, 27, 28, 29,
6371 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6372 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6373 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6374 22, 23, 24, 25, 26, 27, 28, 29, 30,
6375 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6376 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6377 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6378 22, 23, 24, 25, 26, 27, 28, 29, 30,
6379 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6380 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6381 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6382 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6383 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6384 22, 23, 24, 25, 26, 27, 28, 29, 30,
6385 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6386 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6387 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6388 22, 23, 24, 25, 26, 27, 28, 29, 30,
6389 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6390 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6391
6392 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6393 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6394 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6395 22, 23, 24, 25, 26, 27, 28,
6396 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6397 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6398 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6399 22, 23, 24, 25, 26, 27, 28, 29, 30,
6400 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6401 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6402 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6403 22, 23, 24, 25, 26, 27, 28, 29, 30,
6404 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6405 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6406 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6407 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6408 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6409 22, 23, 24, 25, 26, 27, 28, 29, 30,
6410 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6411 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6412 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6413 22, 23, 24, 25, 26, 27, 28, 29, 30,
6414 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6415 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
6416
6417static const char kMonthInYear[] = {
6418 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,
6419 0, 0, 0, 0, 0, 0,
6420 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,
6421 1, 1, 1,
6422 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,
6423 2, 2, 2, 2, 2, 2,
6424 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,
6425 3, 3, 3, 3, 3,
6426 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,
6427 4, 4, 4, 4, 4, 4,
6428 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,
6429 5, 5, 5, 5, 5,
6430 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,
6431 6, 6, 6, 6, 6, 6,
6432 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,
6433 7, 7, 7, 7, 7, 7,
6434 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,
6435 8, 8, 8, 8, 8,
6436 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,
6437 9, 9, 9, 9, 9, 9,
6438 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6439 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6440 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6441 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6442
6443 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,
6444 0, 0, 0, 0, 0, 0,
6445 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,
6446 1, 1, 1,
6447 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,
6448 2, 2, 2, 2, 2, 2,
6449 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,
6450 3, 3, 3, 3, 3,
6451 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,
6452 4, 4, 4, 4, 4, 4,
6453 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,
6454 5, 5, 5, 5, 5,
6455 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,
6456 6, 6, 6, 6, 6, 6,
6457 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,
6458 7, 7, 7, 7, 7, 7,
6459 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,
6460 8, 8, 8, 8, 8,
6461 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,
6462 9, 9, 9, 9, 9, 9,
6463 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6464 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6465 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6466 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6467
6468 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,
6469 0, 0, 0, 0, 0, 0,
6470 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,
6471 1, 1, 1, 1,
6472 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,
6473 2, 2, 2, 2, 2, 2,
6474 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,
6475 3, 3, 3, 3, 3,
6476 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,
6477 4, 4, 4, 4, 4, 4,
6478 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,
6479 5, 5, 5, 5, 5,
6480 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,
6481 6, 6, 6, 6, 6, 6,
6482 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,
6483 7, 7, 7, 7, 7, 7,
6484 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,
6485 8, 8, 8, 8, 8,
6486 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,
6487 9, 9, 9, 9, 9, 9,
6488 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6489 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6490 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6491 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6492
6493 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,
6494 0, 0, 0, 0, 0, 0,
6495 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,
6496 1, 1, 1,
6497 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,
6498 2, 2, 2, 2, 2, 2,
6499 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,
6500 3, 3, 3, 3, 3,
6501 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,
6502 4, 4, 4, 4, 4, 4,
6503 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,
6504 5, 5, 5, 5, 5,
6505 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,
6506 6, 6, 6, 6, 6, 6,
6507 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,
6508 7, 7, 7, 7, 7, 7,
6509 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,
6510 8, 8, 8, 8, 8,
6511 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,
6512 9, 9, 9, 9, 9, 9,
6513 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6514 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6515 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6516 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
6517
6518
6519// This function works for dates from 1970 to 2099.
6520static inline void DateYMDFromTimeAfter1970(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006521 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006522#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006523 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006524#endif
6525
6526 year = 1970 + (4 * date + 2) / kDaysIn4Years;
6527 date %= kDaysIn4Years;
6528
6529 month = kMonthInYear[date];
6530 day = kDayInYear[date];
6531
6532 ASSERT(MakeDay(year, month, day) == save_date);
6533}
6534
6535
6536static inline void DateYMDFromTimeSlow(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006537 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006538#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006539 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006540#endif
6541
6542 date += kDaysOffset;
6543 year = 400 * (date / kDaysIn400Years) - kYearsOffset;
6544 date %= kDaysIn400Years;
6545
6546 ASSERT(MakeDay(year, 0, 1) + date == save_date);
6547
6548 date--;
6549 int yd1 = date / kDaysIn100Years;
6550 date %= kDaysIn100Years;
6551 year += 100 * yd1;
6552
6553 date++;
6554 int yd2 = date / kDaysIn4Years;
6555 date %= kDaysIn4Years;
6556 year += 4 * yd2;
6557
6558 date--;
6559 int yd3 = date / 365;
6560 date %= 365;
6561 year += yd3;
6562
6563 bool is_leap = (!yd1 || yd2) && !yd3;
6564
6565 ASSERT(date >= -1);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006566 ASSERT(is_leap || (date >= 0));
6567 ASSERT((date < 365) || (is_leap && (date < 366)));
6568 ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0))));
6569 ASSERT(is_leap || ((MakeDay(year, 0, 1) + date) == save_date));
6570 ASSERT(!is_leap || ((MakeDay(year, 0, 1) + date + 1) == save_date));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006571
6572 if (is_leap) {
6573 day = kDayInYear[2*365 + 1 + date];
6574 month = kMonthInYear[2*365 + 1 + date];
6575 } else {
6576 day = kDayInYear[date];
6577 month = kMonthInYear[date];
6578 }
6579
6580 ASSERT(MakeDay(year, month, day) == save_date);
6581}
6582
6583
6584static inline void DateYMDFromTime(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006585 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006586 if (date >= 0 && date < 32 * kDaysIn4Years) {
6587 DateYMDFromTimeAfter1970(date, year, month, day);
6588 } else {
6589 DateYMDFromTimeSlow(date, year, month, day);
6590 }
6591}
6592
6593
6594static Object* Runtime_DateYMDFromTime(Arguments args) {
6595 NoHandleAllocation ha;
6596 ASSERT(args.length() == 2);
6597
6598 CONVERT_DOUBLE_CHECKED(t, args[0]);
6599 CONVERT_CHECKED(JSArray, res_array, args[1]);
6600
6601 int year, month, day;
6602 DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
6603
ricow@chromium.org0b9f8502010-08-18 07:45:01 +00006604 RUNTIME_ASSERT(res_array->elements()->map() == Heap::fixed_array_map());
6605 FixedArray* elms = FixedArray::cast(res_array->elements());
6606 RUNTIME_ASSERT(elms->length() == 3);
6607
6608 elms->set(0, Smi::FromInt(year));
6609 elms->set(1, Smi::FromInt(month));
6610 elms->set(2, Smi::FromInt(day));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006611
6612 return Heap::undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006613}
6614
6615
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006616static Object* Runtime_NewArgumentsFast(Arguments args) {
6617 NoHandleAllocation ha;
6618 ASSERT(args.length() == 3);
6619
6620 JSFunction* callee = JSFunction::cast(args[0]);
6621 Object** parameters = reinterpret_cast<Object**>(args[1]);
6622 const int length = Smi::cast(args[2])->value();
6623
6624 Object* result = Heap::AllocateArgumentsObject(callee, length);
6625 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006626 // Allocate the elements if needed.
6627 if (length > 0) {
6628 // Allocate the fixed array.
6629 Object* obj = Heap::AllocateRawFixedArray(length);
6630 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006631
6632 AssertNoAllocation no_gc;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006633 FixedArray* array = reinterpret_cast<FixedArray*>(obj);
6634 array->set_map(Heap::fixed_array_map());
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006635 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006636
6637 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006638 for (int i = 0; i < length; i++) {
6639 array->set(i, *--parameters, mode);
6640 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006641 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006642 }
6643 return result;
6644}
6645
6646
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006647static Object* Runtime_NewClosure(Arguments args) {
6648 HandleScope scope;
6649 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00006650 CONVERT_ARG_CHECKED(Context, context, 0);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006651 CONVERT_ARG_CHECKED(SharedFunctionInfo, shared, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006652
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00006653 PretenureFlag pretenure = (context->global_context() == *context)
6654 ? TENURED // Allocate global closures in old space.
6655 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006656 Handle<JSFunction> result =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006657 Factory::NewFunctionFromSharedFunctionInfo(shared, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006658 return *result;
6659}
6660
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006661static Object* Runtime_NewObjectFromBound(Arguments args) {
6662 HandleScope scope;
6663 ASSERT(args.length() == 2);
6664 CONVERT_ARG_CHECKED(JSFunction, function, 0);
6665 CONVERT_ARG_CHECKED(JSArray, params, 1);
6666
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006667 RUNTIME_ASSERT(params->HasFastElements());
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006668 FixedArray* fixed = FixedArray::cast(params->elements());
6669
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006670 int fixed_length = Smi::cast(params->length())->value();
6671 SmartPointer<Object**> param_data(NewArray<Object**>(fixed_length));
6672 for (int i = 0; i < fixed_length; i++) {
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006673 Handle<Object> val = Handle<Object>(fixed->get(i));
6674 param_data[i] = val.location();
6675 }
6676
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006677 bool exception = false;
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006678 Handle<Object> result = Execution::New(
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006679 function, fixed_length, *param_data, &exception);
6680 if (exception) {
6681 return Failure::Exception();
6682 }
6683 ASSERT(!result.is_null());
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006684 return *result;
6685}
6686
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006687
ager@chromium.org5c838252010-02-19 08:53:10 +00006688static Code* ComputeConstructStub(Handle<JSFunction> function) {
6689 Handle<Object> prototype = Factory::null_value();
6690 if (function->has_instance_prototype()) {
6691 prototype = Handle<Object>(function->instance_prototype());
6692 }
6693 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006694 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00006695 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006696 if (code->IsFailure()) {
6697 return Builtins::builtin(Builtins::JSConstructStubGeneric);
6698 }
6699 return Code::cast(code);
6700 }
6701
ager@chromium.org5c838252010-02-19 08:53:10 +00006702 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006703}
6704
6705
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006706static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006707 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006708 ASSERT(args.length() == 1);
6709
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006710 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006711
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006712 // If the constructor isn't a proper function we throw a type error.
6713 if (!constructor->IsJSFunction()) {
6714 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6715 Handle<Object> type_error =
6716 Factory::NewTypeError("not_constructor", arguments);
6717 return Top::Throw(*type_error);
6718 }
6719
6720 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006721
6722 // If function should not have prototype, construction is not allowed. In this
6723 // case generated code bailouts here, since function has no initial_map.
6724 if (!function->should_have_prototype()) {
6725 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6726 Handle<Object> type_error =
6727 Factory::NewTypeError("not_constructor", arguments);
6728 return Top::Throw(*type_error);
6729 }
6730
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006731#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006732 // Handle stepping into constructors if step into is active.
6733 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006734 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006735 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006736#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006737
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006738 if (function->has_initial_map()) {
6739 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006740 // The 'Function' function ignores the receiver object when
6741 // called using 'new' and creates a new JSFunction object that
6742 // is returned. The receiver object is only used for error
6743 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006744 // JSFunction. Factory::NewJSObject() should not be used to
6745 // allocate JSFunctions since it does not properly initialize
6746 // the shared part of the function. Since the receiver is
6747 // ignored anyway, we use the global object as the receiver
6748 // instead of a new JSFunction object. This way, errors are
6749 // reported the same way whether or not 'Function' is called
6750 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006751 return Top::context()->global();
6752 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006753 }
6754
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006755 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006756 Handle<SharedFunctionInfo> shared(function->shared());
6757 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006758
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006759 bool first_allocation = !function->has_initial_map();
6760 Handle<JSObject> result = Factory::NewJSObject(function);
6761 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006762 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00006763 ComputeConstructStub(Handle<JSFunction>(function)));
6764 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006765 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006766
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006767 Counters::constructed_objects.Increment();
6768 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006769
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006770 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006771}
6772
6773
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006774static Object* Runtime_LazyCompile(Arguments args) {
6775 HandleScope scope;
6776 ASSERT(args.length() == 1);
6777
6778 Handle<JSFunction> function = args.at<JSFunction>(0);
6779#ifdef DEBUG
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00006780 if (FLAG_trace_lazy && !function->shared()->is_compiled()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006781 PrintF("[lazy: ");
6782 function->shared()->name()->Print();
6783 PrintF("]\n");
6784 }
6785#endif
6786
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006787 // Compile the target function. Here we compile using CompileLazyInLoop in
6788 // order to get the optimized version. This helps code like delta-blue
6789 // that calls performance-critical routines through constructors. A
6790 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
6791 // direct call. Since the in-loop tracking takes place through CallICs
6792 // this means that things called through constructors are never known to
6793 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006794 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006795 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006796 return Failure::Exception();
6797 }
6798
6799 return function->code();
6800}
6801
6802
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006803static Object* Runtime_GetFunctionDelegate(Arguments args) {
6804 HandleScope scope;
6805 ASSERT(args.length() == 1);
6806 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6807 return *Execution::GetFunctionDelegate(args.at<Object>(0));
6808}
6809
6810
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00006811static Object* Runtime_GetConstructorDelegate(Arguments args) {
6812 HandleScope scope;
6813 ASSERT(args.length() == 1);
6814 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6815 return *Execution::GetConstructorDelegate(args.at<Object>(0));
6816}
6817
6818
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006819static Object* Runtime_NewContext(Arguments args) {
6820 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00006821 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006822
kasper.lund7276f142008-07-30 08:49:36 +00006823 CONVERT_CHECKED(JSFunction, function, args[0]);
ager@chromium.orgb5737492010-07-15 09:29:43 +00006824 int length = function->shared()->scope_info()->NumberOfContextSlots();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006825 Object* result = Heap::AllocateFunctionContext(length, function);
6826 if (result->IsFailure()) return result;
6827
6828 Top::set_context(Context::cast(result));
6829
kasper.lund7276f142008-07-30 08:49:36 +00006830 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006831}
6832
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006833static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006834 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006835 Object* js_object = object;
6836 if (!js_object->IsJSObject()) {
6837 js_object = js_object->ToObject();
6838 if (js_object->IsFailure()) {
6839 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006840 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006841 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006842 Handle<Object> result =
6843 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
6844 return Top::Throw(*result);
6845 }
6846 }
6847
6848 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006849 Heap::AllocateWithContext(Top::context(),
6850 JSObject::cast(js_object),
6851 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006852 if (result->IsFailure()) return result;
6853
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006854 Context* context = Context::cast(result);
6855 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006856
kasper.lund7276f142008-07-30 08:49:36 +00006857 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006858}
6859
6860
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006861static Object* Runtime_PushContext(Arguments args) {
6862 NoHandleAllocation ha;
6863 ASSERT(args.length() == 1);
6864 return PushContextHelper(args[0], false);
6865}
6866
6867
6868static Object* Runtime_PushCatchContext(Arguments args) {
6869 NoHandleAllocation ha;
6870 ASSERT(args.length() == 1);
6871 return PushContextHelper(args[0], true);
6872}
6873
6874
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006875static Object* Runtime_LookupContext(Arguments args) {
6876 HandleScope scope;
6877 ASSERT(args.length() == 2);
6878
6879 CONVERT_ARG_CHECKED(Context, context, 0);
6880 CONVERT_ARG_CHECKED(String, name, 1);
6881
6882 int index;
6883 PropertyAttributes attributes;
6884 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006885 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006886 context->Lookup(name, flags, &index, &attributes);
6887
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006888 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006889 ASSERT(holder->IsJSObject());
6890 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006891 }
6892
6893 // No intermediate context found. Use global object by default.
6894 return Top::context()->global();
6895}
6896
6897
ager@chromium.orga1645e22009-09-09 19:27:10 +00006898// A mechanism to return a pair of Object pointers in registers (if possible).
6899// How this is achieved is calling convention-dependent.
6900// All currently supported x86 compiles uses calling conventions that are cdecl
6901// variants where a 64-bit value is returned in two 32-bit registers
6902// (edx:eax on ia32, r1:r0 on ARM).
6903// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
6904// In Win64 calling convention, a struct of two pointers is returned in memory,
6905// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006906#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006907struct ObjectPair {
6908 Object* x;
6909 Object* y;
6910};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006911
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006912static inline ObjectPair MakePair(Object* x, Object* y) {
6913 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006914 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
6915 // In Win64 they are assigned to a hidden first argument.
6916 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006917}
6918#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006919typedef uint64_t ObjectPair;
6920static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006921 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006922 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006923}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006924#endif
6925
6926
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006927static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006928 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
6929 USE(attributes);
6930 return x->IsTheHole() ? Heap::undefined_value() : x;
6931}
6932
6933
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006934static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
6935 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006936 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006937 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006938 JSFunction* context_extension_function =
6939 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006940 // If the holder isn't a context extension object, we just return it
6941 // as the receiver. This allows arguments objects to be used as
6942 // receivers, but only if they are put in the context scope chain
6943 // explicitly via a with-statement.
6944 Object* constructor = holder->map()->constructor();
6945 if (constructor != context_extension_function) return holder;
6946 // Fall back to using the global object as the receiver if the
6947 // property turns out to be a local variable allocated in a context
6948 // extension object - introduced via eval.
6949 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006950}
6951
6952
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006953static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006954 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00006955 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006956
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006957 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00006958 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006959 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006960 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006961 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006962
6963 int index;
6964 PropertyAttributes attributes;
6965 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006966 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006967 context->Lookup(name, flags, &index, &attributes);
6968
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006969 // If the index is non-negative, the slot has been found in a local
6970 // variable or a parameter. Read it from the context object or the
6971 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006972 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006973 // If the "property" we were looking for is a local variable or an
6974 // argument in a context, the receiver is the global object; see
6975 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
6976 JSObject* receiver = Top::context()->global()->global_receiver();
6977 Object* value = (holder->IsContext())
6978 ? Context::cast(*holder)->get(index)
6979 : JSObject::cast(*holder)->GetElement(index);
6980 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006981 }
6982
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006983 // If the holder is found, we read the property from it.
6984 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006985 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006986 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006987 JSObject* receiver;
6988 if (object->IsGlobalObject()) {
6989 receiver = GlobalObject::cast(object)->global_receiver();
6990 } else if (context->is_exception_holder(*holder)) {
6991 receiver = Top::context()->global()->global_receiver();
6992 } else {
6993 receiver = ComputeReceiverForNonGlobal(object);
6994 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006995 // No need to unhole the value here. This is taken care of by the
6996 // GetProperty function.
6997 Object* value = object->GetProperty(*name);
6998 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006999 }
7000
7001 if (throw_error) {
7002 // The property doesn't exist - throw exception.
7003 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007004 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007005 return MakePair(Top::Throw(*reference_error), NULL);
7006 } else {
7007 // The property doesn't exist - return undefined
7008 return MakePair(Heap::undefined_value(), Heap::undefined_value());
7009 }
7010}
7011
7012
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007013static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007014 return LoadContextSlotHelper(args, true);
7015}
7016
7017
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007018static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007019 return LoadContextSlotHelper(args, false);
7020}
7021
7022
7023static Object* Runtime_StoreContextSlot(Arguments args) {
7024 HandleScope scope;
7025 ASSERT(args.length() == 3);
7026
7027 Handle<Object> value(args[0]);
7028 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007029 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007030
7031 int index;
7032 PropertyAttributes attributes;
7033 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007034 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007035 context->Lookup(name, flags, &index, &attributes);
7036
7037 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007038 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007039 // Ignore if read_only variable.
7040 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007041 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007042 }
7043 } else {
7044 ASSERT((attributes & READ_ONLY) == 0);
7045 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007046 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007047 USE(result);
7048 ASSERT(!result->IsFailure());
7049 }
7050 return *value;
7051 }
7052
7053 // Slow case: The property is not in a FixedArray context.
7054 // It is either in an JSObject extension context or it was not found.
7055 Handle<JSObject> context_ext;
7056
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007057 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007058 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007059 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007060 } else {
7061 // The property was not found. It needs to be stored in the global context.
7062 ASSERT(attributes == ABSENT);
7063 attributes = NONE;
7064 context_ext = Handle<JSObject>(Top::context()->global());
7065 }
7066
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007067 // Set the property, but ignore if read_only variable on the context
7068 // extension object itself.
7069 if ((attributes & READ_ONLY) == 0 ||
7070 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007071 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
7072 if (set.is_null()) {
7073 // Failure::Exception is converted to a null handle in the
7074 // handle-based methods such as SetProperty. We therefore need
7075 // to convert null handles back to exceptions.
7076 ASSERT(Top::has_pending_exception());
7077 return Failure::Exception();
7078 }
7079 }
7080 return *value;
7081}
7082
7083
7084static Object* Runtime_Throw(Arguments args) {
7085 HandleScope scope;
7086 ASSERT(args.length() == 1);
7087
7088 return Top::Throw(args[0]);
7089}
7090
7091
7092static Object* Runtime_ReThrow(Arguments args) {
7093 HandleScope scope;
7094 ASSERT(args.length() == 1);
7095
7096 return Top::ReThrow(args[0]);
7097}
7098
7099
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007100static Object* Runtime_PromoteScheduledException(Arguments args) {
7101 ASSERT_EQ(0, args.length());
7102 return Top::PromoteScheduledException();
7103}
7104
7105
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007106static Object* Runtime_ThrowReferenceError(Arguments args) {
7107 HandleScope scope;
7108 ASSERT(args.length() == 1);
7109
7110 Handle<Object> name(args[0]);
7111 Handle<Object> reference_error =
7112 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
7113 return Top::Throw(*reference_error);
7114}
7115
7116
7117static Object* Runtime_StackOverflow(Arguments args) {
7118 NoHandleAllocation na;
7119 return Top::StackOverflow();
7120}
7121
7122
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007123static Object* Runtime_StackGuard(Arguments args) {
7124 ASSERT(args.length() == 1);
7125
7126 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007127 if (StackGuard::IsStackOverflow()) {
7128 return Runtime_StackOverflow(args);
7129 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007130
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007131 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007132}
7133
7134
7135// NOTE: These PrintXXX functions are defined for all builds (not just
7136// DEBUG builds) because we may want to be able to trace function
7137// calls in all modes.
7138static void PrintString(String* str) {
7139 // not uncommon to have empty strings
7140 if (str->length() > 0) {
7141 SmartPointer<char> s =
7142 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
7143 PrintF("%s", *s);
7144 }
7145}
7146
7147
7148static void PrintObject(Object* obj) {
7149 if (obj->IsSmi()) {
7150 PrintF("%d", Smi::cast(obj)->value());
7151 } else if (obj->IsString() || obj->IsSymbol()) {
7152 PrintString(String::cast(obj));
7153 } else if (obj->IsNumber()) {
7154 PrintF("%g", obj->Number());
7155 } else if (obj->IsFailure()) {
7156 PrintF("<failure>");
7157 } else if (obj->IsUndefined()) {
7158 PrintF("<undefined>");
7159 } else if (obj->IsNull()) {
7160 PrintF("<null>");
7161 } else if (obj->IsTrue()) {
7162 PrintF("<true>");
7163 } else if (obj->IsFalse()) {
7164 PrintF("<false>");
7165 } else {
7166 PrintF("%p", obj);
7167 }
7168}
7169
7170
7171static int StackSize() {
7172 int n = 0;
7173 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
7174 return n;
7175}
7176
7177
7178static void PrintTransition(Object* result) {
7179 // indentation
7180 { const int nmax = 80;
7181 int n = StackSize();
7182 if (n <= nmax)
7183 PrintF("%4d:%*s", n, n, "");
7184 else
7185 PrintF("%4d:%*s", n, nmax, "...");
7186 }
7187
7188 if (result == NULL) {
7189 // constructor calls
7190 JavaScriptFrameIterator it;
7191 JavaScriptFrame* frame = it.frame();
7192 if (frame->IsConstructor()) PrintF("new ");
7193 // function name
7194 Object* fun = frame->function();
7195 if (fun->IsJSFunction()) {
7196 PrintObject(JSFunction::cast(fun)->shared()->name());
7197 } else {
7198 PrintObject(fun);
7199 }
7200 // function arguments
7201 // (we are intentionally only printing the actually
7202 // supplied parameters, not all parameters required)
7203 PrintF("(this=");
7204 PrintObject(frame->receiver());
7205 const int length = frame->GetProvidedParametersCount();
7206 for (int i = 0; i < length; i++) {
7207 PrintF(", ");
7208 PrintObject(frame->GetParameter(i));
7209 }
7210 PrintF(") {\n");
7211
7212 } else {
7213 // function result
7214 PrintF("} -> ");
7215 PrintObject(result);
7216 PrintF("\n");
7217 }
7218}
7219
7220
7221static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007222 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007223 NoHandleAllocation ha;
7224 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007225 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007226}
7227
7228
7229static Object* Runtime_TraceExit(Arguments args) {
7230 NoHandleAllocation ha;
7231 PrintTransition(args[0]);
7232 return args[0]; // return TOS
7233}
7234
7235
7236static Object* Runtime_DebugPrint(Arguments args) {
7237 NoHandleAllocation ha;
7238 ASSERT(args.length() == 1);
7239
7240#ifdef DEBUG
7241 if (args[0]->IsString()) {
7242 // If we have a string, assume it's a code "marker"
7243 // and print some interesting cpu debugging info.
7244 JavaScriptFrameIterator it;
7245 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007246 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
7247 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007248 } else {
7249 PrintF("DebugPrint: ");
7250 }
7251 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007252 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007253 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007254 HeapObject::cast(args[0])->map()->Print();
7255 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007256#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007257 // ShortPrint is available in release mode. Print is not.
7258 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007259#endif
7260 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00007261 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007262
7263 return args[0]; // return TOS
7264}
7265
7266
7267static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007268 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007269 NoHandleAllocation ha;
7270 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007271 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007272}
7273
7274
mads.s.ager31e71382008-08-13 09:32:07 +00007275static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007276 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007277 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007278
7279 // According to ECMA-262, section 15.9.1, page 117, the precision of
7280 // the number in a Date object representing a particular instant in
7281 // time is milliseconds. Therefore, we floor the result of getting
7282 // the OS time.
7283 double millis = floor(OS::TimeCurrentMillis());
7284 return Heap::NumberFromDouble(millis);
7285}
7286
7287
7288static Object* Runtime_DateParseString(Arguments args) {
7289 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007290 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007291
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007292 CONVERT_ARG_CHECKED(String, str, 0);
7293 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007294
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007295 CONVERT_ARG_CHECKED(JSArray, output, 1);
7296 RUNTIME_ASSERT(output->HasFastElements());
7297
7298 AssertNoAllocation no_allocation;
7299
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007300 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007301 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
7302 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007303 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007304 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007305 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007306 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007307 result = DateParser::Parse(str->ToUC16Vector(), output_array);
7308 }
7309
7310 if (result) {
7311 return *output;
7312 } else {
7313 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007314 }
7315}
7316
7317
7318static Object* Runtime_DateLocalTimezone(Arguments args) {
7319 NoHandleAllocation ha;
7320 ASSERT(args.length() == 1);
7321
7322 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007323 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007324 return Heap::AllocateStringFromUtf8(CStrVector(zone));
7325}
7326
7327
7328static Object* Runtime_DateLocalTimeOffset(Arguments args) {
7329 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007330 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007331
7332 return Heap::NumberFromDouble(OS::LocalTimeOffset());
7333}
7334
7335
7336static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
7337 NoHandleAllocation ha;
7338 ASSERT(args.length() == 1);
7339
7340 CONVERT_DOUBLE_CHECKED(x, args[0]);
7341 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
7342}
7343
7344
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007345static Object* Runtime_GlobalReceiver(Arguments args) {
7346 ASSERT(args.length() == 1);
7347 Object* global = args[0];
7348 if (!global->IsJSGlobalObject()) return Heap::null_value();
7349 return JSGlobalObject::cast(global)->global_receiver();
7350}
7351
7352
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007353static Object* Runtime_CompileString(Arguments args) {
7354 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007355 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007356 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007357 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007358
ager@chromium.org381abbb2009-02-25 13:23:22 +00007359 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007360 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007361 Compiler::ValidationState validate = (is_json->IsTrue())
7362 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007363 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
7364 context,
7365 true,
7366 validate);
7367 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007368 Handle<JSFunction> fun =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007369 Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007370 return *fun;
7371}
7372
7373
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007374static ObjectPair CompileGlobalEval(Handle<String> source,
7375 Handle<Object> receiver) {
7376 // Deal with a normal eval call with a string argument. Compile it
7377 // and return the compiled function bound in the local context.
7378 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
7379 source,
7380 Handle<Context>(Top::context()),
7381 Top::context()->IsGlobalContext(),
7382 Compiler::DONT_VALIDATE_JSON);
7383 if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
7384 Handle<JSFunction> compiled = Factory::NewFunctionFromSharedFunctionInfo(
7385 shared,
7386 Handle<Context>(Top::context()),
7387 NOT_TENURED);
7388 return MakePair(*compiled, *receiver);
7389}
7390
7391
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007392static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
7393 ASSERT(args.length() == 3);
7394 if (!args[0]->IsJSFunction()) {
7395 return MakePair(Top::ThrowIllegalOperation(), NULL);
7396 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007397
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007398 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007399 Handle<JSFunction> callee = args.at<JSFunction>(0);
7400 Handle<Object> receiver; // Will be overwritten.
7401
7402 // Compute the calling context.
7403 Handle<Context> context = Handle<Context>(Top::context());
7404#ifdef DEBUG
7405 // Make sure Top::context() agrees with the old code that traversed
7406 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007407 StackFrameLocator locator;
7408 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007409 ASSERT(Context::cast(frame->context()) == *context);
7410#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007411
7412 // Find where the 'eval' symbol is bound. It is unaliased only if
7413 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007414 int index = -1;
7415 PropertyAttributes attributes = ABSENT;
7416 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007417 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
7418 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007419 // Stop search when eval is found or when the global context is
7420 // reached.
7421 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007422 if (context->is_function_context()) {
7423 context = Handle<Context>(Context::cast(context->closure()->context()));
7424 } else {
7425 context = Handle<Context>(context->previous());
7426 }
7427 }
7428
iposva@chromium.org245aa852009-02-10 00:49:54 +00007429 // If eval could not be resolved, it has been deleted and we need to
7430 // throw a reference error.
7431 if (attributes == ABSENT) {
7432 Handle<Object> name = Factory::eval_symbol();
7433 Handle<Object> reference_error =
7434 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007435 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007436 }
7437
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007438 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007439 // 'eval' is not bound in the global context. Just call the function
7440 // with the given arguments. This is not necessarily the global eval.
7441 if (receiver->IsContext()) {
7442 context = Handle<Context>::cast(receiver);
7443 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007444 } else if (receiver->IsJSContextExtensionObject()) {
7445 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007446 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007447 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007448 }
7449
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007450 // 'eval' is bound in the global context, but it may have been overwritten.
7451 // Compare it to the builtin 'GlobalEval' function to make sure.
7452 if (*callee != Top::global_context()->global_eval_fun() ||
7453 !args[1]->IsString()) {
7454 return MakePair(*callee, Top::context()->global()->global_receiver());
7455 }
7456
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007457 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
7458}
7459
7460
7461static ObjectPair Runtime_ResolvePossiblyDirectEvalNoLookup(Arguments args) {
7462 ASSERT(args.length() == 3);
7463 if (!args[0]->IsJSFunction()) {
7464 return MakePair(Top::ThrowIllegalOperation(), NULL);
7465 }
7466
7467 HandleScope scope;
7468 Handle<JSFunction> callee = args.at<JSFunction>(0);
7469
7470 // 'eval' is bound in the global context, but it may have been overwritten.
7471 // Compare it to the builtin 'GlobalEval' function to make sure.
7472 if (*callee != Top::global_context()->global_eval_fun() ||
7473 !args[1]->IsString()) {
7474 return MakePair(*callee, Top::context()->global()->global_receiver());
7475 }
7476
7477 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007478}
7479
7480
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007481static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
7482 // This utility adjusts the property attributes for newly created Function
7483 // object ("new Function(...)") by changing the map.
7484 // All it does is changing the prototype property to enumerable
7485 // as specified in ECMA262, 15.3.5.2.
7486 HandleScope scope;
7487 ASSERT(args.length() == 1);
7488 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7489 ASSERT(func->map()->instance_type() ==
7490 Top::function_instance_map()->instance_type());
7491 ASSERT(func->map()->instance_size() ==
7492 Top::function_instance_map()->instance_size());
7493 func->set_map(*Top::function_instance_map());
7494 return *func;
7495}
7496
7497
lrn@chromium.orgc4e51ac2010-08-09 09:47:21 +00007498static Object* Runtime_AllocateInNewSpace(Arguments args) {
7499 // Allocate a block of memory in NewSpace (filled with a filler).
7500 // Use as fallback for allocation in generated code when NewSpace
7501 // is full.
7502 ASSERT(args.length() == 1);
7503 CONVERT_ARG_CHECKED(Smi, size_smi, 0);
7504 int size = size_smi->value();
7505 RUNTIME_ASSERT(IsAligned(size, kPointerSize));
7506 RUNTIME_ASSERT(size > 0);
7507 static const int kMinFreeNewSpaceAfterGC =
7508 Heap::InitialSemiSpaceSize() * 3/4;
7509 RUNTIME_ASSERT(size <= kMinFreeNewSpaceAfterGC);
7510 Object* allocation = Heap::new_space()->AllocateRaw(size);
7511 if (!allocation->IsFailure()) {
7512 Heap::CreateFillerObjectAt(HeapObject::cast(allocation)->address(), size);
7513 }
7514 return allocation;
7515}
7516
7517
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007518// Push an array unto an array of arrays if it is not already in the
7519// array. Returns true if the element was pushed on the stack and
7520// false otherwise.
7521static Object* Runtime_PushIfAbsent(Arguments args) {
7522 ASSERT(args.length() == 2);
7523 CONVERT_CHECKED(JSArray, array, args[0]);
7524 CONVERT_CHECKED(JSArray, element, args[1]);
7525 RUNTIME_ASSERT(array->HasFastElements());
7526 int length = Smi::cast(array->length())->value();
7527 FixedArray* elements = FixedArray::cast(array->elements());
7528 for (int i = 0; i < length; i++) {
7529 if (elements->get(i) == element) return Heap::false_value();
7530 }
7531 Object* obj = array->SetFastElement(length, element);
7532 if (obj->IsFailure()) return obj;
7533 return Heap::true_value();
7534}
7535
7536
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007537/**
7538 * A simple visitor visits every element of Array's.
7539 * The backend storage can be a fixed array for fast elements case,
7540 * or a dictionary for sparse array. Since Dictionary is a subtype
7541 * of FixedArray, the class can be used by both fast and slow cases.
7542 * The second parameter of the constructor, fast_elements, specifies
7543 * whether the storage is a FixedArray or Dictionary.
7544 *
7545 * An index limit is used to deal with the situation that a result array
7546 * length overflows 32-bit non-negative integer.
7547 */
7548class ArrayConcatVisitor {
7549 public:
7550 ArrayConcatVisitor(Handle<FixedArray> storage,
7551 uint32_t index_limit,
7552 bool fast_elements) :
7553 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007554 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007555
7556 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007557 if (i >= index_limit_ - index_offset_) return;
7558 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007559
7560 if (fast_elements_) {
7561 ASSERT(index < static_cast<uint32_t>(storage_->length()));
7562 storage_->set(index, *elm);
7563
7564 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007565 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
7566 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007567 Factory::DictionaryAtNumberPut(dict, index, elm);
7568 if (!result.is_identical_to(dict))
7569 storage_ = result;
7570 }
7571 }
7572
7573 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007574 if (index_limit_ - index_offset_ < delta) {
7575 index_offset_ = index_limit_;
7576 } else {
7577 index_offset_ += delta;
7578 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007579 }
7580
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007581 Handle<FixedArray> storage() { return storage_; }
7582
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007583 private:
7584 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007585 // Limit on the accepted indices. Elements with indices larger than the
7586 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007587 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007588 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007589 uint32_t index_offset_;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007590 const bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007591};
7592
7593
ager@chromium.org3811b432009-10-28 14:53:37 +00007594template<class ExternalArrayClass, class ElementType>
7595static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
7596 bool elements_are_ints,
7597 bool elements_are_guaranteed_smis,
7598 uint32_t range,
7599 ArrayConcatVisitor* visitor) {
7600 Handle<ExternalArrayClass> array(
7601 ExternalArrayClass::cast(receiver->elements()));
7602 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
7603
7604 if (visitor != NULL) {
7605 if (elements_are_ints) {
7606 if (elements_are_guaranteed_smis) {
7607 for (uint32_t j = 0; j < len; j++) {
7608 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
7609 visitor->visit(j, e);
7610 }
7611 } else {
7612 for (uint32_t j = 0; j < len; j++) {
7613 int64_t val = static_cast<int64_t>(array->get(j));
7614 if (Smi::IsValid(static_cast<intptr_t>(val))) {
7615 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
7616 visitor->visit(j, e);
7617 } else {
7618 Handle<Object> e(
7619 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
7620 visitor->visit(j, e);
7621 }
7622 }
7623 }
7624 } else {
7625 for (uint32_t j = 0; j < len; j++) {
7626 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
7627 visitor->visit(j, e);
7628 }
7629 }
7630 }
7631
7632 return len;
7633}
7634
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007635/**
7636 * A helper function that visits elements of a JSObject. Only elements
7637 * whose index between 0 and range (exclusive) are visited.
7638 *
7639 * If the third parameter, visitor, is not NULL, the visitor is called
7640 * with parameters, 'visitor_index_offset + element index' and the element.
7641 *
7642 * It returns the number of visisted elements.
7643 */
7644static uint32_t IterateElements(Handle<JSObject> receiver,
7645 uint32_t range,
7646 ArrayConcatVisitor* visitor) {
7647 uint32_t num_of_elements = 0;
7648
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007649 switch (receiver->GetElementsKind()) {
7650 case JSObject::FAST_ELEMENTS: {
7651 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
7652 uint32_t len = elements->length();
7653 if (range < len) {
7654 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007655 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007656
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007657 for (uint32_t j = 0; j < len; j++) {
7658 Handle<Object> e(elements->get(j));
7659 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007660 num_of_elements++;
7661 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007662 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007663 }
7664 }
7665 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007666 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007667 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007668 case JSObject::PIXEL_ELEMENTS: {
7669 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
7670 uint32_t len = pixels->length();
7671 if (range < len) {
7672 len = range;
7673 }
7674
7675 for (uint32_t j = 0; j < len; j++) {
7676 num_of_elements++;
7677 if (visitor != NULL) {
7678 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
7679 visitor->visit(j, e);
7680 }
7681 }
7682 break;
7683 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007684 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
7685 num_of_elements =
7686 IterateExternalArrayElements<ExternalByteArray, int8_t>(
7687 receiver, true, true, range, visitor);
7688 break;
7689 }
7690 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7691 num_of_elements =
7692 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
7693 receiver, true, true, range, visitor);
7694 break;
7695 }
7696 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
7697 num_of_elements =
7698 IterateExternalArrayElements<ExternalShortArray, int16_t>(
7699 receiver, true, true, range, visitor);
7700 break;
7701 }
7702 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7703 num_of_elements =
7704 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
7705 receiver, true, true, range, visitor);
7706 break;
7707 }
7708 case JSObject::EXTERNAL_INT_ELEMENTS: {
7709 num_of_elements =
7710 IterateExternalArrayElements<ExternalIntArray, int32_t>(
7711 receiver, true, false, range, visitor);
7712 break;
7713 }
7714 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7715 num_of_elements =
7716 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
7717 receiver, true, false, range, visitor);
7718 break;
7719 }
7720 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
7721 num_of_elements =
7722 IterateExternalArrayElements<ExternalFloatArray, float>(
7723 receiver, false, false, range, visitor);
7724 break;
7725 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007726 case JSObject::DICTIONARY_ELEMENTS: {
7727 Handle<NumberDictionary> dict(receiver->element_dictionary());
7728 uint32_t capacity = dict->Capacity();
7729 for (uint32_t j = 0; j < capacity; j++) {
7730 Handle<Object> k(dict->KeyAt(j));
7731 if (dict->IsKey(*k)) {
7732 ASSERT(k->IsNumber());
7733 uint32_t index = static_cast<uint32_t>(k->Number());
7734 if (index < range) {
7735 num_of_elements++;
7736 if (visitor) {
7737 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
7738 }
7739 }
7740 }
7741 }
7742 break;
7743 }
7744 default:
7745 UNREACHABLE();
7746 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007747 }
7748
7749 return num_of_elements;
7750}
7751
7752
7753/**
7754 * A helper function that visits elements of an Array object, and elements
7755 * on its prototypes.
7756 *
7757 * Elements on prototypes are visited first, and only elements whose indices
7758 * less than Array length are visited.
7759 *
7760 * If a ArrayConcatVisitor object is given, the visitor is called with
7761 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007762 *
7763 * The returned number of elements is an upper bound on the actual number
7764 * of elements added. If the same element occurs in more than one object
7765 * in the array's prototype chain, it will be counted more than once, but
7766 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007767 */
7768static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
7769 ArrayConcatVisitor* visitor) {
7770 uint32_t range = static_cast<uint32_t>(array->length()->Number());
7771 Handle<Object> obj = array;
7772
7773 static const int kEstimatedPrototypes = 3;
7774 List< Handle<JSObject> > objects(kEstimatedPrototypes);
7775
7776 // Visit prototype first. If an element on the prototype is shadowed by
7777 // the inheritor using the same index, the ArrayConcatVisitor visits
7778 // the prototype element before the shadowing element.
7779 // The visitor can simply overwrite the old value by new value using
7780 // the same index. This follows Array::concat semantics.
7781 while (!obj->IsNull()) {
7782 objects.Add(Handle<JSObject>::cast(obj));
7783 obj = Handle<Object>(obj->GetPrototype());
7784 }
7785
7786 uint32_t nof_elements = 0;
7787 for (int i = objects.length() - 1; i >= 0; i--) {
7788 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007789 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007790 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007791
7792 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
7793 nof_elements = JSObject::kMaxElementCount;
7794 } else {
7795 nof_elements += encountered_elements;
7796 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007797 }
7798
7799 return nof_elements;
7800}
7801
7802
7803/**
7804 * A helper function of Runtime_ArrayConcat.
7805 *
7806 * The first argument is an Array of arrays and objects. It is the
7807 * same as the arguments array of Array::concat JS function.
7808 *
7809 * If an argument is an Array object, the function visits array
7810 * elements. If an argument is not an Array object, the function
7811 * visits the object as if it is an one-element array.
7812 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007813 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007814 * non-negative number is used as new length. For example, if one
7815 * array length is 2^32 - 1, second array length is 1, the
7816 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007817 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
7818 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007819 */
7820static uint32_t IterateArguments(Handle<JSArray> arguments,
7821 ArrayConcatVisitor* visitor) {
7822 uint32_t visited_elements = 0;
7823 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7824
7825 for (uint32_t i = 0; i < num_of_args; i++) {
7826 Handle<Object> obj(arguments->GetElement(i));
7827 if (obj->IsJSArray()) {
7828 Handle<JSArray> array = Handle<JSArray>::cast(obj);
7829 uint32_t len = static_cast<uint32_t>(array->length()->Number());
7830 uint32_t nof_elements =
7831 IterateArrayAndPrototypeElements(array, visitor);
7832 // Total elements of array and its prototype chain can be more than
7833 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007834 // the array length number of elements. We use the length as an estimate
7835 // for the actual number of elements added.
7836 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
7837 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
7838 visited_elements = JSArray::kMaxElementCount;
7839 } else {
7840 visited_elements += added_elements;
7841 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007842 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007843 } else {
7844 if (visitor) {
7845 visitor->visit(0, obj);
7846 visitor->increase_index_offset(1);
7847 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007848 if (visited_elements < JSArray::kMaxElementCount) {
7849 visited_elements++;
7850 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007851 }
7852 }
7853 return visited_elements;
7854}
7855
7856
7857/**
7858 * Array::concat implementation.
7859 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007860 * TODO(lrn): Fix non-compliance for very large concatenations and update to
7861 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007862 */
7863static Object* Runtime_ArrayConcat(Arguments args) {
7864 ASSERT(args.length() == 1);
7865 HandleScope handle_scope;
7866
7867 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
7868 Handle<JSArray> arguments(arg_arrays);
7869
7870 // Pass 1: estimate the number of elements of the result
7871 // (it could be more than real numbers if prototype has elements).
7872 uint32_t result_length = 0;
7873 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7874
7875 { AssertNoAllocation nogc;
7876 for (uint32_t i = 0; i < num_of_args; i++) {
7877 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007878 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007879 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007880 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007881 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
7882 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007883 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007884 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007885 if (JSObject::kMaxElementCount - result_length < length_estimate) {
7886 result_length = JSObject::kMaxElementCount;
7887 break;
7888 }
7889 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007890 }
7891 }
7892
7893 // Allocate an empty array, will set length and content later.
7894 Handle<JSArray> result = Factory::NewJSArray(0);
7895
7896 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
7897 // If estimated number of elements is more than half of length, a
7898 // fixed array (fast case) is more time and space-efficient than a
7899 // dictionary.
7900 bool fast_case = (estimate_nof_elements * 2) >= result_length;
7901
7902 Handle<FixedArray> storage;
7903 if (fast_case) {
7904 // The backing storage array must have non-existing elements to
7905 // preserve holes across concat operations.
7906 storage = Factory::NewFixedArrayWithHoles(result_length);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007907 result->set_map(*Factory::GetFastElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007908 } else {
7909 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
7910 uint32_t at_least_space_for = estimate_nof_elements +
7911 (estimate_nof_elements >> 2);
7912 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007913 Factory::NewNumberDictionary(at_least_space_for));
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007914 result->set_map(*Factory::GetSlowElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007915 }
7916
7917 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
7918
7919 ArrayConcatVisitor visitor(storage, result_length, fast_case);
7920
7921 IterateArguments(arguments, &visitor);
7922
7923 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007924 // Please note the storage might have changed in the visitor.
7925 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007926
7927 return *result;
7928}
7929
7930
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007931// This will not allocate (flatten the string), but it may run
7932// very slowly for very deeply nested ConsStrings. For debugging use only.
7933static Object* Runtime_GlobalPrint(Arguments args) {
7934 NoHandleAllocation ha;
7935 ASSERT(args.length() == 1);
7936
7937 CONVERT_CHECKED(String, string, args[0]);
7938 StringInputBuffer buffer(string);
7939 while (buffer.has_more()) {
7940 uint16_t character = buffer.GetNext();
7941 PrintF("%c", character);
7942 }
7943 return string;
7944}
7945
ager@chromium.org5ec48922009-05-05 07:25:34 +00007946// Moves all own elements of an object, that are below a limit, to positions
7947// starting at zero. All undefined values are placed after non-undefined values,
7948// and are followed by non-existing element. Does not change the length
7949// property.
7950// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007951static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007952 ASSERT(args.length() == 2);
7953 CONVERT_CHECKED(JSObject, object, args[0]);
7954 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
7955 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007956}
7957
7958
7959// Move contents of argument 0 (an array) to argument 1 (an array)
7960static Object* Runtime_MoveArrayContents(Arguments args) {
7961 ASSERT(args.length() == 2);
7962 CONVERT_CHECKED(JSArray, from, args[0]);
7963 CONVERT_CHECKED(JSArray, to, args[1]);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007964 HeapObject* new_elements = from->elements();
7965 Object* new_map;
ricow@chromium.org0b9f8502010-08-18 07:45:01 +00007966 if (new_elements->map() == Heap::fixed_array_map() ||
7967 new_elements->map() == Heap::fixed_cow_array_map()) {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007968 new_map = to->map()->GetFastElementsMap();
7969 } else {
7970 new_map = to->map()->GetSlowElementsMap();
7971 }
7972 if (new_map->IsFailure()) return new_map;
7973 to->set_map(Map::cast(new_map));
7974 to->set_elements(new_elements);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007975 to->set_length(from->length());
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007976 Object* obj = from->ResetElements();
7977 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007978 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007979 return to;
7980}
7981
7982
7983// How many elements does this array have?
7984static Object* Runtime_EstimateNumberOfElements(Arguments args) {
7985 ASSERT(args.length() == 1);
7986 CONVERT_CHECKED(JSArray, array, args[0]);
7987 HeapObject* elements = array->elements();
7988 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007989 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007990 } else {
7991 return array->length();
7992 }
7993}
7994
7995
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007996static Object* Runtime_SwapElements(Arguments args) {
7997 HandleScope handle_scope;
7998
7999 ASSERT_EQ(3, args.length());
8000
ager@chromium.orgac091b72010-05-05 07:34:42 +00008001 CONVERT_ARG_CHECKED(JSObject, object, 0);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008002 Handle<Object> key1 = args.at<Object>(1);
8003 Handle<Object> key2 = args.at<Object>(2);
8004
8005 uint32_t index1, index2;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008006 if (!key1->ToArrayIndex(&index1)
8007 || !key2->ToArrayIndex(&index2)) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00008008 return Top::ThrowIllegalOperation();
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008009 }
8010
ager@chromium.orgac091b72010-05-05 07:34:42 +00008011 Handle<JSObject> jsobject = Handle<JSObject>::cast(object);
8012 Handle<Object> tmp1 = GetElement(jsobject, index1);
8013 Handle<Object> tmp2 = GetElement(jsobject, index2);
8014
8015 SetElement(jsobject, index1, tmp2);
8016 SetElement(jsobject, index2, tmp1);
8017
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008018 return Heap::undefined_value();
8019}
8020
8021
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008022// Returns an array that tells you where in the [0, length) interval an array
8023// might have elements. Can either return keys or intervals. Keys can have
8024// gaps in (undefined). Intervals can also span over some undefined keys.
8025static Object* Runtime_GetArrayKeys(Arguments args) {
8026 ASSERT(args.length() == 2);
8027 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00008028 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008029 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008030 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008031 // Create an array and get all the keys into it, then remove all the
8032 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008033 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008034 int keys_length = keys->length();
8035 for (int i = 0; i < keys_length; i++) {
8036 Object* key = keys->get(i);
8037 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008038 if (!key->ToArrayIndex(&index) || index >= length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008039 // Zap invalid keys.
8040 keys->set_undefined(i);
8041 }
8042 }
8043 return *Factory::NewJSArrayWithElements(keys);
8044 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008045 ASSERT(array->HasFastElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008046 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
8047 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008048 single_interval->set(0, Smi::FromInt(-1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008049 uint32_t actual_length =
8050 static_cast<uint32_t>(FixedArray::cast(array->elements())->length());
ager@chromium.org5ec48922009-05-05 07:25:34 +00008051 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008052 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00008053 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008054 single_interval->set(1, *length_object);
8055 return *Factory::NewJSArrayWithElements(single_interval);
8056 }
8057}
8058
8059
8060// DefineAccessor takes an optional final argument which is the
8061// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
8062// to the way accessors are implemented, it is set for both the getter
8063// and setter on the first call to DefineAccessor and ignored on
8064// subsequent calls.
8065static Object* Runtime_DefineAccessor(Arguments args) {
8066 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
8067 // Compute attributes.
8068 PropertyAttributes attributes = NONE;
8069 if (args.length() == 5) {
8070 CONVERT_CHECKED(Smi, attrs, args[4]);
8071 int value = attrs->value();
8072 // Only attribute bits should be set.
8073 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
8074 attributes = static_cast<PropertyAttributes>(value);
8075 }
8076
8077 CONVERT_CHECKED(JSObject, obj, args[0]);
8078 CONVERT_CHECKED(String, name, args[1]);
8079 CONVERT_CHECKED(Smi, flag, args[2]);
8080 CONVERT_CHECKED(JSFunction, fun, args[3]);
8081 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
8082}
8083
8084
8085static Object* Runtime_LookupAccessor(Arguments args) {
8086 ASSERT(args.length() == 3);
8087 CONVERT_CHECKED(JSObject, obj, args[0]);
8088 CONVERT_CHECKED(String, name, args[1]);
8089 CONVERT_CHECKED(Smi, flag, args[2]);
8090 return obj->LookupAccessor(name, flag->value() == 0);
8091}
8092
8093
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008094#ifdef ENABLE_DEBUGGER_SUPPORT
8095static Object* Runtime_DebugBreak(Arguments args) {
8096 ASSERT(args.length() == 0);
8097 return Execution::DebugBreakHelper();
8098}
8099
8100
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008101// Helper functions for wrapping and unwrapping stack frame ids.
8102static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008103 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008104 return Smi::FromInt(id >> 2);
8105}
8106
8107
8108static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
8109 return static_cast<StackFrame::Id>(wrapped->value() << 2);
8110}
8111
8112
8113// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00008114// args[0]: debug event listener function to set or null or undefined for
8115// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008116// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00008117static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008118 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00008119 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
8120 args[0]->IsUndefined() ||
8121 args[0]->IsNull());
8122 Handle<Object> callback = args.at<Object>(0);
8123 Handle<Object> data = args.at<Object>(1);
8124 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008125
8126 return Heap::undefined_value();
8127}
8128
8129
8130static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00008131 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008132 StackGuard::DebugBreak();
8133 return Heap::undefined_value();
8134}
8135
8136
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008137static Object* DebugLookupResultValue(Object* receiver, String* name,
8138 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00008139 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008140 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008141 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008142 case NORMAL:
8143 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008144 if (value->IsTheHole()) {
8145 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008146 }
8147 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008148 case FIELD:
8149 value =
8150 JSObject::cast(
8151 result->holder())->FastPropertyAt(result->GetFieldIndex());
8152 if (value->IsTheHole()) {
8153 return Heap::undefined_value();
8154 }
8155 return value;
8156 case CONSTANT_FUNCTION:
8157 return result->GetConstantFunction();
8158 case CALLBACKS: {
8159 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008160 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008161 value = receiver->GetPropertyWithCallback(
8162 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00008163 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008164 value = Top::pending_exception();
8165 Top::clear_pending_exception();
8166 if (caught_exception != NULL) {
8167 *caught_exception = true;
8168 }
8169 }
8170 return value;
8171 } else {
8172 return Heap::undefined_value();
8173 }
8174 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008175 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008176 case MAP_TRANSITION:
8177 case CONSTANT_TRANSITION:
8178 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008179 return Heap::undefined_value();
8180 default:
8181 UNREACHABLE();
8182 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008183 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008184 return Heap::undefined_value();
8185}
8186
8187
ager@chromium.org32912102009-01-16 10:38:43 +00008188// Get debugger related details for an object property.
8189// args[0]: object holding property
8190// args[1]: name of the property
8191//
8192// The array returned contains the following information:
8193// 0: Property value
8194// 1: Property details
8195// 2: Property value is exception
8196// 3: Getter function if defined
8197// 4: Setter function if defined
8198// Items 2-4 are only filled if the property has either a getter or a setter
8199// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00008200static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008201 HandleScope scope;
8202
8203 ASSERT(args.length() == 2);
8204
8205 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8206 CONVERT_ARG_CHECKED(String, name, 1);
8207
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008208 // Make sure to set the current context to the context before the debugger was
8209 // entered (if the debugger is entered). The reason for switching context here
8210 // is that for some property lookups (accessors and interceptors) callbacks
8211 // into the embedding application can occour, and the embedding application
8212 // could have the assumption that its own global context is the current
8213 // context and not some internal debugger context.
8214 SaveContext save;
8215 if (Debug::InDebugger()) {
8216 Top::set_context(*Debug::debugger_entry()->GetContext());
8217 }
8218
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008219 // Skip the global proxy as it has no properties and always delegates to the
8220 // real global object.
8221 if (obj->IsJSGlobalProxy()) {
8222 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
8223 }
8224
8225
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008226 // Check if the name is trivially convertible to an index and get the element
8227 // if so.
8228 uint32_t index;
8229 if (name->AsArrayIndex(&index)) {
8230 Handle<FixedArray> details = Factory::NewFixedArray(2);
8231 details->set(0, Runtime::GetElementOrCharAt(obj, index));
8232 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
8233 return *Factory::NewJSArrayWithElements(details);
8234 }
8235
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008236 // Find the number of objects making up this.
8237 int length = LocalPrototypeChainLength(*obj);
8238
8239 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008240 Handle<JSObject> jsproto = obj;
8241 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008242 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008243 jsproto->LocalLookup(*name, &result);
8244 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008245 // LookupResult is not GC safe as it holds raw object pointers.
8246 // GC can happen later in this code so put the required fields into
8247 // local variables using handles when required for later use.
8248 PropertyType result_type = result.type();
8249 Handle<Object> result_callback_obj;
8250 if (result_type == CALLBACKS) {
8251 result_callback_obj = Handle<Object>(result.GetCallbackObject());
8252 }
8253 Smi* property_details = result.GetPropertyDetails().AsSmi();
8254 // DebugLookupResultValue can cause GC so details from LookupResult needs
8255 // to be copied to handles before this.
8256 bool caught_exception = false;
8257 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
8258 &caught_exception);
8259 if (raw_value->IsFailure()) return raw_value;
8260 Handle<Object> value(raw_value);
8261
8262 // If the callback object is a fixed array then it contains JavaScript
8263 // getter and/or setter.
8264 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
8265 result_callback_obj->IsFixedArray();
8266 Handle<FixedArray> details =
8267 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
8268 details->set(0, *value);
8269 details->set(1, property_details);
8270 if (hasJavaScriptAccessors) {
8271 details->set(2,
8272 caught_exception ? Heap::true_value()
8273 : Heap::false_value());
8274 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
8275 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
8276 }
8277
8278 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008279 }
8280 if (i < length - 1) {
8281 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
8282 }
8283 }
8284
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008285 return Heap::undefined_value();
8286}
8287
8288
8289static Object* Runtime_DebugGetProperty(Arguments args) {
8290 HandleScope scope;
8291
8292 ASSERT(args.length() == 2);
8293
8294 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8295 CONVERT_ARG_CHECKED(String, name, 1);
8296
8297 LookupResult result;
8298 obj->Lookup(*name, &result);
8299 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008300 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008301 }
8302 return Heap::undefined_value();
8303}
8304
8305
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008306// Return the property type calculated from the property details.
8307// args[0]: smi with property details.
8308static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
8309 ASSERT(args.length() == 1);
8310 CONVERT_CHECKED(Smi, details, args[0]);
8311 PropertyType type = PropertyDetails(details).type();
8312 return Smi::FromInt(static_cast<int>(type));
8313}
8314
8315
8316// Return the property attribute calculated from the property details.
8317// args[0]: smi with property details.
8318static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
8319 ASSERT(args.length() == 1);
8320 CONVERT_CHECKED(Smi, details, args[0]);
8321 PropertyAttributes attributes = PropertyDetails(details).attributes();
8322 return Smi::FromInt(static_cast<int>(attributes));
8323}
8324
8325
8326// Return the property insertion index calculated from the property details.
8327// args[0]: smi with property details.
8328static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
8329 ASSERT(args.length() == 1);
8330 CONVERT_CHECKED(Smi, details, args[0]);
8331 int index = PropertyDetails(details).index();
8332 return Smi::FromInt(index);
8333}
8334
8335
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008336// Return property value from named interceptor.
8337// args[0]: object
8338// args[1]: property name
8339static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
8340 HandleScope scope;
8341 ASSERT(args.length() == 2);
8342 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8343 RUNTIME_ASSERT(obj->HasNamedInterceptor());
8344 CONVERT_ARG_CHECKED(String, name, 1);
8345
8346 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008347 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008348}
8349
8350
8351// Return element value from indexed interceptor.
8352// args[0]: object
8353// args[1]: index
8354static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
8355 HandleScope scope;
8356 ASSERT(args.length() == 2);
8357 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8358 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
8359 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
8360
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008361 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008362}
8363
8364
8365static Object* Runtime_CheckExecutionState(Arguments args) {
8366 ASSERT(args.length() >= 1);
8367 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00008368 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008369 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008370 return Top::Throw(Heap::illegal_execution_state_symbol());
8371 }
8372
8373 return Heap::true_value();
8374}
8375
8376
8377static Object* Runtime_GetFrameCount(Arguments args) {
8378 HandleScope scope;
8379 ASSERT(args.length() == 1);
8380
8381 // Check arguments.
8382 Object* result = Runtime_CheckExecutionState(args);
8383 if (result->IsFailure()) return result;
8384
8385 // Count all frames which are relevant to debugging stack trace.
8386 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008387 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008388 if (id == StackFrame::NO_ID) {
8389 // If there is no JavaScript stack frame count is 0.
8390 return Smi::FromInt(0);
8391 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008392 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
8393 return Smi::FromInt(n);
8394}
8395
8396
8397static const int kFrameDetailsFrameIdIndex = 0;
8398static const int kFrameDetailsReceiverIndex = 1;
8399static const int kFrameDetailsFunctionIndex = 2;
8400static const int kFrameDetailsArgumentCountIndex = 3;
8401static const int kFrameDetailsLocalCountIndex = 4;
8402static const int kFrameDetailsSourcePositionIndex = 5;
8403static const int kFrameDetailsConstructCallIndex = 6;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008404static const int kFrameDetailsAtReturnIndex = 7;
8405static const int kFrameDetailsDebuggerFrameIndex = 8;
8406static const int kFrameDetailsFirstDynamicIndex = 9;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008407
8408// Return an array with frame details
8409// args[0]: number: break id
8410// args[1]: number: frame index
8411//
8412// The array returned contains the following information:
8413// 0: Frame id
8414// 1: Receiver
8415// 2: Function
8416// 3: Argument count
8417// 4: Local count
8418// 5: Source position
8419// 6: Constructor call
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008420// 7: Is at return
8421// 8: Debugger frame
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008422// Arguments name, value
8423// Locals name, value
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008424// Return value if any
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008425static Object* Runtime_GetFrameDetails(Arguments args) {
8426 HandleScope scope;
8427 ASSERT(args.length() == 2);
8428
8429 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008430 Object* check = Runtime_CheckExecutionState(args);
8431 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008432 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8433
8434 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008435 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008436 if (id == StackFrame::NO_ID) {
8437 // If there are no JavaScript stack frames return undefined.
8438 return Heap::undefined_value();
8439 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008440 int count = 0;
8441 JavaScriptFrameIterator it(id);
8442 for (; !it.done(); it.Advance()) {
8443 if (count == index) break;
8444 count++;
8445 }
8446 if (it.done()) return Heap::undefined_value();
8447
8448 // Traverse the saved contexts chain to find the active context for the
8449 // selected frame.
8450 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008451 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008452 save = save->prev();
8453 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008454 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008455
8456 // Get the frame id.
8457 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
8458
8459 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008460 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008461
8462 // Check for constructor frame.
8463 bool constructor = it.frame()->IsConstructor();
8464
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008465 // Get scope info and read from it for local variable information.
8466 Handle<JSFunction> function(JSFunction::cast(it.frame()->function()));
ager@chromium.orgb5737492010-07-15 09:29:43 +00008467 Handle<SerializedScopeInfo> scope_info(function->shared()->scope_info());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008468 ScopeInfo<> info(*scope_info);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008469
8470 // Get the context.
8471 Handle<Context> context(Context::cast(it.frame()->context()));
8472
8473 // Get the locals names and values into a temporary array.
8474 //
8475 // TODO(1240907): Hide compiler-introduced stack variables
8476 // (e.g. .result)? For users of the debugger, they will probably be
8477 // confusing.
8478 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
8479 for (int i = 0; i < info.NumberOfLocals(); i++) {
8480 // Name of the local.
8481 locals->set(i * 2, *info.LocalName(i));
8482
8483 // Fetch the value of the local - either from the stack or from a
8484 // heap-allocated context.
8485 if (i < info.number_of_stack_slots()) {
8486 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
8487 } else {
8488 Handle<String> name = info.LocalName(i);
8489 // Traverse the context chain to the function context as all local
8490 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008491 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008492 context = Handle<Context>(context->previous());
8493 }
8494 ASSERT(context->is_function_context());
8495 locals->set(i * 2 + 1,
ager@chromium.orgb5737492010-07-15 09:29:43 +00008496 context->get(scope_info->ContextSlotIndex(*name, NULL)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008497 }
8498 }
8499
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008500 // Check whether this frame is positioned at return.
8501 int at_return = (index == 0) ? Debug::IsBreakAtReturn(it.frame()) : false;
8502
8503 // If positioned just before return find the value to be returned and add it
8504 // to the frame information.
8505 Handle<Object> return_value = Factory::undefined_value();
8506 if (at_return) {
8507 StackFrameIterator it2;
8508 Address internal_frame_sp = NULL;
8509 while (!it2.done()) {
8510 if (it2.frame()->is_internal()) {
8511 internal_frame_sp = it2.frame()->sp();
8512 } else {
8513 if (it2.frame()->is_java_script()) {
8514 if (it2.frame()->id() == it.frame()->id()) {
8515 // The internal frame just before the JavaScript frame contains the
8516 // value to return on top. A debug break at return will create an
8517 // internal frame to store the return value (eax/rax/r0) before
8518 // entering the debug break exit frame.
8519 if (internal_frame_sp != NULL) {
8520 return_value =
8521 Handle<Object>(Memory::Object_at(internal_frame_sp));
8522 break;
8523 }
8524 }
8525 }
8526
8527 // Indicate that the previous frame was not an internal frame.
8528 internal_frame_sp = NULL;
8529 }
8530 it2.Advance();
8531 }
8532 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008533
8534 // Now advance to the arguments adapter frame (if any). It contains all
8535 // the provided parameters whereas the function frame always have the number
8536 // of arguments matching the functions parameters. The rest of the
8537 // information (except for what is collected above) is the same.
8538 it.AdvanceToArgumentsFrame();
8539
8540 // Find the number of arguments to fill. At least fill the number of
8541 // parameters for the function and fill more if more parameters are provided.
8542 int argument_count = info.number_of_parameters();
8543 if (argument_count < it.frame()->GetProvidedParametersCount()) {
8544 argument_count = it.frame()->GetProvidedParametersCount();
8545 }
8546
8547 // Calculate the size of the result.
8548 int details_size = kFrameDetailsFirstDynamicIndex +
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008549 2 * (argument_count + info.NumberOfLocals()) +
8550 (at_return ? 1 : 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008551 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8552
8553 // Add the frame id.
8554 details->set(kFrameDetailsFrameIdIndex, *frame_id);
8555
8556 // Add the function (same as in function frame).
8557 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
8558
8559 // Add the arguments count.
8560 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
8561
8562 // Add the locals count
8563 details->set(kFrameDetailsLocalCountIndex,
8564 Smi::FromInt(info.NumberOfLocals()));
8565
8566 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00008567 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008568 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
8569 } else {
8570 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
8571 }
8572
8573 // Add the constructor information.
8574 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
8575
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008576 // Add the at return information.
8577 details->set(kFrameDetailsAtReturnIndex, Heap::ToBoolean(at_return));
8578
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008579 // Add information on whether this frame is invoked in the debugger context.
8580 details->set(kFrameDetailsDebuggerFrameIndex,
8581 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
8582
8583 // Fill the dynamic part.
8584 int details_index = kFrameDetailsFirstDynamicIndex;
8585
8586 // Add arguments name and value.
8587 for (int i = 0; i < argument_count; i++) {
8588 // Name of the argument.
8589 if (i < info.number_of_parameters()) {
8590 details->set(details_index++, *info.parameter_name(i));
8591 } else {
8592 details->set(details_index++, Heap::undefined_value());
8593 }
8594
8595 // Parameter value.
8596 if (i < it.frame()->GetProvidedParametersCount()) {
8597 details->set(details_index++, it.frame()->GetParameter(i));
8598 } else {
8599 details->set(details_index++, Heap::undefined_value());
8600 }
8601 }
8602
8603 // Add locals name and value from the temporary copy from the function frame.
8604 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
8605 details->set(details_index++, locals->get(i));
8606 }
8607
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008608 // Add the value being returned.
8609 if (at_return) {
8610 details->set(details_index++, *return_value);
8611 }
8612
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008613 // Add the receiver (same as in function frame).
8614 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
8615 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
8616 Handle<Object> receiver(it.frame()->receiver());
8617 if (!receiver->IsJSObject()) {
8618 // If the receiver is NOT a JSObject we have hit an optimization
8619 // where a value object is not converted into a wrapped JS objects.
8620 // To hide this optimization from the debugger, we wrap the receiver
8621 // by creating correct wrapper object based on the calling frame's
8622 // global context.
8623 it.Advance();
8624 Handle<Context> calling_frames_global_context(
8625 Context::cast(Context::cast(it.frame()->context())->global_context()));
8626 receiver = Factory::ToObject(receiver, calling_frames_global_context);
8627 }
8628 details->set(kFrameDetailsReceiverIndex, *receiver);
8629
8630 ASSERT_EQ(details_size, details_index);
8631 return *Factory::NewJSArrayWithElements(details);
8632}
8633
8634
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008635// Copy all the context locals into an object used to materialize a scope.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008636static void CopyContextLocalsToScopeObject(
8637 Handle<SerializedScopeInfo> serialized_scope_info,
8638 ScopeInfo<>& scope_info,
8639 Handle<Context> context,
8640 Handle<JSObject> scope_object) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008641 // Fill all context locals to the context extension.
8642 for (int i = Context::MIN_CONTEXT_SLOTS;
8643 i < scope_info.number_of_context_slots();
8644 i++) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00008645 int context_index = serialized_scope_info->ContextSlotIndex(
8646 *scope_info.context_slot_name(i), NULL);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008647
8648 // Don't include the arguments shadow (.arguments) context variable.
8649 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
8650 SetProperty(scope_object,
8651 scope_info.context_slot_name(i),
8652 Handle<Object>(context->get(context_index)), NONE);
8653 }
8654 }
8655}
8656
8657
8658// Create a plain JSObject which materializes the local scope for the specified
8659// frame.
8660static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
8661 Handle<JSFunction> function(JSFunction::cast(frame->function()));
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008662 Handle<SharedFunctionInfo> shared(function->shared());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008663 Handle<SerializedScopeInfo> serialized_scope_info(shared->scope_info());
8664 ScopeInfo<> scope_info(*serialized_scope_info);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008665
8666 // Allocate and initialize a JSObject with all the arguments, stack locals
8667 // heap locals and extension properties of the debugged function.
8668 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
8669
8670 // First fill all parameters.
8671 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8672 SetProperty(local_scope,
8673 scope_info.parameter_name(i),
8674 Handle<Object>(frame->GetParameter(i)), NONE);
8675 }
8676
8677 // Second fill all stack locals.
8678 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
8679 SetProperty(local_scope,
8680 scope_info.stack_slot_name(i),
8681 Handle<Object>(frame->GetExpression(i)), NONE);
8682 }
8683
8684 // Third fill all context locals.
8685 Handle<Context> frame_context(Context::cast(frame->context()));
8686 Handle<Context> function_context(frame_context->fcontext());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008687 CopyContextLocalsToScopeObject(serialized_scope_info, scope_info,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008688 function_context, local_scope);
8689
8690 // Finally copy any properties from the function context extension. This will
8691 // be variables introduced by eval.
8692 if (function_context->closure() == *function) {
8693 if (function_context->has_extension() &&
8694 !function_context->IsGlobalContext()) {
8695 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008696 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008697 for (int i = 0; i < keys->length(); i++) {
8698 // Names of variables introduced by eval are strings.
8699 ASSERT(keys->get(i)->IsString());
8700 Handle<String> key(String::cast(keys->get(i)));
8701 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
8702 }
8703 }
8704 }
8705 return local_scope;
8706}
8707
8708
8709// Create a plain JSObject which materializes the closure content for the
8710// context.
8711static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
8712 ASSERT(context->is_function_context());
8713
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008714 Handle<SharedFunctionInfo> shared(context->closure()->shared());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008715 Handle<SerializedScopeInfo> serialized_scope_info(shared->scope_info());
8716 ScopeInfo<> scope_info(*serialized_scope_info);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008717
8718 // Allocate and initialize a JSObject with all the content of theis function
8719 // closure.
8720 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
8721
8722 // Check whether the arguments shadow object exists.
8723 int arguments_shadow_index =
ager@chromium.orgb5737492010-07-15 09:29:43 +00008724 shared->scope_info()->ContextSlotIndex(Heap::arguments_shadow_symbol(),
8725 NULL);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008726 if (arguments_shadow_index >= 0) {
8727 // In this case all the arguments are available in the arguments shadow
8728 // object.
8729 Handle<JSObject> arguments_shadow(
8730 JSObject::cast(context->get(arguments_shadow_index)));
8731 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8732 SetProperty(closure_scope,
8733 scope_info.parameter_name(i),
8734 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
8735 }
8736 }
8737
8738 // Fill all context locals to the context extension.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008739 CopyContextLocalsToScopeObject(serialized_scope_info, scope_info,
8740 context, closure_scope);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008741
8742 // Finally copy any properties from the function context extension. This will
8743 // be variables introduced by eval.
8744 if (context->has_extension()) {
8745 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008746 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008747 for (int i = 0; i < keys->length(); i++) {
8748 // Names of variables introduced by eval are strings.
8749 ASSERT(keys->get(i)->IsString());
8750 Handle<String> key(String::cast(keys->get(i)));
8751 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
8752 }
8753 }
8754
8755 return closure_scope;
8756}
8757
8758
8759// Iterate over the actual scopes visible from a stack frame. All scopes are
8760// backed by an actual context except the local scope, which is inserted
8761// "artifically" in the context chain.
8762class ScopeIterator {
8763 public:
8764 enum ScopeType {
8765 ScopeTypeGlobal = 0,
8766 ScopeTypeLocal,
8767 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00008768 ScopeTypeClosure,
8769 // Every catch block contains an implicit with block (its parameter is
8770 // a JSContextExtensionObject) that extends current scope with a variable
8771 // holding exception object. Such with blocks are treated as scopes of their
8772 // own type.
8773 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008774 };
8775
8776 explicit ScopeIterator(JavaScriptFrame* frame)
8777 : frame_(frame),
8778 function_(JSFunction::cast(frame->function())),
8779 context_(Context::cast(frame->context())),
8780 local_done_(false),
8781 at_local_(false) {
8782
8783 // Check whether the first scope is actually a local scope.
8784 if (context_->IsGlobalContext()) {
8785 // If there is a stack slot for .result then this local scope has been
8786 // created for evaluating top level code and it is not a real local scope.
8787 // Checking for the existence of .result seems fragile, but the scope info
8788 // saved with the code object does not otherwise have that information.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008789 int index = function_->shared()->scope_info()->
8790 StackSlotIndex(Heap::result_symbol());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008791 at_local_ = index < 0;
8792 } else if (context_->is_function_context()) {
8793 at_local_ = true;
8794 }
8795 }
8796
8797 // More scopes?
8798 bool Done() { return context_.is_null(); }
8799
8800 // Move to the next scope.
8801 void Next() {
8802 // If at a local scope mark the local scope as passed.
8803 if (at_local_) {
8804 at_local_ = false;
8805 local_done_ = true;
8806
8807 // If the current context is not associated with the local scope the
8808 // current context is the next real scope, so don't move to the next
8809 // context in this case.
8810 if (context_->closure() != *function_) {
8811 return;
8812 }
8813 }
8814
8815 // The global scope is always the last in the chain.
8816 if (context_->IsGlobalContext()) {
8817 context_ = Handle<Context>();
8818 return;
8819 }
8820
8821 // Move to the next context.
8822 if (context_->is_function_context()) {
8823 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
8824 } else {
8825 context_ = Handle<Context>(context_->previous());
8826 }
8827
8828 // If passing the local scope indicate that the current scope is now the
8829 // local scope.
8830 if (!local_done_ &&
8831 (context_->IsGlobalContext() || (context_->is_function_context()))) {
8832 at_local_ = true;
8833 }
8834 }
8835
8836 // Return the type of the current scope.
8837 int Type() {
8838 if (at_local_) {
8839 return ScopeTypeLocal;
8840 }
8841 if (context_->IsGlobalContext()) {
8842 ASSERT(context_->global()->IsGlobalObject());
8843 return ScopeTypeGlobal;
8844 }
8845 if (context_->is_function_context()) {
8846 return ScopeTypeClosure;
8847 }
8848 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00008849 // Current scope is either an explicit with statement or a with statement
8850 // implicitely generated for a catch block.
8851 // If the extension object here is a JSContextExtensionObject then
8852 // current with statement is one frome a catch block otherwise it's a
8853 // regular with statement.
8854 if (context_->extension()->IsJSContextExtensionObject()) {
8855 return ScopeTypeCatch;
8856 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008857 return ScopeTypeWith;
8858 }
8859
8860 // Return the JavaScript object with the content of the current scope.
8861 Handle<JSObject> ScopeObject() {
8862 switch (Type()) {
8863 case ScopeIterator::ScopeTypeGlobal:
8864 return Handle<JSObject>(CurrentContext()->global());
8865 break;
8866 case ScopeIterator::ScopeTypeLocal:
8867 // Materialize the content of the local scope into a JSObject.
8868 return MaterializeLocalScope(frame_);
8869 break;
8870 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00008871 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008872 // Return the with object.
8873 return Handle<JSObject>(CurrentContext()->extension());
8874 break;
8875 case ScopeIterator::ScopeTypeClosure:
8876 // Materialize the content of the closure scope into a JSObject.
8877 return MaterializeClosure(CurrentContext());
8878 break;
8879 }
8880 UNREACHABLE();
8881 return Handle<JSObject>();
8882 }
8883
8884 // Return the context for this scope. For the local context there might not
8885 // be an actual context.
8886 Handle<Context> CurrentContext() {
8887 if (at_local_ && context_->closure() != *function_) {
8888 return Handle<Context>();
8889 }
8890 return context_;
8891 }
8892
8893#ifdef DEBUG
8894 // Debug print of the content of the current scope.
8895 void DebugPrint() {
8896 switch (Type()) {
8897 case ScopeIterator::ScopeTypeGlobal:
8898 PrintF("Global:\n");
8899 CurrentContext()->Print();
8900 break;
8901
8902 case ScopeIterator::ScopeTypeLocal: {
8903 PrintF("Local:\n");
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008904 ScopeInfo<> scope_info(function_->shared()->scope_info());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008905 scope_info.Print();
8906 if (!CurrentContext().is_null()) {
8907 CurrentContext()->Print();
8908 if (CurrentContext()->has_extension()) {
8909 Handle<JSObject> extension =
8910 Handle<JSObject>(CurrentContext()->extension());
8911 if (extension->IsJSContextExtensionObject()) {
8912 extension->Print();
8913 }
8914 }
8915 }
8916 break;
8917 }
8918
8919 case ScopeIterator::ScopeTypeWith: {
8920 PrintF("With:\n");
8921 Handle<JSObject> extension =
8922 Handle<JSObject>(CurrentContext()->extension());
8923 extension->Print();
8924 break;
8925 }
8926
ager@chromium.orga1645e22009-09-09 19:27:10 +00008927 case ScopeIterator::ScopeTypeCatch: {
8928 PrintF("Catch:\n");
8929 Handle<JSObject> extension =
8930 Handle<JSObject>(CurrentContext()->extension());
8931 extension->Print();
8932 break;
8933 }
8934
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008935 case ScopeIterator::ScopeTypeClosure: {
8936 PrintF("Closure:\n");
8937 CurrentContext()->Print();
8938 if (CurrentContext()->has_extension()) {
8939 Handle<JSObject> extension =
8940 Handle<JSObject>(CurrentContext()->extension());
8941 if (extension->IsJSContextExtensionObject()) {
8942 extension->Print();
8943 }
8944 }
8945 break;
8946 }
8947
8948 default:
8949 UNREACHABLE();
8950 }
8951 PrintF("\n");
8952 }
8953#endif
8954
8955 private:
8956 JavaScriptFrame* frame_;
8957 Handle<JSFunction> function_;
8958 Handle<Context> context_;
8959 bool local_done_;
8960 bool at_local_;
8961
8962 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
8963};
8964
8965
8966static Object* Runtime_GetScopeCount(Arguments args) {
8967 HandleScope scope;
8968 ASSERT(args.length() == 2);
8969
8970 // Check arguments.
8971 Object* check = Runtime_CheckExecutionState(args);
8972 if (check->IsFailure()) return check;
8973 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8974
8975 // Get the frame where the debugging is performed.
8976 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8977 JavaScriptFrameIterator it(id);
8978 JavaScriptFrame* frame = it.frame();
8979
8980 // Count the visible scopes.
8981 int n = 0;
8982 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8983 n++;
8984 }
8985
8986 return Smi::FromInt(n);
8987}
8988
8989
8990static const int kScopeDetailsTypeIndex = 0;
8991static const int kScopeDetailsObjectIndex = 1;
8992static const int kScopeDetailsSize = 2;
8993
8994// Return an array with scope details
8995// args[0]: number: break id
8996// args[1]: number: frame index
8997// args[2]: number: scope index
8998//
8999// The array returned contains the following information:
9000// 0: Scope type
9001// 1: Scope object
9002static Object* Runtime_GetScopeDetails(Arguments args) {
9003 HandleScope scope;
9004 ASSERT(args.length() == 3);
9005
9006 // Check arguments.
9007 Object* check = Runtime_CheckExecutionState(args);
9008 if (check->IsFailure()) return check;
9009 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9010 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
9011
9012 // Get the frame where the debugging is performed.
9013 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9014 JavaScriptFrameIterator frame_it(id);
9015 JavaScriptFrame* frame = frame_it.frame();
9016
9017 // Find the requested scope.
9018 int n = 0;
9019 ScopeIterator it(frame);
9020 for (; !it.Done() && n < index; it.Next()) {
9021 n++;
9022 }
9023 if (it.Done()) {
9024 return Heap::undefined_value();
9025 }
9026
9027 // Calculate the size of the result.
9028 int details_size = kScopeDetailsSize;
9029 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
9030
9031 // Fill in scope details.
9032 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
9033 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
9034
9035 return *Factory::NewJSArrayWithElements(details);
9036}
9037
9038
9039static Object* Runtime_DebugPrintScopes(Arguments args) {
9040 HandleScope scope;
9041 ASSERT(args.length() == 0);
9042
9043#ifdef DEBUG
9044 // Print the scopes for the top frame.
9045 StackFrameLocator locator;
9046 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
9047 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
9048 it.DebugPrint();
9049 }
9050#endif
9051 return Heap::undefined_value();
9052}
9053
9054
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009055static Object* Runtime_GetCFrames(Arguments args) {
9056 HandleScope scope;
9057 ASSERT(args.length() == 1);
9058 Object* result = Runtime_CheckExecutionState(args);
9059 if (result->IsFailure()) return result;
9060
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00009061#if V8_HOST_ARCH_64_BIT
9062 UNIMPLEMENTED();
9063 return Heap::undefined_value();
9064#else
9065
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009066 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009067 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
9068 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009069 if (frames_count == OS::kStackWalkError) {
9070 return Heap::undefined_value();
9071 }
9072
9073 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
9074 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
9075 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
9076 for (int i = 0; i < frames_count; i++) {
9077 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
9078 frame_value->SetProperty(
9079 *address_str,
9080 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
9081 NONE);
9082
9083 // Get the stack walk text for this frame.
9084 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009085 int frame_text_length = StrLength(frames[i].text);
9086 if (frame_text_length > 0) {
9087 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009088 frame_text = Factory::NewStringFromAscii(str);
9089 }
9090
9091 if (!frame_text.is_null()) {
9092 frame_value->SetProperty(*text_str, *frame_text, NONE);
9093 }
9094
9095 frames_array->set(i, *frame_value);
9096 }
9097 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00009098#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009099}
9100
9101
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00009102static Object* Runtime_GetThreadCount(Arguments args) {
9103 HandleScope scope;
9104 ASSERT(args.length() == 1);
9105
9106 // Check arguments.
9107 Object* result = Runtime_CheckExecutionState(args);
9108 if (result->IsFailure()) return result;
9109
9110 // Count all archived V8 threads.
9111 int n = 0;
9112 for (ThreadState* thread = ThreadState::FirstInUse();
9113 thread != NULL;
9114 thread = thread->Next()) {
9115 n++;
9116 }
9117
9118 // Total number of threads is current thread and archived threads.
9119 return Smi::FromInt(n + 1);
9120}
9121
9122
9123static const int kThreadDetailsCurrentThreadIndex = 0;
9124static const int kThreadDetailsThreadIdIndex = 1;
9125static const int kThreadDetailsSize = 2;
9126
9127// Return an array with thread details
9128// args[0]: number: break id
9129// args[1]: number: thread index
9130//
9131// The array returned contains the following information:
9132// 0: Is current thread?
9133// 1: Thread id
9134static Object* Runtime_GetThreadDetails(Arguments args) {
9135 HandleScope scope;
9136 ASSERT(args.length() == 2);
9137
9138 // Check arguments.
9139 Object* check = Runtime_CheckExecutionState(args);
9140 if (check->IsFailure()) return check;
9141 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
9142
9143 // Allocate array for result.
9144 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
9145
9146 // Thread index 0 is current thread.
9147 if (index == 0) {
9148 // Fill the details.
9149 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
9150 details->set(kThreadDetailsThreadIdIndex,
9151 Smi::FromInt(ThreadManager::CurrentId()));
9152 } else {
9153 // Find the thread with the requested index.
9154 int n = 1;
9155 ThreadState* thread = ThreadState::FirstInUse();
9156 while (index != n && thread != NULL) {
9157 thread = thread->Next();
9158 n++;
9159 }
9160 if (thread == NULL) {
9161 return Heap::undefined_value();
9162 }
9163
9164 // Fill the details.
9165 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
9166 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
9167 }
9168
9169 // Convert to JS array and return.
9170 return *Factory::NewJSArrayWithElements(details);
9171}
9172
9173
whesse@chromium.orge90029b2010-08-02 11:52:17 +00009174// Sets the disable break state
9175// args[0]: disable break state
9176static Object* Runtime_SetDisableBreak(Arguments args) {
9177 HandleScope scope;
9178 ASSERT(args.length() == 1);
9179 CONVERT_BOOLEAN_CHECKED(disable_break, args[0]);
9180 Debug::set_disable_break(disable_break);
9181 return Heap::undefined_value();
9182}
9183
9184
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009185static Object* Runtime_GetBreakLocations(Arguments args) {
9186 HandleScope scope;
9187 ASSERT(args.length() == 1);
9188
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009189 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9190 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009191 // Find the number of break points
9192 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
9193 if (break_locations->IsUndefined()) return Heap::undefined_value();
9194 // Return array as JS array
9195 return *Factory::NewJSArrayWithElements(
9196 Handle<FixedArray>::cast(break_locations));
9197}
9198
9199
9200// Set a break point in a function
9201// args[0]: function
9202// args[1]: number: break source position (within the function source)
9203// args[2]: number: break point object
9204static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
9205 HandleScope scope;
9206 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009207 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9208 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009209 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9210 RUNTIME_ASSERT(source_position >= 0);
9211 Handle<Object> break_point_object_arg = args.at<Object>(2);
9212
9213 // Set break point.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009214 Debug::SetBreakPoint(shared, break_point_object_arg, &source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009215
lrn@chromium.org32d961d2010-06-30 09:09:34 +00009216 return Smi::FromInt(source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009217}
9218
9219
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009220Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
9221 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009222 // Iterate the heap looking for SharedFunctionInfo generated from the
9223 // script. The inner most SharedFunctionInfo containing the source position
9224 // for the requested break point is found.
9225 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
9226 // which is found is not compiled it is compiled and the heap is iterated
9227 // again as the compilation might create inner functions from the newly
9228 // compiled function and the actual requested break point might be in one of
9229 // these functions.
9230 bool done = false;
9231 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00009232 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009233 Handle<SharedFunctionInfo> target;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009234 while (!done) {
9235 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009236 for (HeapObject* obj = iterator.next();
9237 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009238 if (obj->IsSharedFunctionInfo()) {
9239 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
9240 if (shared->script() == *script) {
9241 // If the SharedFunctionInfo found has the requested script data and
9242 // contains the source position it is a candidate.
9243 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00009244 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009245 start_position = shared->start_position();
9246 }
9247 if (start_position <= position &&
9248 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00009249 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009250 // candidate this is the new candidate.
9251 if (target.is_null()) {
9252 target_start_position = start_position;
9253 target = shared;
9254 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00009255 if (target_start_position == start_position &&
9256 shared->end_position() == target->end_position()) {
9257 // If a top-level function contain only one function
9258 // declartion the source for the top-level and the function is
9259 // the same. In that case prefer the non top-level function.
9260 if (!shared->is_toplevel()) {
9261 target_start_position = start_position;
9262 target = shared;
9263 }
9264 } else if (target_start_position <= start_position &&
9265 shared->end_position() <= target->end_position()) {
9266 // This containment check includes equality as a function inside
9267 // a top-level function can share either start or end position
9268 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009269 target_start_position = start_position;
9270 target = shared;
9271 }
9272 }
9273 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009274 }
9275 }
9276 }
9277
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009278 if (target.is_null()) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009279 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009280 }
9281
9282 // If the candidate found is compiled we are done. NOTE: when lazy
9283 // compilation of inner functions is introduced some additional checking
9284 // needs to be done here to compile inner functions.
9285 done = target->is_compiled();
9286 if (!done) {
9287 // If the candidate is not compiled compile it to reveal any inner
9288 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009289 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009290 }
9291 }
9292
9293 return *target;
9294}
9295
9296
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009297// Changes the state of a break point in a script and returns source position
9298// where break point was set. NOTE: Regarding performance see the NOTE for
9299// GetScriptFromScriptData.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009300// args[0]: script to set break point in
9301// args[1]: number: break source position (within the script source)
9302// args[2]: number: break point object
9303static Object* Runtime_SetScriptBreakPoint(Arguments args) {
9304 HandleScope scope;
9305 ASSERT(args.length() == 3);
9306 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
9307 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9308 RUNTIME_ASSERT(source_position >= 0);
9309 Handle<Object> break_point_object_arg = args.at<Object>(2);
9310
9311 // Get the script from the script wrapper.
9312 RUNTIME_ASSERT(wrapper->value()->IsScript());
9313 Handle<Script> script(Script::cast(wrapper->value()));
9314
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009315 Object* result = Runtime::FindSharedFunctionInfoInScript(
9316 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009317 if (!result->IsUndefined()) {
9318 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
9319 // Find position within function. The script position might be before the
9320 // source position of the first function.
9321 int position;
9322 if (shared->start_position() > source_position) {
9323 position = 0;
9324 } else {
9325 position = source_position - shared->start_position();
9326 }
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009327 Debug::SetBreakPoint(shared, break_point_object_arg, &position);
9328 position += shared->start_position();
9329 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009330 }
9331 return Heap::undefined_value();
9332}
9333
9334
9335// Clear a break point
9336// args[0]: number: break point object
9337static Object* Runtime_ClearBreakPoint(Arguments args) {
9338 HandleScope scope;
9339 ASSERT(args.length() == 1);
9340 Handle<Object> break_point_object_arg = args.at<Object>(0);
9341
9342 // Clear break point.
9343 Debug::ClearBreakPoint(break_point_object_arg);
9344
9345 return Heap::undefined_value();
9346}
9347
9348
9349// Change the state of break on exceptions
9350// args[0]: boolean indicating uncaught exceptions
9351// args[1]: boolean indicating on/off
9352static Object* Runtime_ChangeBreakOnException(Arguments args) {
9353 HandleScope scope;
9354 ASSERT(args.length() == 2);
9355 ASSERT(args[0]->IsNumber());
9356 ASSERT(args[1]->IsBoolean());
9357
9358 // Update break point state
9359 ExceptionBreakType type =
9360 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
9361 bool enable = args[1]->ToBoolean()->IsTrue();
9362 Debug::ChangeBreakOnException(type, enable);
9363 return Heap::undefined_value();
9364}
9365
9366
9367// Prepare for stepping
9368// args[0]: break id for checking execution state
9369// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00009370// args[2]: number of times to perform the step, for step out it is the number
9371// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009372static Object* Runtime_PrepareStep(Arguments args) {
9373 HandleScope scope;
9374 ASSERT(args.length() == 3);
9375 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009376 Object* check = Runtime_CheckExecutionState(args);
9377 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009378 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
9379 return Top::Throw(Heap::illegal_argument_symbol());
9380 }
9381
9382 // Get the step action and check validity.
9383 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
9384 if (step_action != StepIn &&
9385 step_action != StepNext &&
9386 step_action != StepOut &&
9387 step_action != StepInMin &&
9388 step_action != StepMin) {
9389 return Top::Throw(Heap::illegal_argument_symbol());
9390 }
9391
9392 // Get the number of steps.
9393 int step_count = NumberToInt32(args[2]);
9394 if (step_count < 1) {
9395 return Top::Throw(Heap::illegal_argument_symbol());
9396 }
9397
ager@chromium.orga1645e22009-09-09 19:27:10 +00009398 // Clear all current stepping setup.
9399 Debug::ClearStepping();
9400
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009401 // Prepare step.
9402 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
9403 return Heap::undefined_value();
9404}
9405
9406
9407// Clear all stepping set by PrepareStep.
9408static Object* Runtime_ClearStepping(Arguments args) {
9409 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009410 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009411 Debug::ClearStepping();
9412 return Heap::undefined_value();
9413}
9414
9415
9416// Creates a copy of the with context chain. The copy of the context chain is
9417// is linked to the function context supplied.
9418static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
9419 Handle<Context> function_context) {
9420 // At the bottom of the chain. Return the function context to link to.
9421 if (context_chain->is_function_context()) {
9422 return function_context;
9423 }
9424
9425 // Recursively copy the with contexts.
9426 Handle<Context> previous(context_chain->previous());
9427 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
9428 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009429 CopyWithContextChain(function_context, previous),
9430 extension,
9431 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009432}
9433
9434
9435// Helper function to find or create the arguments object for
9436// Runtime_DebugEvaluate.
9437static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
9438 Handle<JSFunction> function,
ager@chromium.orgb5737492010-07-15 09:29:43 +00009439 Handle<SerializedScopeInfo> scope_info,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009440 const ScopeInfo<>* sinfo,
9441 Handle<Context> function_context) {
9442 // Try to find the value of 'arguments' to pass as parameter. If it is not
9443 // found (that is the debugged function does not reference 'arguments' and
9444 // does not support eval) then create an 'arguments' object.
9445 int index;
9446 if (sinfo->number_of_stack_slots() > 0) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00009447 index = scope_info->StackSlotIndex(Heap::arguments_symbol());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009448 if (index != -1) {
9449 return Handle<Object>(frame->GetExpression(index));
9450 }
9451 }
9452
9453 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00009454 index = scope_info->ContextSlotIndex(Heap::arguments_symbol(), NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009455 if (index != -1) {
9456 return Handle<Object>(function_context->get(index));
9457 }
9458 }
9459
9460 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009461 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
9462 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009463
9464 AssertNoAllocation no_gc;
9465 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009466 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009467 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009468 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009469 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009470 return arguments;
9471}
9472
9473
9474// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00009475// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009476// extension part has all the parameters and locals of the function on the
9477// stack frame. A function which calls eval with the code to evaluate is then
9478// compiled in this context and called in this context. As this context
9479// replaces the context of the function on the stack frame a new (empty)
9480// function is created as well to be used as the closure for the context.
9481// This function and the context acts as replacements for the function on the
9482// stack frame presenting the same view of the values of parameters and
9483// local variables as if the piece of JavaScript was evaluated at the point
9484// where the function on the stack frame is currently stopped.
9485static Object* Runtime_DebugEvaluate(Arguments args) {
9486 HandleScope scope;
9487
9488 // Check the execution state and decode arguments frame and source to be
9489 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009490 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009491 Object* check_result = Runtime_CheckExecutionState(args);
9492 if (check_result->IsFailure()) return check_result;
9493 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9494 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009495 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
9496
9497 // Handle the processing of break.
9498 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009499
9500 // Get the frame where the debugging is performed.
9501 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9502 JavaScriptFrameIterator it(id);
9503 JavaScriptFrame* frame = it.frame();
9504 Handle<JSFunction> function(JSFunction::cast(frame->function()));
ager@chromium.orgb5737492010-07-15 09:29:43 +00009505 Handle<SerializedScopeInfo> scope_info(function->shared()->scope_info());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009506 ScopeInfo<> sinfo(*scope_info);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009507
9508 // Traverse the saved contexts chain to find the active context for the
9509 // selected frame.
9510 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009511 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009512 save = save->prev();
9513 }
9514 ASSERT(save != NULL);
9515 SaveContext savex;
9516 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009517
9518 // Create the (empty) function replacing the function on the stack frame for
9519 // the purpose of evaluating in the context created below. It is important
9520 // that this function does not describe any parameters and local variables
9521 // in the context. If it does then this will cause problems with the lookup
9522 // in Context::Lookup, where context slots for parameters and local variables
9523 // are looked at before the extension object.
9524 Handle<JSFunction> go_between =
9525 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
9526 go_between->set_context(function->context());
9527#ifdef DEBUG
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009528 ScopeInfo<> go_between_sinfo(go_between->shared()->scope_info());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009529 ASSERT(go_between_sinfo.number_of_parameters() == 0);
9530 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
9531#endif
9532
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009533 // Materialize the content of the local scope into a JSObject.
9534 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009535
9536 // Allocate a new context for the debug evaluation and set the extension
9537 // object build.
9538 Handle<Context> context =
9539 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009540 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009541 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009542 Handle<Context> frame_context(Context::cast(frame->context()));
9543 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009544 context = CopyWithContextChain(frame_context, context);
9545
9546 // Wrap the evaluation statement in a new function compiled in the newly
9547 // created context. The function has one parameter which has to be called
9548 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00009549 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009550 // function(arguments,__source__) {return eval(__source__);}
9551 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00009552 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009553 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009554 Handle<String> function_source =
9555 Factory::NewStringFromAscii(Vector<const char>(source_str,
9556 source_str_length));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009557 Handle<SharedFunctionInfo> shared =
ager@chromium.org381abbb2009-02-25 13:23:22 +00009558 Compiler::CompileEval(function_source,
9559 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00009560 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00009561 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009562 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009563 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009564 Factory::NewFunctionFromSharedFunctionInfo(shared, context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009565
9566 // Invoke the result of the compilation to get the evaluation function.
9567 bool has_pending_exception;
9568 Handle<Object> receiver(frame->receiver());
9569 Handle<Object> evaluation_function =
9570 Execution::Call(compiled_function, receiver, 0, NULL,
9571 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009572 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009573
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009574 Handle<Object> arguments = GetArgumentsObject(frame, function, scope_info,
9575 &sinfo, function_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009576
9577 // Invoke the evaluation function and return the result.
9578 const int argc = 2;
9579 Object** argv[argc] = { arguments.location(),
9580 Handle<Object>::cast(source).location() };
9581 Handle<Object> result =
9582 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
9583 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009584 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009585
9586 // Skip the global proxy as it has no properties and always delegates to the
9587 // real global object.
9588 if (result->IsJSGlobalProxy()) {
9589 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
9590 }
9591
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009592 return *result;
9593}
9594
9595
9596static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
9597 HandleScope scope;
9598
9599 // Check the execution state and decode arguments frame and source to be
9600 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009601 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009602 Object* check_result = Runtime_CheckExecutionState(args);
9603 if (check_result->IsFailure()) return check_result;
9604 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009605 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
9606
9607 // Handle the processing of break.
9608 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009609
9610 // Enter the top context from before the debugger was invoked.
9611 SaveContext save;
9612 SaveContext* top = &save;
9613 while (top != NULL && *top->context() == *Debug::debug_context()) {
9614 top = top->prev();
9615 }
9616 if (top != NULL) {
9617 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009618 }
9619
9620 // Get the global context now set to the top context from before the
9621 // debugger was invoked.
9622 Handle<Context> context = Top::global_context();
9623
9624 // Compile the source to be evaluated.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009625 Handle<SharedFunctionInfo> shared =
9626 Compiler::CompileEval(source,
9627 context,
9628 true,
9629 Compiler::DONT_VALIDATE_JSON);
9630 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009631 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009632 Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
9633 context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009634
9635 // Invoke the result of the compilation to get the evaluation function.
9636 bool has_pending_exception;
9637 Handle<Object> receiver = Top::global();
9638 Handle<Object> result =
9639 Execution::Call(compiled_function, receiver, 0, NULL,
9640 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009641 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009642 return *result;
9643}
9644
9645
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009646static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
9647 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009648 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009649
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009650 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009651 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009652
9653 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009654 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00009655 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
9656 // Get the script wrapper in a local handle before calling GetScriptWrapper,
9657 // because using
9658 // instances->set(i, *GetScriptWrapper(script))
9659 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
9660 // already have deferenced the instances handle.
9661 Handle<JSValue> wrapper = GetScriptWrapper(script);
9662 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009663 }
9664
9665 // Return result as a JS array.
9666 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
9667 Handle<JSArray>::cast(result)->SetContent(*instances);
9668 return *result;
9669}
9670
9671
9672// Helper function used by Runtime_DebugReferencedBy below.
9673static int DebugReferencedBy(JSObject* target,
9674 Object* instance_filter, int max_references,
9675 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009676 JSFunction* arguments_function) {
9677 NoHandleAllocation ha;
9678 AssertNoAllocation no_alloc;
9679
9680 // Iterate the heap.
9681 int count = 0;
9682 JSObject* last = NULL;
9683 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009684 HeapObject* heap_obj = NULL;
9685 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009686 (max_references == 0 || count < max_references)) {
9687 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009688 if (heap_obj->IsJSObject()) {
9689 // Skip context extension objects and argument arrays as these are
9690 // checked in the context of functions using them.
9691 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00009692 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009693 obj->map()->constructor() == arguments_function) {
9694 continue;
9695 }
9696
9697 // Check if the JS object has a reference to the object looked for.
9698 if (obj->ReferencesObject(target)) {
9699 // Check instance filter if supplied. This is normally used to avoid
9700 // references from mirror objects (see Runtime_IsInPrototypeChain).
9701 if (!instance_filter->IsUndefined()) {
9702 Object* V = obj;
9703 while (true) {
9704 Object* prototype = V->GetPrototype();
9705 if (prototype->IsNull()) {
9706 break;
9707 }
9708 if (instance_filter == prototype) {
9709 obj = NULL; // Don't add this object.
9710 break;
9711 }
9712 V = prototype;
9713 }
9714 }
9715
9716 if (obj != NULL) {
9717 // Valid reference found add to instance array if supplied an update
9718 // count.
9719 if (instances != NULL && count < instances_size) {
9720 instances->set(count, obj);
9721 }
9722 last = obj;
9723 count++;
9724 }
9725 }
9726 }
9727 }
9728
9729 // Check for circular reference only. This can happen when the object is only
9730 // referenced from mirrors and has a circular reference in which case the
9731 // object is not really alive and would have been garbage collected if not
9732 // referenced from the mirror.
9733 if (count == 1 && last == target) {
9734 count = 0;
9735 }
9736
9737 // Return the number of referencing objects found.
9738 return count;
9739}
9740
9741
9742// Scan the heap for objects with direct references to an object
9743// args[0]: the object to find references to
9744// args[1]: constructor function for instances to exclude (Mirror)
9745// args[2]: the the maximum number of objects to return
9746static Object* Runtime_DebugReferencedBy(Arguments args) {
9747 ASSERT(args.length() == 3);
9748
9749 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009750 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009751
9752 // Check parameters.
9753 CONVERT_CHECKED(JSObject, target, args[0]);
9754 Object* instance_filter = args[1];
9755 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
9756 instance_filter->IsJSObject());
9757 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
9758 RUNTIME_ASSERT(max_references >= 0);
9759
9760 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009761 JSObject* arguments_boilerplate =
9762 Top::context()->global_context()->arguments_boilerplate();
9763 JSFunction* arguments_function =
9764 JSFunction::cast(arguments_boilerplate->map()->constructor());
9765
9766 // Get the number of referencing objects.
9767 int count;
9768 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009769 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009770
9771 // Allocate an array to hold the result.
9772 Object* object = Heap::AllocateFixedArray(count);
9773 if (object->IsFailure()) return object;
9774 FixedArray* instances = FixedArray::cast(object);
9775
9776 // Fill the referencing objects.
9777 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009778 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009779
9780 // Return result as JS array.
9781 Object* result =
9782 Heap::AllocateJSObject(
9783 Top::context()->global_context()->array_function());
9784 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9785 return result;
9786}
9787
9788
9789// Helper function used by Runtime_DebugConstructedBy below.
9790static int DebugConstructedBy(JSFunction* constructor, int max_references,
9791 FixedArray* instances, int instances_size) {
9792 AssertNoAllocation no_alloc;
9793
9794 // Iterate the heap.
9795 int count = 0;
9796 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009797 HeapObject* heap_obj = NULL;
9798 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009799 (max_references == 0 || count < max_references)) {
9800 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009801 if (heap_obj->IsJSObject()) {
9802 JSObject* obj = JSObject::cast(heap_obj);
9803 if (obj->map()->constructor() == constructor) {
9804 // Valid reference found add to instance array if supplied an update
9805 // count.
9806 if (instances != NULL && count < instances_size) {
9807 instances->set(count, obj);
9808 }
9809 count++;
9810 }
9811 }
9812 }
9813
9814 // Return the number of referencing objects found.
9815 return count;
9816}
9817
9818
9819// Scan the heap for objects constructed by a specific function.
9820// args[0]: the constructor to find instances of
9821// args[1]: the the maximum number of objects to return
9822static Object* Runtime_DebugConstructedBy(Arguments args) {
9823 ASSERT(args.length() == 2);
9824
9825 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009826 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009827
9828 // Check parameters.
9829 CONVERT_CHECKED(JSFunction, constructor, args[0]);
9830 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
9831 RUNTIME_ASSERT(max_references >= 0);
9832
9833 // Get the number of referencing objects.
9834 int count;
9835 count = DebugConstructedBy(constructor, max_references, NULL, 0);
9836
9837 // Allocate an array to hold the result.
9838 Object* object = Heap::AllocateFixedArray(count);
9839 if (object->IsFailure()) return object;
9840 FixedArray* instances = FixedArray::cast(object);
9841
9842 // Fill the referencing objects.
9843 count = DebugConstructedBy(constructor, max_references, instances, count);
9844
9845 // Return result as JS array.
9846 Object* result =
9847 Heap::AllocateJSObject(
9848 Top::context()->global_context()->array_function());
9849 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9850 return result;
9851}
9852
9853
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009854// Find the effective prototype object as returned by __proto__.
9855// args[0]: the object to find the prototype for.
9856static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009857 ASSERT(args.length() == 1);
9858
9859 CONVERT_CHECKED(JSObject, obj, args[0]);
9860
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009861 // Use the __proto__ accessor.
9862 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009863}
9864
9865
9866static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009867 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009868 CPU::DebugBreak();
9869 return Heap::undefined_value();
9870}
9871
9872
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009873static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009874#ifdef DEBUG
9875 HandleScope scope;
9876 ASSERT(args.length() == 1);
9877 // Get the function and make sure it is compiled.
9878 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009879 Handle<SharedFunctionInfo> shared(func->shared());
9880 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009881 return Failure::Exception();
9882 }
9883 func->code()->PrintLn();
9884#endif // DEBUG
9885 return Heap::undefined_value();
9886}
ager@chromium.org9085a012009-05-11 19:22:57 +00009887
9888
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009889static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
9890#ifdef DEBUG
9891 HandleScope scope;
9892 ASSERT(args.length() == 1);
9893 // Get the function and make sure it is compiled.
9894 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009895 Handle<SharedFunctionInfo> shared(func->shared());
9896 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009897 return Failure::Exception();
9898 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009899 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009900#endif // DEBUG
9901 return Heap::undefined_value();
9902}
9903
9904
ager@chromium.org9085a012009-05-11 19:22:57 +00009905static Object* Runtime_FunctionGetInferredName(Arguments args) {
9906 NoHandleAllocation ha;
9907 ASSERT(args.length() == 1);
9908
9909 CONVERT_CHECKED(JSFunction, f, args[0]);
9910 return f->shared()->inferred_name();
9911}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009912
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009913
9914static int FindSharedFunctionInfosForScript(Script* script,
9915 FixedArray* buffer) {
9916 AssertNoAllocation no_allocations;
9917
9918 int counter = 0;
9919 int buffer_size = buffer->length();
9920 HeapIterator iterator;
9921 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
9922 ASSERT(obj != NULL);
9923 if (!obj->IsSharedFunctionInfo()) {
9924 continue;
9925 }
9926 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
9927 if (shared->script() != script) {
9928 continue;
9929 }
9930 if (counter < buffer_size) {
9931 buffer->set(counter, shared);
9932 }
9933 counter++;
9934 }
9935 return counter;
9936}
9937
9938// For a script finds all SharedFunctionInfo's in the heap that points
9939// to this script. Returns JSArray of SharedFunctionInfo wrapped
9940// in OpaqueReferences.
9941static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
9942 Arguments args) {
9943 ASSERT(args.length() == 1);
9944 HandleScope scope;
9945 CONVERT_CHECKED(JSValue, script_value, args[0]);
9946
9947 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
9948
9949 const int kBufferSize = 32;
9950
9951 Handle<FixedArray> array;
9952 array = Factory::NewFixedArray(kBufferSize);
9953 int number = FindSharedFunctionInfosForScript(*script, *array);
9954 if (number > kBufferSize) {
9955 array = Factory::NewFixedArray(number);
9956 FindSharedFunctionInfosForScript(*script, *array);
9957 }
9958
9959 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
9960 result->set_length(Smi::FromInt(number));
9961
9962 LiveEdit::WrapSharedFunctionInfos(result);
9963
9964 return *result;
9965}
9966
9967// For a script calculates compilation information about all its functions.
9968// The script source is explicitly specified by the second argument.
9969// The source of the actual script is not used, however it is important that
9970// all generated code keeps references to this particular instance of script.
9971// Returns a JSArray of compilation infos. The array is ordered so that
9972// each function with all its descendant is always stored in a continues range
9973// with the function itself going first. The root function is a script function.
9974static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
9975 ASSERT(args.length() == 2);
9976 HandleScope scope;
9977 CONVERT_CHECKED(JSValue, script, args[0]);
9978 CONVERT_ARG_CHECKED(String, source, 1);
9979 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
9980
9981 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
9982
9983 if (Top::has_pending_exception()) {
9984 return Failure::Exception();
9985 }
9986
9987 return result;
9988}
9989
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009990// Changes the source of the script to a new_source.
9991// If old_script_name is provided (i.e. is a String), also creates a copy of
9992// the script with its original source and sends notification to debugger.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009993static Object* Runtime_LiveEditReplaceScript(Arguments args) {
9994 ASSERT(args.length() == 3);
9995 HandleScope scope;
9996 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
9997 CONVERT_ARG_CHECKED(String, new_source, 1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009998 Handle<Object> old_script_name(args[2]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009999
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010000 CONVERT_CHECKED(Script, original_script_pointer,
10001 original_script_value->value());
10002 Handle<Script> original_script(original_script_pointer);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010003
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010004 Object* old_script = LiveEdit::ChangeScriptSource(original_script,
10005 new_source,
10006 old_script_name);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010007
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010008 if (old_script->IsScript()) {
10009 Handle<Script> script_handle(Script::cast(old_script));
10010 return *(GetScriptWrapper(script_handle));
10011 } else {
10012 return Heap::null_value();
10013 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010014}
10015
10016// Replaces code of SharedFunctionInfo with a new one.
10017static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
10018 ASSERT(args.length() == 2);
10019 HandleScope scope;
10020 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
10021 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
10022
ager@chromium.orgac091b72010-05-05 07:34:42 +000010023 return LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010024}
10025
10026// Connects SharedFunctionInfo to another script.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010027static Object* Runtime_LiveEditFunctionSetScript(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010028 ASSERT(args.length() == 2);
10029 HandleScope scope;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010030 Handle<Object> function_object(args[0]);
10031 Handle<Object> script_object(args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010032
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010033 if (function_object->IsJSValue()) {
10034 Handle<JSValue> function_wrapper = Handle<JSValue>::cast(function_object);
10035 if (script_object->IsJSValue()) {
10036 CONVERT_CHECKED(Script, script, JSValue::cast(*script_object)->value());
10037 script_object = Handle<Object>(script);
10038 }
10039
10040 LiveEdit::SetFunctionScript(function_wrapper, script_object);
10041 } else {
10042 // Just ignore this. We may not have a SharedFunctionInfo for some functions
10043 // and we check it in this function.
10044 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010045
10046 return Heap::undefined_value();
10047}
10048
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010049
10050// In a code of a parent function replaces original function as embedded object
10051// with a substitution one.
10052static Object* Runtime_LiveEditReplaceRefToNestedFunction(Arguments args) {
10053 ASSERT(args.length() == 3);
10054 HandleScope scope;
10055
10056 CONVERT_ARG_CHECKED(JSValue, parent_wrapper, 0);
10057 CONVERT_ARG_CHECKED(JSValue, orig_wrapper, 1);
10058 CONVERT_ARG_CHECKED(JSValue, subst_wrapper, 2);
10059
10060 LiveEdit::ReplaceRefToNestedFunction(parent_wrapper, orig_wrapper,
10061 subst_wrapper);
10062
10063 return Heap::undefined_value();
10064}
10065
10066
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010067// Updates positions of a shared function info (first parameter) according
10068// to script source change. Text change is described in second parameter as
10069// array of groups of 3 numbers:
10070// (change_begin, change_end, change_end_new_position).
10071// Each group describes a change in text; groups are sorted by change_begin.
10072static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
10073 ASSERT(args.length() == 2);
10074 HandleScope scope;
10075 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
10076 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
10077
ager@chromium.orgac091b72010-05-05 07:34:42 +000010078 return LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010079}
10080
10081
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010082// For array of SharedFunctionInfo's (each wrapped in JSValue)
10083// checks that none of them have activations on stacks (of any thread).
10084// Returns array of the same length with corresponding results of
10085// LiveEdit::FunctionPatchabilityStatus type.
ager@chromium.org357bf652010-04-12 11:30:10 +000010086static Object* Runtime_LiveEditCheckAndDropActivations(Arguments args) {
10087 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010088 HandleScope scope;
10089 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +000010090 CONVERT_BOOLEAN_CHECKED(do_drop, args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010091
ager@chromium.org357bf652010-04-12 11:30:10 +000010092 return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010093}
10094
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010095// Compares 2 strings line-by-line and returns diff in form of JSArray of
fschneider@chromium.org013f3e12010-04-26 13:27:52 +000010096// triplets (pos1, pos1_end, pos2_end) describing list of diff chunks.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010097static Object* Runtime_LiveEditCompareStringsLinewise(Arguments args) {
10098 ASSERT(args.length() == 2);
10099 HandleScope scope;
10100 CONVERT_ARG_CHECKED(String, s1, 0);
10101 CONVERT_ARG_CHECKED(String, s2, 1);
10102
10103 return *LiveEdit::CompareStringsLinewise(s1, s2);
10104}
10105
10106
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010107
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010108// A testing entry. Returns statement position which is the closest to
10109// source_position.
10110static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
10111 ASSERT(args.length() == 2);
10112 HandleScope scope;
10113 CONVERT_ARG_CHECKED(JSFunction, function, 0);
10114 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
10115
10116 Handle<Code> code(function->code());
10117
10118 RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
10119 int closest_pc = 0;
10120 int distance = kMaxInt;
10121 while (!it.done()) {
10122 int statement_position = static_cast<int>(it.rinfo()->data());
10123 // Check if this break point is closer that what was previously found.
10124 if (source_position <= statement_position &&
10125 statement_position - source_position < distance) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +000010126 closest_pc =
10127 static_cast<int>(it.rinfo()->pc() - code->instruction_start());
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010128 distance = statement_position - source_position;
10129 // Check whether we can't get any closer.
10130 if (distance == 0) break;
10131 }
10132 it.next();
10133 }
10134
10135 return Smi::FromInt(closest_pc);
10136}
10137
10138
ager@chromium.org357bf652010-04-12 11:30:10 +000010139// Calls specified function with or without entering the debugger.
10140// This is used in unit tests to run code as if debugger is entered or simply
10141// to have a stack with C++ frame in the middle.
10142static Object* Runtime_ExecuteInDebugContext(Arguments args) {
10143 ASSERT(args.length() == 2);
10144 HandleScope scope;
10145 CONVERT_ARG_CHECKED(JSFunction, function, 0);
10146 CONVERT_BOOLEAN_CHECKED(without_debugger, args[1]);
10147
10148 Handle<Object> result;
10149 bool pending_exception;
10150 {
10151 if (without_debugger) {
10152 result = Execution::Call(function, Top::global(), 0, NULL,
10153 &pending_exception);
10154 } else {
10155 EnterDebugger enter_debugger;
10156 result = Execution::Call(function, Top::global(), 0, NULL,
10157 &pending_exception);
10158 }
10159 }
10160 if (!pending_exception) {
10161 return *result;
10162 } else {
10163 return Failure::Exception();
10164 }
10165}
10166
10167
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010168#endif // ENABLE_DEBUGGER_SUPPORT
10169
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010170#ifdef ENABLE_LOGGING_AND_PROFILING
10171
10172static Object* Runtime_ProfilerResume(Arguments args) {
10173 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010174 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010175
10176 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010177 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10178 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010179 return Heap::undefined_value();
10180}
10181
10182
10183static Object* Runtime_ProfilerPause(Arguments args) {
10184 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010185 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010186
10187 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010188 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10189 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010190 return Heap::undefined_value();
10191}
10192
10193#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010194
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010195// Finds the script object from the script data. NOTE: This operation uses
10196// heap traversal to find the function generated for the source position
10197// for the requested break point. For lazily compiled functions several heap
10198// traversals might be required rendering this operation as a rather slow
10199// operation. However for setting break points which is normally done through
10200// some kind of user interaction the performance is not crucial.
10201static Handle<Object> Runtime_GetScriptFromScriptName(
10202 Handle<String> script_name) {
10203 // Scan the heap for Script objects to find the script with the requested
10204 // script data.
10205 Handle<Script> script;
10206 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010207 HeapObject* obj = NULL;
10208 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010209 // If a script is found check if it has the script data requested.
10210 if (obj->IsScript()) {
10211 if (Script::cast(obj)->name()->IsString()) {
10212 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
10213 script = Handle<Script>(Script::cast(obj));
10214 }
10215 }
10216 }
10217 }
10218
10219 // If no script with the requested script data is found return undefined.
10220 if (script.is_null()) return Factory::undefined_value();
10221
10222 // Return the script found.
10223 return GetScriptWrapper(script);
10224}
10225
10226
10227// Get the script object from script data. NOTE: Regarding performance
10228// see the NOTE for GetScriptFromScriptData.
10229// args[0]: script data for the script to find the source for
10230static Object* Runtime_GetScript(Arguments args) {
10231 HandleScope scope;
10232
10233 ASSERT(args.length() == 1);
10234
10235 CONVERT_CHECKED(String, script_name, args[0]);
10236
10237 // Find the requested script.
10238 Handle<Object> result =
10239 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
10240 return *result;
10241}
10242
10243
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010244// Determines whether the given stack frame should be displayed in
10245// a stack trace. The caller is the error constructor that asked
10246// for the stack trace to be collected. The first time a construct
10247// call to this function is encountered it is skipped. The seen_caller
10248// in/out parameter is used to remember if the caller has been seen
10249// yet.
10250static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
10251 bool* seen_caller) {
10252 // Only display JS frames.
10253 if (!raw_frame->is_java_script())
10254 return false;
10255 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
10256 Object* raw_fun = frame->function();
10257 // Not sure when this can happen but skip it just in case.
10258 if (!raw_fun->IsJSFunction())
10259 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010260 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010261 *seen_caller = true;
10262 return false;
10263 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010264 // Skip all frames until we've seen the caller. Also, skip the most
10265 // obvious builtin calls. Some builtin calls (such as Number.ADD
10266 // which is invoked using 'call') are very difficult to recognize
10267 // so we're leaving them in for now.
10268 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010269}
10270
10271
10272// Collect the raw data for a stack trace. Returns an array of three
10273// element segments each containing a receiver, function and native
10274// code offset.
10275static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010276 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010277 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010278 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
10279
10280 HandleScope scope;
10281
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000010282 limit = Max(limit, 0); // Ensure that limit is not negative.
10283 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010284 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010285
10286 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010287 // If the caller parameter is a function we skip frames until we're
10288 // under it before starting to collect.
10289 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010290 int cursor = 0;
10291 int frames_seen = 0;
10292 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010293 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010294 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010295 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010296 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010297 Object* recv = frame->receiver();
10298 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010299 Address pc = frame->pc();
10300 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010301 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010302 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010303 if (cursor + 2 < elements->length()) {
10304 elements->set(cursor++, recv);
10305 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010306 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010307 } else {
10308 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010309 Handle<Object> recv_handle(recv);
10310 Handle<Object> fun_handle(fun);
10311 SetElement(result, cursor++, recv_handle);
10312 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010313 SetElement(result, cursor++, Handle<Smi>(offset));
10314 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010315 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010316 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010317 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010318
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010319 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010320 return *result;
10321}
10322
10323
ager@chromium.org3811b432009-10-28 14:53:37 +000010324// Returns V8 version as a string.
10325static Object* Runtime_GetV8Version(Arguments args) {
10326 ASSERT_EQ(args.length(), 0);
10327
10328 NoHandleAllocation ha;
10329
10330 const char* version_string = v8::V8::GetVersion();
10331
10332 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
10333}
10334
10335
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010336static Object* Runtime_Abort(Arguments args) {
10337 ASSERT(args.length() == 2);
10338 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
10339 Smi::cast(args[1])->value());
10340 Top::PrintStack();
10341 OS::Abort();
10342 UNREACHABLE();
10343 return NULL;
10344}
10345
10346
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010347static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
10348 ASSERT(args.length() == 0);
10349 HandleScope::DeleteExtensions();
10350 return Heap::undefined_value();
10351}
10352
10353
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010354static Object* CacheMiss(FixedArray* cache_obj, int index, Object* key_obj) {
10355 ASSERT(index % 2 == 0); // index of the key
10356 ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
10357 ASSERT(index < cache_obj->length());
10358
10359 HandleScope scope;
10360
10361 Handle<FixedArray> cache(cache_obj);
10362 Handle<Object> key(key_obj);
10363 Handle<JSFunction> factory(JSFunction::cast(
10364 cache->get(JSFunctionResultCache::kFactoryIndex)));
10365 // TODO(antonm): consider passing a receiver when constructing a cache.
10366 Handle<Object> receiver(Top::global_context()->global());
10367
10368 Handle<Object> value;
10369 {
10370 // This handle is nor shared, nor used later, so it's safe.
10371 Object** argv[] = { key.location() };
10372 bool pending_exception = false;
10373 value = Execution::Call(factory,
10374 receiver,
10375 1,
10376 argv,
10377 &pending_exception);
10378 if (pending_exception) return Failure::Exception();
10379 }
10380
10381 cache->set(index, *key);
10382 cache->set(index + 1, *value);
10383 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(index));
10384
10385 return *value;
10386}
10387
10388
10389static Object* Runtime_GetFromCache(Arguments args) {
10390 // This is only called from codegen, so checks might be more lax.
10391 CONVERT_CHECKED(FixedArray, cache, args[0]);
10392 Object* key = args[1];
10393
10394 const int finger_index =
10395 Smi::cast(cache->get(JSFunctionResultCache::kFingerIndex))->value();
10396
10397 Object* o = cache->get(finger_index);
10398 if (o == key) {
10399 // The fastest case: hit the same place again.
10400 return cache->get(finger_index + 1);
10401 }
10402
10403 for (int i = finger_index - 2;
10404 i >= JSFunctionResultCache::kEntriesIndex;
10405 i -= 2) {
10406 o = cache->get(i);
10407 if (o == key) {
10408 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10409 return cache->get(i + 1);
10410 }
10411 }
10412
10413 const int size =
10414 Smi::cast(cache->get(JSFunctionResultCache::kCacheSizeIndex))->value();
10415 ASSERT(size <= cache->length());
10416
10417 for (int i = size - 2; i > finger_index; i -= 2) {
10418 o = cache->get(i);
10419 if (o == key) {
10420 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10421 return cache->get(i + 1);
10422 }
10423 }
10424
10425 // Cache miss. If we have spare room, put new data into it, otherwise
10426 // evict post finger entry which must be least recently used.
10427 if (size < cache->length()) {
10428 cache->set(JSFunctionResultCache::kCacheSizeIndex, Smi::FromInt(size + 2));
10429 return CacheMiss(cache, size, key);
10430 } else {
antonm@chromium.org397e23c2010-04-21 12:00:05 +000010431 int target_index = finger_index + JSFunctionResultCache::kEntrySize;
10432 if (target_index == cache->length()) {
10433 target_index = JSFunctionResultCache::kEntriesIndex;
10434 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010435 return CacheMiss(cache, target_index, key);
10436 }
10437}
10438
kasper.lund44510672008-07-25 07:37:58 +000010439#ifdef DEBUG
10440// ListNatives is ONLY used by the fuzz-natives.js in debug mode
10441// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010442static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +000010443 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010444 HandleScope scope;
10445 Handle<JSArray> result = Factory::NewJSArray(0);
10446 int index = 0;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010447 bool inline_runtime_functions = false;
ager@chromium.orga1645e22009-09-09 19:27:10 +000010448#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010449 { \
10450 HandleScope inner; \
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010451 Handle<String> name; \
10452 /* Inline runtime functions have an underscore in front of the name. */ \
10453 if (inline_runtime_functions) { \
10454 name = Factory::NewStringFromAscii( \
10455 Vector<const char>("_" #Name, StrLength("_" #Name))); \
10456 } else { \
10457 name = Factory::NewStringFromAscii( \
10458 Vector<const char>(#Name, StrLength(#Name))); \
10459 } \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010460 Handle<JSArray> pair = Factory::NewJSArray(0); \
10461 SetElement(pair, 0, name); \
10462 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
10463 SetElement(result, index++, pair); \
10464 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010465 inline_runtime_functions = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010466 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010467 inline_runtime_functions = true;
10468 INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010469#undef ADD_ENTRY
10470 return *result;
10471}
kasper.lund44510672008-07-25 07:37:58 +000010472#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010473
10474
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010475static Object* Runtime_Log(Arguments args) {
10476 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +000010477 CONVERT_CHECKED(String, format, args[0]);
10478 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010479 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010480 Logger::LogRuntime(chars, elms);
10481 return Heap::undefined_value();
10482}
10483
10484
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010485static Object* Runtime_IS_VAR(Arguments args) {
10486 UNREACHABLE(); // implemented as macro in the parser
10487 return NULL;
10488}
10489
10490
10491// ----------------------------------------------------------------------------
10492// Implementation of Runtime
10493
ager@chromium.orga1645e22009-09-09 19:27:10 +000010494#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010495 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +000010496 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010497
10498static Runtime::Function Runtime_functions[] = {
10499 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010500 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010501};
10502
10503#undef F
10504
10505
10506Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
10507 ASSERT(0 <= fid && fid < kNofFunctions);
10508 return &Runtime_functions[fid];
10509}
10510
10511
ricow@chromium.org65fae842010-08-25 15:26:24 +000010512Runtime::Function* Runtime::FunctionForName(Vector<const char> name) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010513 for (Function* f = Runtime_functions; f->name != NULL; f++) {
ricow@chromium.org65fae842010-08-25 15:26:24 +000010514 if (strncmp(f->name, name.start(), name.length()) == 0
10515 && f->name[name.length()] == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010516 return f;
10517 }
10518 }
10519 return NULL;
10520}
10521
10522
10523void Runtime::PerformGC(Object* result) {
10524 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010525 if (failure->IsRetryAfterGC()) {
10526 // Try to do a garbage collection; ignore it if it fails. The C
10527 // entry stub will throw an out-of-memory exception in that case.
10528 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
10529 } else {
10530 // Handle last resort GC and make sure to allow future allocations
10531 // to grow the heap without causing GCs (if possible).
10532 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +000010533 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010534 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010535}
10536
10537
10538} } // namespace v8::internal