blob: a6924a0ff362250918b8af2b6b1b3eb77087faee [file] [log] [blame]
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001// Copyright 2006-2009 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include <stdlib.h>
29
30#include "v8.h"
31
32#include "accessors.h"
33#include "api.h"
34#include "arguments.h"
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000035#include "codegen.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000036#include "compiler.h"
37#include "cpu.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000038#include "dateparser-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000039#include "debug.h"
40#include "execution.h"
41#include "jsregexp.h"
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000042#include "liveedit.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000043#include "parser.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000044#include "platform.h"
45#include "runtime.h"
46#include "scopeinfo.h"
ager@chromium.org7c537e22008-10-16 08:43:32 +000047#include "smart-pointer.h"
ager@chromium.org18ad94b2009-09-02 08:22:29 +000048#include "stub-cache.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000049#include "v8threads.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000050
kasperl@chromium.org71affb52009-05-26 05:44:31 +000051namespace v8 {
52namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000053
54
ager@chromium.org3e875802009-06-29 08:26:34 +000055#define RUNTIME_ASSERT(value) \
56 if (!(value)) return Top::ThrowIllegalOperation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000057
58// Cast the given object to a value of the specified type and store
59// it in a variable with the given name. If the object is not of the
60// expected type call IllegalOperation and return.
61#define CONVERT_CHECKED(Type, name, obj) \
62 RUNTIME_ASSERT(obj->Is##Type()); \
63 Type* name = Type::cast(obj);
64
65#define CONVERT_ARG_CHECKED(Type, name, index) \
66 RUNTIME_ASSERT(args[index]->Is##Type()); \
67 Handle<Type> name = args.at<Type>(index);
68
kasper.lundbd3ec4e2008-07-09 11:06:54 +000069// Cast the given object to a boolean and store it in a variable with
70// the given name. If the object is not a boolean call IllegalOperation
71// and return.
72#define CONVERT_BOOLEAN_CHECKED(name, obj) \
73 RUNTIME_ASSERT(obj->IsBoolean()); \
74 bool name = (obj)->IsTrue();
75
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000076// Cast the given object to a Smi and store its value in an int variable
77// with the given name. If the object is not a Smi call IllegalOperation
78// and return.
79#define CONVERT_SMI_CHECKED(name, obj) \
80 RUNTIME_ASSERT(obj->IsSmi()); \
81 int name = Smi::cast(obj)->value();
82
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000083// Cast the given object to a double and store it in a variable with
84// the given name. If the object is not a number (as opposed to
85// the number not-a-number) call IllegalOperation and return.
86#define CONVERT_DOUBLE_CHECKED(name, obj) \
87 RUNTIME_ASSERT(obj->IsNumber()); \
88 double name = (obj)->Number();
89
90// Call the specified converter on the object *comand store the result in
91// a variable of the specified type with the given name. If the
92// object is not a Number call IllegalOperation and return.
93#define CONVERT_NUMBER_CHECKED(type, name, Type, obj) \
94 RUNTIME_ASSERT(obj->IsNumber()); \
95 type name = NumberTo##Type(obj);
96
97// Non-reentrant string buffer for efficient general use in this file.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000098static StaticResource<StringInputBuffer> runtime_string_input_buffer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000099
100
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000101static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
102 StackLimitCheck check;
103 if (check.HasOverflowed()) return Top::StackOverflow();
104
105 Object* result = Heap::CopyJSObject(boilerplate);
106 if (result->IsFailure()) return result;
107 JSObject* copy = JSObject::cast(result);
108
109 // Deep copy local properties.
110 if (copy->HasFastProperties()) {
111 FixedArray* properties = copy->properties();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000112 for (int i = 0; i < properties->length(); i++) {
113 Object* value = properties->get(i);
114 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000115 JSObject* js_object = JSObject::cast(value);
116 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000117 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000118 properties->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000119 }
120 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000121 int nof = copy->map()->inobject_properties();
122 for (int i = 0; i < nof; i++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000123 Object* value = copy->InObjectPropertyAt(i);
124 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000125 JSObject* js_object = JSObject::cast(value);
126 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000127 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000128 copy->InObjectPropertyAtPut(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000129 }
130 }
131 } else {
132 result = Heap::AllocateFixedArray(copy->NumberOfLocalProperties(NONE));
133 if (result->IsFailure()) return result;
134 FixedArray* names = FixedArray::cast(result);
135 copy->GetLocalPropertyNames(names, 0);
136 for (int i = 0; i < names->length(); i++) {
137 ASSERT(names->get(i)->IsString());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000138 String* key_string = String::cast(names->get(i));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000139 PropertyAttributes attributes =
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000140 copy->GetLocalPropertyAttribute(key_string);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000141 // Only deep copy fields from the object literal expression.
142 // In particular, don't try to copy the length attribute of
143 // an array.
144 if (attributes != NONE) continue;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000145 Object* value = copy->GetProperty(key_string, &attributes);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000146 ASSERT(!value->IsFailure());
147 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000148 JSObject* js_object = JSObject::cast(value);
149 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000150 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000151 result = copy->SetProperty(key_string, result, NONE);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000152 if (result->IsFailure()) return result;
153 }
154 }
155 }
156
157 // Deep copy local elements.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000158 // Pixel elements cannot be created using an object literal.
ager@chromium.org3811b432009-10-28 14:53:37 +0000159 ASSERT(!copy->HasPixelElements() && !copy->HasExternalArrayElements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000160 switch (copy->GetElementsKind()) {
161 case JSObject::FAST_ELEMENTS: {
162 FixedArray* elements = FixedArray::cast(copy->elements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000163 for (int i = 0; i < elements->length(); i++) {
164 Object* value = elements->get(i);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000165 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000166 JSObject* js_object = JSObject::cast(value);
167 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000168 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000169 elements->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000170 }
171 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000172 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000173 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000174 case JSObject::DICTIONARY_ELEMENTS: {
175 NumberDictionary* element_dictionary = copy->element_dictionary();
176 int capacity = element_dictionary->Capacity();
177 for (int i = 0; i < capacity; i++) {
178 Object* k = element_dictionary->KeyAt(i);
179 if (element_dictionary->IsKey(k)) {
180 Object* value = element_dictionary->ValueAt(i);
181 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000182 JSObject* js_object = JSObject::cast(value);
183 result = DeepCopyBoilerplate(js_object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000184 if (result->IsFailure()) return result;
185 element_dictionary->ValueAtPut(i, result);
186 }
187 }
188 }
189 break;
190 }
191 default:
192 UNREACHABLE();
193 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000194 }
195 return copy;
196}
197
198
199static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
200 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
201 return DeepCopyBoilerplate(boilerplate);
202}
203
204
205static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000206 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000207 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000208}
209
210
ager@chromium.org236ad962008-09-25 09:45:57 +0000211static Handle<Map> ComputeObjectLiteralMap(
212 Handle<Context> context,
213 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000214 bool* is_result_from_cache) {
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +0000215 int properties_length = constant_properties->length();
216 int number_of_properties = properties_length / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000217 if (FLAG_canonicalize_object_literal_maps) {
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +0000218 // Check that there are only symbols and array indices among keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000219 int number_of_symbol_keys = 0;
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +0000220 for (int p = 0; p != properties_length; p += 2) {
221 Object* key = constant_properties->get(p);
222 uint32_t element_index = 0;
223 if (key->IsSymbol()) {
224 number_of_symbol_keys++;
225 } else if (key->ToArrayIndex(&element_index)) {
226 // An index key does not require space in the property backing store.
227 number_of_properties--;
228 } else {
229 // Bail out as a non-symbol non-index key makes caching impossible.
230 // ASSERT to make sure that the if condition after the loop is false.
231 ASSERT(number_of_symbol_keys != number_of_properties);
232 break;
233 }
ager@chromium.org236ad962008-09-25 09:45:57 +0000234 }
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +0000235 // If we only have symbols and array indices among keys then we can
236 // use the map cache in the global context.
ager@chromium.org236ad962008-09-25 09:45:57 +0000237 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000238 if ((number_of_symbol_keys == number_of_properties) &&
239 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000240 // Create the fixed array with the key.
241 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +0000242 if (number_of_symbol_keys > 0) {
243 int index = 0;
244 for (int p = 0; p < properties_length; p += 2) {
245 Object* key = constant_properties->get(p);
246 if (key->IsSymbol()) {
247 keys->set(index++, key);
248 }
249 }
250 ASSERT(index == number_of_symbol_keys);
ager@chromium.org236ad962008-09-25 09:45:57 +0000251 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000252 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000253 return Factory::ObjectLiteralMapFromCache(context, keys);
254 }
255 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000256 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000257 return Factory::CopyMap(
258 Handle<Map>(context->object_function()->initial_map()),
259 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000260}
261
262
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000263static Handle<Object> CreateLiteralBoilerplate(
264 Handle<FixedArray> literals,
265 Handle<FixedArray> constant_properties);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000266
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000267
268static Handle<Object> CreateObjectLiteralBoilerplate(
269 Handle<FixedArray> literals,
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000270 Handle<FixedArray> constant_properties,
271 bool should_have_fast_elements) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000272 // Get the global context from the literals array. This is the
273 // context in which the function was created and we use the object
274 // function from this context to create the object literal. We do
275 // not use the object function from the current global context
276 // because this might be the object function from another context
277 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000278 Handle<Context> context =
279 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
280
281 bool is_result_from_cache;
282 Handle<Map> map = ComputeObjectLiteralMap(context,
283 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000284 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000285
ager@chromium.org236ad962008-09-25 09:45:57 +0000286 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000287
288 // Normalize the elements of the boilerplate to save space if needed.
289 if (!should_have_fast_elements) NormalizeElements(boilerplate);
290
ager@chromium.org32912102009-01-16 10:38:43 +0000291 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000292 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000293 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000294 length / 2,
ager@chromium.org236ad962008-09-25 09:45:57 +0000295 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000296 for (int index = 0; index < length; index +=2) {
297 Handle<Object> key(constant_properties->get(index+0));
298 Handle<Object> value(constant_properties->get(index+1));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000299 if (value->IsFixedArray()) {
300 // The value contains the constant_properties of a
301 // simple object literal.
302 Handle<FixedArray> array = Handle<FixedArray>::cast(value);
303 value = CreateLiteralBoilerplate(literals, array);
304 if (value.is_null()) return value;
305 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000306 Handle<Object> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000307 uint32_t element_index = 0;
308 if (key->IsSymbol()) {
309 // If key is a symbol it is not an array element.
310 Handle<String> name(String::cast(*key));
311 ASSERT(!name->AsArrayIndex(&element_index));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000312 result = SetProperty(boilerplate, name, value, NONE);
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000313 } else if (key->ToArrayIndex(&element_index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000314 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000315 result = SetElement(boilerplate, element_index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000316 } else {
317 // Non-uint32 number.
318 ASSERT(key->IsNumber());
319 double num = key->Number();
320 char arr[100];
321 Vector<char> buffer(arr, ARRAY_SIZE(arr));
322 const char* str = DoubleToCString(num, buffer);
323 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000324 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000325 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000326 // If setting the property on the boilerplate throws an
327 // exception, the exception is converted to an empty handle in
328 // the handle based operations. In that case, we need to
329 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000330 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000331 }
332 }
333
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000334 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000335}
336
337
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000338static Handle<Object> CreateArrayLiteralBoilerplate(
339 Handle<FixedArray> literals,
340 Handle<FixedArray> elements) {
341 // Create the JSArray.
342 Handle<JSFunction> constructor(
343 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
344 Handle<Object> object = Factory::NewJSObject(constructor);
345
346 Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
347
348 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
349 for (int i = 0; i < content->length(); i++) {
350 if (content->get(i)->IsFixedArray()) {
351 // The value contains the constant_properties of a
352 // simple object literal.
353 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
354 Handle<Object> result =
355 CreateLiteralBoilerplate(literals, fa);
356 if (result.is_null()) return result;
357 content->set(i, *result);
358 }
359 }
360
361 // Set the elements.
362 Handle<JSArray>::cast(object)->SetContent(*content);
363 return object;
364}
365
366
367static Handle<Object> CreateLiteralBoilerplate(
368 Handle<FixedArray> literals,
369 Handle<FixedArray> array) {
370 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
371 switch (CompileTimeValue::GetType(array)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000372 case CompileTimeValue::OBJECT_LITERAL_FAST_ELEMENTS:
373 return CreateObjectLiteralBoilerplate(literals, elements, true);
374 case CompileTimeValue::OBJECT_LITERAL_SLOW_ELEMENTS:
375 return CreateObjectLiteralBoilerplate(literals, elements, false);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000376 case CompileTimeValue::ARRAY_LITERAL:
377 return CreateArrayLiteralBoilerplate(literals, elements);
378 default:
379 UNREACHABLE();
380 return Handle<Object>::null();
381 }
382}
383
384
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000385static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000386 // Takes a FixedArray of elements containing the literal elements of
387 // the array literal and produces JSArray with those elements.
388 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000389 // which contains the context from which to get the Array function
390 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000391 HandleScope scope;
392 ASSERT(args.length() == 3);
393 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
394 CONVERT_SMI_CHECKED(literals_index, args[1]);
395 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000396
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000397 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
398 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000399
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000400 // Update the functions literal and return the boilerplate.
401 literals->set(literals_index, *object);
402 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000403}
404
405
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000406static Object* Runtime_CreateObjectLiteral(Arguments args) {
407 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000408 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000409 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
410 CONVERT_SMI_CHECKED(literals_index, args[1]);
411 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000412 CONVERT_SMI_CHECKED(fast_elements, args[3]);
413 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000414
415 // Check if boilerplate exists. If not, create it first.
416 Handle<Object> boilerplate(literals->get(literals_index));
417 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000418 boilerplate = CreateObjectLiteralBoilerplate(literals,
419 constant_properties,
420 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000421 if (boilerplate.is_null()) return Failure::Exception();
422 // Update the functions literal and return the boilerplate.
423 literals->set(literals_index, *boilerplate);
424 }
425 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
426}
427
428
429static Object* Runtime_CreateObjectLiteralShallow(Arguments args) {
430 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000431 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000432 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
433 CONVERT_SMI_CHECKED(literals_index, args[1]);
434 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000435 CONVERT_SMI_CHECKED(fast_elements, args[3]);
436 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000437
438 // Check if boilerplate exists. If not, create it first.
439 Handle<Object> boilerplate(literals->get(literals_index));
440 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000441 boilerplate = CreateObjectLiteralBoilerplate(literals,
442 constant_properties,
443 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000444 if (boilerplate.is_null()) return Failure::Exception();
445 // Update the functions literal and return the boilerplate.
446 literals->set(literals_index, *boilerplate);
447 }
448 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
449}
450
451
452static Object* Runtime_CreateArrayLiteral(Arguments args) {
453 HandleScope scope;
454 ASSERT(args.length() == 3);
455 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
456 CONVERT_SMI_CHECKED(literals_index, args[1]);
457 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
458
459 // Check if boilerplate exists. If not, create it first.
460 Handle<Object> boilerplate(literals->get(literals_index));
461 if (*boilerplate == Heap::undefined_value()) {
462 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
463 if (boilerplate.is_null()) return Failure::Exception();
464 // Update the functions literal and return the boilerplate.
465 literals->set(literals_index, *boilerplate);
466 }
467 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
468}
469
470
471static Object* Runtime_CreateArrayLiteralShallow(Arguments args) {
472 HandleScope scope;
473 ASSERT(args.length() == 3);
474 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
475 CONVERT_SMI_CHECKED(literals_index, args[1]);
476 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
477
478 // Check if boilerplate exists. If not, create it first.
479 Handle<Object> boilerplate(literals->get(literals_index));
480 if (*boilerplate == Heap::undefined_value()) {
481 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
482 if (boilerplate.is_null()) return Failure::Exception();
483 // Update the functions literal and return the boilerplate.
484 literals->set(literals_index, *boilerplate);
485 }
486 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
487}
488
489
ager@chromium.org32912102009-01-16 10:38:43 +0000490static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
491 ASSERT(args.length() == 2);
492 CONVERT_CHECKED(String, key, args[0]);
493 Object* value = args[1];
494 // Create a catch context extension object.
495 JSFunction* constructor =
496 Top::context()->global_context()->context_extension_function();
497 Object* object = Heap::AllocateJSObject(constructor);
498 if (object->IsFailure()) return object;
499 // Assign the exception value to the catch variable and make sure
500 // that the catch variable is DontDelete.
501 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
502 if (value->IsFailure()) return value;
503 return object;
504}
505
506
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000507static Object* Runtime_ClassOf(Arguments args) {
508 NoHandleAllocation ha;
509 ASSERT(args.length() == 1);
510 Object* obj = args[0];
511 if (!obj->IsJSObject()) return Heap::null_value();
512 return JSObject::cast(obj)->class_name();
513}
514
ager@chromium.org7c537e22008-10-16 08:43:32 +0000515
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000516static Object* Runtime_IsInPrototypeChain(Arguments args) {
517 NoHandleAllocation ha;
518 ASSERT(args.length() == 2);
519 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
520 Object* O = args[0];
521 Object* V = args[1];
522 while (true) {
523 Object* prototype = V->GetPrototype();
524 if (prototype->IsNull()) return Heap::false_value();
525 if (O == prototype) return Heap::true_value();
526 V = prototype;
527 }
528}
529
530
ager@chromium.org9085a012009-05-11 19:22:57 +0000531// Inserts an object as the hidden prototype of another object.
532static Object* Runtime_SetHiddenPrototype(Arguments args) {
533 NoHandleAllocation ha;
534 ASSERT(args.length() == 2);
535 CONVERT_CHECKED(JSObject, jsobject, args[0]);
536 CONVERT_CHECKED(JSObject, proto, args[1]);
537
538 // Sanity checks. The old prototype (that we are replacing) could
539 // theoretically be null, but if it is not null then check that we
540 // didn't already install a hidden prototype here.
541 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
542 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
543 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
544
545 // Allocate up front before we start altering state in case we get a GC.
546 Object* map_or_failure = proto->map()->CopyDropTransitions();
547 if (map_or_failure->IsFailure()) return map_or_failure;
548 Map* new_proto_map = Map::cast(map_or_failure);
549
550 map_or_failure = jsobject->map()->CopyDropTransitions();
551 if (map_or_failure->IsFailure()) return map_or_failure;
552 Map* new_map = Map::cast(map_or_failure);
553
554 // Set proto's prototype to be the old prototype of the object.
555 new_proto_map->set_prototype(jsobject->GetPrototype());
556 proto->set_map(new_proto_map);
557 new_proto_map->set_is_hidden_prototype();
558
559 // Set the object's prototype to proto.
560 new_map->set_prototype(proto);
561 jsobject->set_map(new_map);
562
563 return Heap::undefined_value();
564}
565
566
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000567static Object* Runtime_IsConstructCall(Arguments args) {
568 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000569 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000570 JavaScriptFrameIterator it;
571 return Heap::ToBoolean(it.frame()->IsConstructor());
572}
573
574
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000575// Recursively traverses hidden prototypes if property is not found
576static void GetOwnPropertyImplementation(JSObject* obj,
577 String* name,
578 LookupResult* result) {
579 obj->LocalLookupRealNamedProperty(name, result);
580
581 if (!result->IsProperty()) {
582 Object* proto = obj->GetPrototype();
583 if (proto->IsJSObject() &&
584 JSObject::cast(proto)->map()->is_hidden_prototype())
585 GetOwnPropertyImplementation(JSObject::cast(proto),
586 name, result);
587 }
588}
589
590
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000591// Enumerator used as indices into the array returned from GetOwnProperty
592enum PropertyDescriptorIndices {
593 IS_ACCESSOR_INDEX,
594 VALUE_INDEX,
595 GETTER_INDEX,
596 SETTER_INDEX,
597 WRITABLE_INDEX,
598 ENUMERABLE_INDEX,
599 CONFIGURABLE_INDEX,
600 DESCRIPTOR_SIZE
601};
602
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000603// Returns an array with the property description:
604// if args[1] is not a property on args[0]
605// returns undefined
606// if args[1] is a data property on args[0]
607// [false, value, Writeable, Enumerable, Configurable]
608// if args[1] is an accessor on args[0]
609// [true, GetFunction, SetFunction, Enumerable, Configurable]
610static Object* Runtime_GetOwnProperty(Arguments args) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000611 ASSERT(args.length() == 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000612 HandleScope scope;
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000613 Handle<FixedArray> elms = Factory::NewFixedArray(DESCRIPTOR_SIZE);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000614 Handle<JSArray> desc = Factory::NewJSArrayWithElements(elms);
615 LookupResult result;
616 CONVERT_CHECKED(JSObject, obj, args[0]);
617 CONVERT_CHECKED(String, name, args[1]);
618
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000619 // This could be an element.
620 uint32_t index;
621 if (name->AsArrayIndex(&index)) {
622 if (!obj->HasLocalElement(index)) {
623 return Heap::undefined_value();
624 }
625
626 // Special handling of string objects according to ECMAScript 5 15.5.5.2.
627 // Note that this might be a string object with elements other than the
628 // actual string value. This is covered by the subsequent cases.
629 if (obj->IsStringObjectWithCharacterAt(index)) {
630 JSValue* js_value = JSValue::cast(obj);
631 String* str = String::cast(js_value->value());
632 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
633 elms->set(VALUE_INDEX, str->SubString(index, index+1));
634 elms->set(WRITABLE_INDEX, Heap::false_value());
635 elms->set(ENUMERABLE_INDEX, Heap::false_value());
636 elms->set(CONFIGURABLE_INDEX, Heap::false_value());
637 return *desc;
638 }
639
640 // This can potentially be an element in the elements dictionary or
641 // a fast element.
642 if (obj->HasDictionaryElements()) {
643 NumberDictionary* dictionary = obj->element_dictionary();
644 int entry = dictionary->FindEntry(index);
645 PropertyDetails details = dictionary->DetailsAt(entry);
646 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
647 elms->set(VALUE_INDEX, dictionary->ValueAt(entry));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000648 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!details.IsReadOnly()));
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000649 elms->set(ENUMERABLE_INDEX, Heap::ToBoolean(!details.IsDontEnum()));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000650 elms->set(CONFIGURABLE_INDEX, Heap::ToBoolean(!details.IsDontDelete()));
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000651 return *desc;
652 } else {
653 // Elements that are stored as array elements always has:
654 // writable: true, configurable: true, enumerable: true.
655 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
656 elms->set(VALUE_INDEX, obj->GetElement(index));
657 elms->set(WRITABLE_INDEX, Heap::true_value());
658 elms->set(ENUMERABLE_INDEX, Heap::true_value());
659 elms->set(CONFIGURABLE_INDEX, Heap::true_value());
660 return *desc;
661 }
662 }
663
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000664 // Use recursive implementation to also traverse hidden prototypes
665 GetOwnPropertyImplementation(obj, name, &result);
666
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000667 if (!result.IsProperty()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000668 return Heap::undefined_value();
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000669 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000670 if (result.type() == CALLBACKS) {
671 Object* structure = result.GetCallbackObject();
ager@chromium.org5c838252010-02-19 08:53:10 +0000672 if (structure->IsProxy() || structure->IsAccessorInfo()) {
673 // Property that is internally implemented as a callback or
674 // an API defined callback.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000675 Object* value = obj->GetPropertyWithCallback(
676 obj, structure, name, result.holder());
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000677 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
678 elms->set(VALUE_INDEX, value);
679 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!result.IsReadOnly()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000680 } else if (structure->IsFixedArray()) {
681 // __defineGetter__/__defineSetter__ callback.
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000682 elms->set(IS_ACCESSOR_INDEX, Heap::true_value());
683 elms->set(GETTER_INDEX, FixedArray::cast(structure)->get(0));
684 elms->set(SETTER_INDEX, FixedArray::cast(structure)->get(1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000685 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000686 return Heap::undefined_value();
687 }
688 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000689 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
690 elms->set(VALUE_INDEX, result.GetLazyValue());
691 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!result.IsReadOnly()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000692 }
693
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000694 elms->set(ENUMERABLE_INDEX, Heap::ToBoolean(!result.IsDontEnum()));
695 elms->set(CONFIGURABLE_INDEX, Heap::ToBoolean(!result.IsDontDelete()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000696 return *desc;
697}
698
699
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +0000700static Object* Runtime_PreventExtensions(Arguments args) {
701 ASSERT(args.length() == 1);
702 CONVERT_CHECKED(JSObject, obj, args[0]);
703 return obj->PreventExtensions();
704}
705
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000706static Object* Runtime_IsExtensible(Arguments args) {
707 ASSERT(args.length() == 1);
708 CONVERT_CHECKED(JSObject, obj, args[0]);
709 return obj->map()->is_extensible() ? Heap::true_value()
710 : Heap::false_value();
711}
712
713
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000714static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000715 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000716 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000717 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
718 CONVERT_ARG_CHECKED(String, pattern, 1);
719 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000720 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
721 if (result.is_null()) return Failure::Exception();
722 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000723}
724
725
726static Object* Runtime_CreateApiFunction(Arguments args) {
727 HandleScope scope;
728 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000729 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000730 return *Factory::CreateApiFunction(data);
731}
732
733
734static Object* Runtime_IsTemplate(Arguments args) {
735 ASSERT(args.length() == 1);
736 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000737 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000738 return Heap::ToBoolean(result);
739}
740
741
742static Object* Runtime_GetTemplateField(Arguments args) {
743 ASSERT(args.length() == 2);
744 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000745 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000746 int index = field->value();
747 int offset = index * kPointerSize + HeapObject::kHeaderSize;
748 InstanceType type = templ->map()->instance_type();
749 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
750 type == OBJECT_TEMPLATE_INFO_TYPE);
751 RUNTIME_ASSERT(offset > 0);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000752 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000753 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
754 } else {
755 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
756 }
757 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000758}
759
760
ager@chromium.org870a0b62008-11-04 11:43:05 +0000761static Object* Runtime_DisableAccessChecks(Arguments args) {
762 ASSERT(args.length() == 1);
763 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000764 Map* old_map = object->map();
765 bool needs_access_checks = old_map->is_access_check_needed();
766 if (needs_access_checks) {
767 // Copy map so it won't interfere constructor's initial map.
768 Object* new_map = old_map->CopyDropTransitions();
769 if (new_map->IsFailure()) return new_map;
770
771 Map::cast(new_map)->set_is_access_check_needed(false);
772 object->set_map(Map::cast(new_map));
773 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000774 return needs_access_checks ? Heap::true_value() : Heap::false_value();
775}
776
777
778static Object* Runtime_EnableAccessChecks(Arguments args) {
779 ASSERT(args.length() == 1);
780 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000781 Map* old_map = object->map();
782 if (!old_map->is_access_check_needed()) {
783 // Copy map so it won't interfere constructor's initial map.
784 Object* new_map = old_map->CopyDropTransitions();
785 if (new_map->IsFailure()) return new_map;
786
787 Map::cast(new_map)->set_is_access_check_needed(true);
788 object->set_map(Map::cast(new_map));
789 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000790 return Heap::undefined_value();
791}
792
793
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000794static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
795 HandleScope scope;
796 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
797 Handle<Object> args[2] = { type_handle, name };
798 Handle<Object> error =
799 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
800 return Top::Throw(*error);
801}
802
803
804static Object* Runtime_DeclareGlobals(Arguments args) {
805 HandleScope scope;
806 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
807
ager@chromium.org3811b432009-10-28 14:53:37 +0000808 Handle<Context> context = args.at<Context>(0);
809 CONVERT_ARG_CHECKED(FixedArray, pairs, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000810 bool is_eval = Smi::cast(args[2])->value() == 1;
811
812 // Compute the property attributes. According to ECMA-262, section
813 // 13, page 71, the property must be read-only and
814 // non-deletable. However, neither SpiderMonkey nor KJS creates the
815 // property as read-only, so we don't either.
816 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
817
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000818 // Traverse the name/value pairs and set the properties.
819 int length = pairs->length();
820 for (int i = 0; i < length; i += 2) {
821 HandleScope scope;
822 Handle<String> name(String::cast(pairs->get(i)));
823 Handle<Object> value(pairs->get(i + 1));
824
825 // We have to declare a global const property. To capture we only
826 // assign to it when evaluating the assignment for "const x =
827 // <expr>" the initial value is the hole.
828 bool is_const_property = value->IsTheHole();
829
830 if (value->IsUndefined() || is_const_property) {
831 // Lookup the property in the global object, and don't set the
832 // value of the variable if the property is already there.
833 LookupResult lookup;
834 global->Lookup(*name, &lookup);
835 if (lookup.IsProperty()) {
836 // Determine if the property is local by comparing the holder
837 // against the global object. The information will be used to
838 // avoid throwing re-declaration errors when declaring
839 // variables or constants that exist in the prototype chain.
840 bool is_local = (*global == lookup.holder());
841 // Get the property attributes and determine if the property is
842 // read-only.
843 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
844 bool is_read_only = (attributes & READ_ONLY) != 0;
845 if (lookup.type() == INTERCEPTOR) {
846 // If the interceptor says the property is there, we
847 // just return undefined without overwriting the property.
848 // Otherwise, we continue to setting the property.
849 if (attributes != ABSENT) {
850 // Check if the existing property conflicts with regards to const.
851 if (is_local && (is_read_only || is_const_property)) {
852 const char* type = (is_read_only) ? "const" : "var";
853 return ThrowRedeclarationError(type, name);
854 };
855 // The property already exists without conflicting: Go to
856 // the next declaration.
857 continue;
858 }
859 // Fall-through and introduce the absent property by using
860 // SetProperty.
861 } else {
862 if (is_local && (is_read_only || is_const_property)) {
863 const char* type = (is_read_only) ? "const" : "var";
864 return ThrowRedeclarationError(type, name);
865 }
866 // The property already exists without conflicting: Go to
867 // the next declaration.
868 continue;
869 }
870 }
871 } else {
872 // Copy the function and update its context. Use it as value.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000873 Handle<SharedFunctionInfo> shared =
874 Handle<SharedFunctionInfo>::cast(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000875 Handle<JSFunction> function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000876 Factory::NewFunctionFromSharedFunctionInfo(shared, context, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000877 value = function;
878 }
879
880 LookupResult lookup;
881 global->LocalLookup(*name, &lookup);
882
883 PropertyAttributes attributes = is_const_property
884 ? static_cast<PropertyAttributes>(base | READ_ONLY)
885 : base;
886
887 if (lookup.IsProperty()) {
888 // There's a local property that we need to overwrite because
889 // we're either declaring a function or there's an interceptor
890 // that claims the property is absent.
891
892 // Check for conflicting re-declarations. We cannot have
893 // conflicting types in case of intercepted properties because
894 // they are absent.
895 if (lookup.type() != INTERCEPTOR &&
896 (lookup.IsReadOnly() || is_const_property)) {
897 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
898 return ThrowRedeclarationError(type, name);
899 }
900 SetProperty(global, name, value, attributes);
901 } else {
902 // If a property with this name does not already exist on the
903 // global object add the property locally. We take special
904 // precautions to always add it as a local property even in case
905 // of callbacks in the prototype chain (this rules out using
906 // SetProperty). Also, we must use the handle-based version to
907 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000908 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000909 }
910 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000911
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000912 return Heap::undefined_value();
913}
914
915
916static Object* Runtime_DeclareContextSlot(Arguments args) {
917 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000918 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000919
ager@chromium.org7c537e22008-10-16 08:43:32 +0000920 CONVERT_ARG_CHECKED(Context, context, 0);
921 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000922 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000923 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000924 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000925 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000926
927 // Declarations are always done in the function context.
928 context = Handle<Context>(context->fcontext());
929
930 int index;
931 PropertyAttributes attributes;
932 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000933 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000934 context->Lookup(name, flags, &index, &attributes);
935
936 if (attributes != ABSENT) {
937 // The name was declared before; check for conflicting
938 // re-declarations: This is similar to the code in parser.cc in
939 // the AstBuildingParser::Declare function.
940 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
941 // Functions are not read-only.
942 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
943 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
944 return ThrowRedeclarationError(type, name);
945 }
946
947 // Initialize it if necessary.
948 if (*initial_value != NULL) {
949 if (index >= 0) {
950 // The variable or constant context slot should always be in
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000951 // the function context or the arguments object.
952 if (holder->IsContext()) {
953 ASSERT(holder.is_identical_to(context));
954 if (((attributes & READ_ONLY) == 0) ||
955 context->get(index)->IsTheHole()) {
956 context->set(index, *initial_value);
957 }
958 } else {
959 Handle<JSObject>::cast(holder)->SetElement(index, *initial_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000960 }
961 } else {
962 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000963 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000964 SetProperty(context_ext, name, initial_value, mode);
965 }
966 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000967
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000968 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000969 // The property is not in the function context. It needs to be
970 // "declared" in the function context's extension context, or in the
971 // global context.
972 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000973 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000974 // The function context's extension context exists - use it.
975 context_ext = Handle<JSObject>(context->extension());
976 } else {
977 // The function context's extension context does not exists - allocate
978 // it.
979 context_ext = Factory::NewJSObject(Top::context_extension_function());
980 // And store it in the extension slot.
981 context->set_extension(*context_ext);
982 }
983 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000984
ager@chromium.org7c537e22008-10-16 08:43:32 +0000985 // Declare the property by setting it to the initial value if provided,
986 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
987 // constant declarations).
988 ASSERT(!context_ext->HasLocalProperty(*name));
989 Handle<Object> value(Heap::undefined_value());
990 if (*initial_value != NULL) value = initial_value;
991 SetProperty(context_ext, name, value, mode);
992 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
993 }
994
995 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000996}
997
998
999static Object* Runtime_InitializeVarGlobal(Arguments args) {
1000 NoHandleAllocation nha;
1001
1002 // Determine if we need to assign to the variable if it already
1003 // exists (based on the number of arguments).
1004 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
1005 bool assign = args.length() == 2;
1006
1007 CONVERT_ARG_CHECKED(String, name, 0);
1008 GlobalObject* global = Top::context()->global();
1009
1010 // According to ECMA-262, section 12.2, page 62, the property must
1011 // not be deletable.
1012 PropertyAttributes attributes = DONT_DELETE;
1013
1014 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001015 // there, there is a property with this name in the prototype chain.
1016 // We follow Safari and Firefox behavior and only set the property
1017 // locally if there is an explicit initialization value that we have
1018 // to assign to the property. When adding the property we take
1019 // special precautions to always add it as a local property even in
1020 // case of callbacks in the prototype chain (this rules out using
1021 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
1022 // this.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001023 // Note that objects can have hidden prototypes, so we need to traverse
1024 // the whole chain of hidden prototypes to do a 'local' lookup.
1025 JSObject* real_holder = global;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001026 LookupResult lookup;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001027 while (true) {
1028 real_holder->LocalLookup(*name, &lookup);
1029 if (lookup.IsProperty()) {
1030 // Determine if this is a redeclaration of something read-only.
1031 if (lookup.IsReadOnly()) {
1032 // If we found readonly property on one of hidden prototypes,
1033 // just shadow it.
1034 if (real_holder != Top::context()->global()) break;
1035 return ThrowRedeclarationError("const", name);
1036 }
1037
1038 // Determine if this is a redeclaration of an intercepted read-only
1039 // property and figure out if the property exists at all.
1040 bool found = true;
1041 PropertyType type = lookup.type();
1042 if (type == INTERCEPTOR) {
1043 HandleScope handle_scope;
1044 Handle<JSObject> holder(real_holder);
1045 PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
1046 real_holder = *holder;
1047 if (intercepted == ABSENT) {
1048 // The interceptor claims the property isn't there. We need to
1049 // make sure to introduce it.
1050 found = false;
1051 } else if ((intercepted & READ_ONLY) != 0) {
1052 // The property is present, but read-only. Since we're trying to
1053 // overwrite it with a variable declaration we must throw a
1054 // re-declaration error. However if we found readonly property
1055 // on one of hidden prototypes, just shadow it.
1056 if (real_holder != Top::context()->global()) break;
1057 return ThrowRedeclarationError("const", name);
1058 }
1059 }
1060
1061 if (found && !assign) {
1062 // The global property is there and we're not assigning any value
1063 // to it. Just return.
1064 return Heap::undefined_value();
1065 }
1066
1067 // Assign the value (or undefined) to the property.
1068 Object* value = (assign) ? args[1] : Heap::undefined_value();
1069 return real_holder->SetProperty(&lookup, *name, value, attributes);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001070 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001071
1072 Object* proto = real_holder->GetPrototype();
1073 if (!proto->IsJSObject())
1074 break;
1075
1076 if (!JSObject::cast(proto)->map()->is_hidden_prototype())
1077 break;
1078
1079 real_holder = JSObject::cast(proto);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001080 }
1081
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001082 global = Top::context()->global();
1083 if (assign) {
1084 return global->IgnoreAttributesAndSetLocalProperty(*name,
1085 args[1],
1086 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001087 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001088 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001089}
1090
1091
1092static Object* Runtime_InitializeConstGlobal(Arguments args) {
1093 // All constants are declared with an initial value. The name
1094 // of the constant is the first argument and the initial value
1095 // is the second.
1096 RUNTIME_ASSERT(args.length() == 2);
1097 CONVERT_ARG_CHECKED(String, name, 0);
1098 Handle<Object> value = args.at<Object>(1);
1099
1100 // Get the current global object from top.
1101 GlobalObject* global = Top::context()->global();
1102
1103 // According to ECMA-262, section 12.2, page 62, the property must
1104 // not be deletable. Since it's a const, it must be READ_ONLY too.
1105 PropertyAttributes attributes =
1106 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
1107
1108 // Lookup the property locally in the global object. If it isn't
1109 // there, we add the property and take special precautions to always
1110 // add it as a local property even in case of callbacks in the
1111 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001112 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001113 LookupResult lookup;
1114 global->LocalLookup(*name, &lookup);
1115 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001116 return global->IgnoreAttributesAndSetLocalProperty(*name,
1117 *value,
1118 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001119 }
1120
1121 // Determine if this is a redeclaration of something not
1122 // read-only. In case the result is hidden behind an interceptor we
1123 // need to ask it for the property attributes.
1124 if (!lookup.IsReadOnly()) {
1125 if (lookup.type() != INTERCEPTOR) {
1126 return ThrowRedeclarationError("var", name);
1127 }
1128
1129 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
1130
1131 // Throw re-declaration error if the intercepted property is present
1132 // but not read-only.
1133 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
1134 return ThrowRedeclarationError("var", name);
1135 }
1136
1137 // Restore global object from context (in case of GC) and continue
1138 // with setting the value because the property is either absent or
1139 // read-only. We also have to do redo the lookup.
1140 global = Top::context()->global();
1141
1142 // BUG 1213579: Handle the case where we have to set a read-only
1143 // property through an interceptor and only do it if it's
1144 // uninitialized, e.g. the hole. Nirk...
1145 global->SetProperty(*name, *value, attributes);
1146 return *value;
1147 }
1148
1149 // Set the value, but only we're assigning the initial value to a
1150 // constant. For now, we determine this by checking if the
1151 // current value is the hole.
1152 PropertyType type = lookup.type();
1153 if (type == FIELD) {
1154 FixedArray* properties = global->properties();
1155 int index = lookup.GetFieldIndex();
1156 if (properties->get(index)->IsTheHole()) {
1157 properties->set(index, *value);
1158 }
1159 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001160 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
1161 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001162 }
1163 } else {
1164 // Ignore re-initialization of constants that have already been
1165 // assigned a function value.
1166 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
1167 }
1168
1169 // Use the set value as the result of the operation.
1170 return *value;
1171}
1172
1173
1174static Object* Runtime_InitializeConstContextSlot(Arguments args) {
1175 HandleScope scope;
1176 ASSERT(args.length() == 3);
1177
1178 Handle<Object> value(args[0]);
1179 ASSERT(!value->IsTheHole());
1180 CONVERT_ARG_CHECKED(Context, context, 1);
1181 Handle<String> name(String::cast(args[2]));
1182
1183 // Initializations are always done in the function context.
1184 context = Handle<Context>(context->fcontext());
1185
1186 int index;
1187 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001188 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001189 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001190 context->Lookup(name, flags, &index, &attributes);
1191
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001192 // In most situations, the property introduced by the const
1193 // declaration should be present in the context extension object.
1194 // However, because declaration and initialization are separate, the
1195 // property might have been deleted (if it was introduced by eval)
1196 // before we reach the initialization point.
1197 //
1198 // Example:
1199 //
1200 // function f() { eval("delete x; const x;"); }
1201 //
1202 // In that case, the initialization behaves like a normal assignment
1203 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001204 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001205 // Property was found in a context.
1206 if (holder->IsContext()) {
1207 // The holder cannot be the function context. If it is, there
1208 // should have been a const redeclaration error when declaring
1209 // the const property.
1210 ASSERT(!holder.is_identical_to(context));
1211 if ((attributes & READ_ONLY) == 0) {
1212 Handle<Context>::cast(holder)->set(index, *value);
1213 }
1214 } else {
1215 // The holder is an arguments object.
1216 ASSERT((attributes & READ_ONLY) == 0);
1217 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001218 }
1219 return *value;
1220 }
1221
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001222 // The property could not be found, we introduce it in the global
1223 // context.
1224 if (attributes == ABSENT) {
1225 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1226 SetProperty(global, name, value, NONE);
1227 return *value;
1228 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001229
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001230 // The property was present in a context extension object.
1231 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001232
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001233 if (*context_ext == context->extension()) {
1234 // This is the property that was introduced by the const
1235 // declaration. Set it if it hasn't been set before. NOTE: We
1236 // cannot use GetProperty() to get the current value as it
1237 // 'unholes' the value.
1238 LookupResult lookup;
1239 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1240 ASSERT(lookup.IsProperty()); // the property was declared
1241 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1242
1243 PropertyType type = lookup.type();
1244 if (type == FIELD) {
1245 FixedArray* properties = context_ext->properties();
1246 int index = lookup.GetFieldIndex();
1247 if (properties->get(index)->IsTheHole()) {
1248 properties->set(index, *value);
1249 }
1250 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001251 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
1252 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001253 }
1254 } else {
1255 // We should not reach here. Any real, named property should be
1256 // either a field or a dictionary slot.
1257 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001258 }
1259 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001260 // The property was found in a different context extension object.
1261 // Set it if it is not a read-only property.
1262 if ((attributes & READ_ONLY) == 0) {
1263 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1264 // Setting a property might throw an exception. Exceptions
1265 // are converted to empty handles in handle operations. We
1266 // need to convert back to exceptions here.
1267 if (set.is_null()) {
1268 ASSERT(Top::has_pending_exception());
1269 return Failure::Exception();
1270 }
1271 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001272 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001273
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001274 return *value;
1275}
1276
1277
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001278static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1279 Arguments args) {
1280 HandleScope scope;
1281 ASSERT(args.length() == 2);
1282 CONVERT_ARG_CHECKED(JSObject, object, 0);
1283 CONVERT_SMI_CHECKED(properties, args[1]);
1284 if (object->HasFastProperties()) {
1285 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1286 }
1287 return *object;
1288}
1289
1290
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001291static Object* Runtime_RegExpExec(Arguments args) {
1292 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001293 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001294 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1295 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001296 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001297 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001298 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001299 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001300 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001301 RUNTIME_ASSERT(index >= 0);
1302 RUNTIME_ASSERT(index <= subject->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001303 Counters::regexp_entry_runtime.Increment();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001304 Handle<Object> result = RegExpImpl::Exec(regexp,
1305 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001306 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001307 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001308 if (result.is_null()) return Failure::Exception();
1309 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001310}
1311
1312
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00001313static Object* Runtime_RegExpConstructResult(Arguments args) {
1314 ASSERT(args.length() == 3);
1315 CONVERT_SMI_CHECKED(elements_count, args[0]);
1316 if (elements_count > JSArray::kMaxFastElementsLength) {
1317 return Top::ThrowIllegalOperation();
1318 }
1319 Object* new_object = Heap::AllocateFixedArrayWithHoles(elements_count);
1320 if (new_object->IsFailure()) return new_object;
1321 FixedArray* elements = FixedArray::cast(new_object);
1322 new_object = Heap::AllocateRaw(JSRegExpResult::kSize,
1323 NEW_SPACE,
1324 OLD_POINTER_SPACE);
1325 if (new_object->IsFailure()) return new_object;
1326 {
1327 AssertNoAllocation no_gc;
1328 HandleScope scope;
1329 reinterpret_cast<HeapObject*>(new_object)->
1330 set_map(Top::global_context()->regexp_result_map());
1331 }
1332 JSArray* array = JSArray::cast(new_object);
1333 array->set_properties(Heap::empty_fixed_array());
1334 array->set_elements(elements);
1335 array->set_length(Smi::FromInt(elements_count));
1336 // Write in-object properties after the length of the array.
1337 array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, args[1]);
1338 array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, args[2]);
1339 return array;
1340}
1341
1342
lrn@chromium.org25156de2010-04-06 13:10:27 +00001343static Object* Runtime_RegExpInitializeObject(Arguments args) {
1344 AssertNoAllocation no_alloc;
1345 ASSERT(args.length() == 5);
1346 CONVERT_CHECKED(JSRegExp, regexp, args[0]);
1347 CONVERT_CHECKED(String, source, args[1]);
1348
1349 Object* global = args[2];
1350 if (!global->IsTrue()) global = Heap::false_value();
1351
1352 Object* ignoreCase = args[3];
1353 if (!ignoreCase->IsTrue()) ignoreCase = Heap::false_value();
1354
1355 Object* multiline = args[4];
1356 if (!multiline->IsTrue()) multiline = Heap::false_value();
1357
1358 Map* map = regexp->map();
1359 Object* constructor = map->constructor();
1360 if (constructor->IsJSFunction() &&
1361 JSFunction::cast(constructor)->initial_map() == map) {
1362 // If we still have the original map, set in-object properties directly.
1363 regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, source);
1364 // TODO(lrn): Consider skipping write barrier on booleans as well.
1365 // Both true and false should be in oldspace at all times.
1366 regexp->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, global);
1367 regexp->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex, ignoreCase);
1368 regexp->InObjectPropertyAtPut(JSRegExp::kMultilineFieldIndex, multiline);
1369 regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex,
1370 Smi::FromInt(0),
1371 SKIP_WRITE_BARRIER);
1372 return regexp;
1373 }
1374
1375 // Map has changed, so use generic, but slower, method.
1376 PropertyAttributes final =
1377 static_cast<PropertyAttributes>(READ_ONLY | DONT_ENUM | DONT_DELETE);
1378 PropertyAttributes writable =
1379 static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
1380 regexp->IgnoreAttributesAndSetLocalProperty(Heap::source_symbol(),
1381 source,
1382 final);
1383 regexp->IgnoreAttributesAndSetLocalProperty(Heap::global_symbol(),
1384 global,
1385 final);
1386 regexp->IgnoreAttributesAndSetLocalProperty(Heap::ignore_case_symbol(),
1387 ignoreCase,
1388 final);
1389 regexp->IgnoreAttributesAndSetLocalProperty(Heap::multiline_symbol(),
1390 multiline,
1391 final);
1392 regexp->IgnoreAttributesAndSetLocalProperty(Heap::last_index_symbol(),
1393 Smi::FromInt(0),
1394 writable);
1395 return regexp;
1396}
1397
1398
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001399static Object* Runtime_FinishArrayPrototypeSetup(Arguments args) {
1400 HandleScope scope;
1401 ASSERT(args.length() == 1);
1402 CONVERT_ARG_CHECKED(JSArray, prototype, 0);
1403 // This is necessary to enable fast checks for absence of elements
1404 // on Array.prototype and below.
1405 prototype->set_elements(Heap::empty_fixed_array());
1406 return Smi::FromInt(0);
1407}
1408
1409
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001410static Handle<JSFunction> InstallBuiltin(Handle<JSObject> holder,
1411 const char* name,
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001412 Builtins::Name builtin_name) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001413 Handle<String> key = Factory::LookupAsciiSymbol(name);
1414 Handle<Code> code(Builtins::builtin(builtin_name));
1415 Handle<JSFunction> optimized = Factory::NewFunction(key,
1416 JS_OBJECT_TYPE,
1417 JSObject::kHeaderSize,
1418 code,
1419 false);
1420 optimized->shared()->DontAdaptArguments();
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001421 SetProperty(holder, key, optimized, NONE);
1422 return optimized;
1423}
1424
1425
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001426static Object* Runtime_SpecialArrayFunctions(Arguments args) {
1427 HandleScope scope;
1428 ASSERT(args.length() == 1);
1429 CONVERT_ARG_CHECKED(JSObject, holder, 0);
1430
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001431 InstallBuiltin(holder, "pop", Builtins::ArrayPop);
1432 InstallBuiltin(holder, "push", Builtins::ArrayPush);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001433 InstallBuiltin(holder, "shift", Builtins::ArrayShift);
1434 InstallBuiltin(holder, "unshift", Builtins::ArrayUnshift);
1435 InstallBuiltin(holder, "slice", Builtins::ArraySlice);
1436 InstallBuiltin(holder, "splice", Builtins::ArraySplice);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001437 InstallBuiltin(holder, "concat", Builtins::ArrayConcat);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001438
1439 return *holder;
1440}
1441
1442
ager@chromium.org357bf652010-04-12 11:30:10 +00001443static Object* Runtime_GetGlobalReceiver(Arguments args) {
1444 // Returns a real global receiver, not one of builtins object.
1445 Context* global_context = Top::context()->global()->global_context();
1446 return global_context->global()->global_receiver();
1447}
1448
1449
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001450static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1451 HandleScope scope;
1452 ASSERT(args.length() == 4);
1453 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1454 int index = Smi::cast(args[1])->value();
1455 Handle<String> pattern = args.at<String>(2);
1456 Handle<String> flags = args.at<String>(3);
1457
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001458 // Get the RegExp function from the context in the literals array.
1459 // This is the RegExp function from the context in which the
1460 // function was created. We do not use the RegExp function from the
1461 // current global context because this might be the RegExp function
1462 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001463 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001464 Handle<JSFunction>(
1465 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001466 // Compute the regular expression literal.
1467 bool has_pending_exception;
1468 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001469 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1470 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001471 if (has_pending_exception) {
1472 ASSERT(Top::has_pending_exception());
1473 return Failure::Exception();
1474 }
1475 literals->set(index, *regexp);
1476 return *regexp;
1477}
1478
1479
1480static Object* Runtime_FunctionGetName(Arguments args) {
1481 NoHandleAllocation ha;
1482 ASSERT(args.length() == 1);
1483
1484 CONVERT_CHECKED(JSFunction, f, args[0]);
1485 return f->shared()->name();
1486}
1487
1488
ager@chromium.org236ad962008-09-25 09:45:57 +00001489static Object* Runtime_FunctionSetName(Arguments args) {
1490 NoHandleAllocation ha;
1491 ASSERT(args.length() == 2);
1492
1493 CONVERT_CHECKED(JSFunction, f, args[0]);
1494 CONVERT_CHECKED(String, name, args[1]);
1495 f->shared()->set_name(name);
1496 return Heap::undefined_value();
1497}
1498
1499
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001500static Object* Runtime_FunctionRemovePrototype(Arguments args) {
1501 NoHandleAllocation ha;
1502 ASSERT(args.length() == 1);
1503
1504 CONVERT_CHECKED(JSFunction, f, args[0]);
1505 Object* obj = f->RemovePrototype();
1506 if (obj->IsFailure()) return obj;
1507
1508 return Heap::undefined_value();
1509}
1510
1511
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001512static Object* Runtime_FunctionGetScript(Arguments args) {
1513 HandleScope scope;
1514 ASSERT(args.length() == 1);
1515
1516 CONVERT_CHECKED(JSFunction, fun, args[0]);
1517 Handle<Object> script = Handle<Object>(fun->shared()->script());
1518 if (!script->IsScript()) return Heap::undefined_value();
1519
1520 return *GetScriptWrapper(Handle<Script>::cast(script));
1521}
1522
1523
1524static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1525 NoHandleAllocation ha;
1526 ASSERT(args.length() == 1);
1527
1528 CONVERT_CHECKED(JSFunction, f, args[0]);
1529 return f->shared()->GetSourceCode();
1530}
1531
1532
1533static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1534 NoHandleAllocation ha;
1535 ASSERT(args.length() == 1);
1536
1537 CONVERT_CHECKED(JSFunction, fun, args[0]);
1538 int pos = fun->shared()->start_position();
1539 return Smi::FromInt(pos);
1540}
1541
1542
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001543static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1544 ASSERT(args.length() == 2);
1545
1546 CONVERT_CHECKED(JSFunction, fun, args[0]);
1547 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1548
1549 Code* code = fun->code();
1550 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1551
1552 Address pc = code->address() + offset;
1553 return Smi::FromInt(fun->code()->SourcePosition(pc));
1554}
1555
1556
1557
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001558static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1559 NoHandleAllocation ha;
1560 ASSERT(args.length() == 2);
1561
1562 CONVERT_CHECKED(JSFunction, fun, args[0]);
1563 CONVERT_CHECKED(String, name, args[1]);
1564 fun->SetInstanceClassName(name);
1565 return Heap::undefined_value();
1566}
1567
1568
1569static Object* Runtime_FunctionSetLength(Arguments args) {
1570 NoHandleAllocation ha;
1571 ASSERT(args.length() == 2);
1572
1573 CONVERT_CHECKED(JSFunction, fun, args[0]);
1574 CONVERT_CHECKED(Smi, length, args[1]);
1575 fun->shared()->set_length(length->value());
1576 return length;
1577}
1578
1579
1580static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001581 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001582 ASSERT(args.length() == 2);
1583
1584 CONVERT_CHECKED(JSFunction, fun, args[0]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001585 ASSERT(fun->should_have_prototype());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001586 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1587 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001588 return args[0]; // return TOS
1589}
1590
1591
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001592static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1593 NoHandleAllocation ha;
1594 ASSERT(args.length() == 1);
1595
1596 CONVERT_CHECKED(JSFunction, f, args[0]);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001597 return f->shared()->IsApiFunction() ? Heap::true_value()
1598 : Heap::false_value();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001599}
1600
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001601static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1602 NoHandleAllocation ha;
1603 ASSERT(args.length() == 1);
1604
1605 CONVERT_CHECKED(JSFunction, f, args[0]);
1606 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1607}
1608
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001609
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001610static Object* Runtime_SetCode(Arguments args) {
1611 HandleScope scope;
1612 ASSERT(args.length() == 2);
1613
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001614 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001615 Handle<Object> code = args.at<Object>(1);
1616
1617 Handle<Context> context(target->context());
1618
1619 if (!code->IsNull()) {
1620 RUNTIME_ASSERT(code->IsJSFunction());
1621 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001622 Handle<SharedFunctionInfo> shared(fun->shared());
1623 SetExpectedNofProperties(target, shared->expected_nof_properties());
1624
1625 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001626 return Failure::Exception();
1627 }
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00001628 // Set the code, scope info, formal parameter count,
1629 // and the length of the target function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001630 target->set_code(fun->code());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00001631 target->shared()->set_scope_info(shared->scope_info());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001632 target->shared()->set_length(shared->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001633 target->shared()->set_formal_parameter_count(
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001634 shared->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001635 // Set the source code of the target function to undefined.
1636 // SetCode is only used for built-in constructors like String,
1637 // Array, and Object, and some web code
1638 // doesn't like seeing source code for constructors.
1639 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001640 // Clear the optimization hints related to the compiled code as these are no
1641 // longer valid when the code is overwritten.
1642 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001643 context = Handle<Context>(fun->context());
1644
1645 // Make sure we get a fresh copy of the literal vector to avoid
1646 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001647 int number_of_literals = fun->NumberOfLiterals();
1648 Handle<FixedArray> literals =
1649 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001650 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001651 // Insert the object, regexp and array functions in the literals
1652 // array prefix. These are the functions that will be used when
1653 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001654 literals->set(JSFunction::kLiteralGlobalContextIndex,
1655 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001656 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001657 // It's okay to skip the write barrier here because the literals
1658 // are guaranteed to be in old space.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001659 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001660 }
1661
1662 target->set_context(*context);
1663 return *target;
1664}
1665
1666
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001667static Object* CharFromCode(Object* char_code) {
1668 uint32_t code;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001669 if (char_code->ToArrayIndex(&code)) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001670 if (code <= 0xffff) {
1671 return Heap::LookupSingleCharacterStringFromCode(code);
1672 }
1673 }
1674 return Heap::empty_string();
1675}
1676
1677
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001678static Object* Runtime_StringCharCodeAt(Arguments args) {
1679 NoHandleAllocation ha;
1680 ASSERT(args.length() == 2);
1681
1682 CONVERT_CHECKED(String, subject, args[0]);
1683 Object* index = args[1];
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001684 RUNTIME_ASSERT(index->IsNumber());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001685
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001686 uint32_t i = 0;
1687 if (index->IsSmi()) {
1688 int value = Smi::cast(index)->value();
1689 if (value < 0) return Heap::nan_value();
1690 i = value;
1691 } else {
1692 ASSERT(index->IsHeapNumber());
1693 double value = HeapNumber::cast(index)->value();
1694 i = static_cast<uint32_t>(DoubleToInteger(value));
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001695 }
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001696
1697 // Flatten the string. If someone wants to get a char at an index
1698 // in a cons string, it is likely that more indices will be
1699 // accessed.
1700 Object* flat = subject->TryFlatten();
1701 if (flat->IsFailure()) return flat;
1702 subject = String::cast(flat);
1703
1704 if (i >= static_cast<uint32_t>(subject->length())) {
1705 return Heap::nan_value();
1706 }
1707
1708 return Smi::FromInt(subject->Get(i));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001709}
1710
1711
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001712static Object* Runtime_CharFromCode(Arguments args) {
1713 NoHandleAllocation ha;
1714 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001715 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001716}
1717
lrn@chromium.org25156de2010-04-06 13:10:27 +00001718
1719class FixedArrayBuilder {
1720 public:
1721 explicit FixedArrayBuilder(int initial_capacity)
1722 : array_(Factory::NewFixedArrayWithHoles(initial_capacity)),
1723 length_(0) {
1724 // Require a non-zero initial size. Ensures that doubling the size to
1725 // extend the array will work.
1726 ASSERT(initial_capacity > 0);
1727 }
1728
1729 explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
1730 : array_(backing_store),
1731 length_(0) {
1732 // Require a non-zero initial size. Ensures that doubling the size to
1733 // extend the array will work.
1734 ASSERT(backing_store->length() > 0);
1735 }
1736
1737 bool HasCapacity(int elements) {
1738 int length = array_->length();
1739 int required_length = length_ + elements;
1740 return (length >= required_length);
1741 }
1742
1743 void EnsureCapacity(int elements) {
1744 int length = array_->length();
1745 int required_length = length_ + elements;
1746 if (length < required_length) {
1747 int new_length = length;
1748 do {
1749 new_length *= 2;
1750 } while (new_length < required_length);
1751 Handle<FixedArray> extended_array =
1752 Factory::NewFixedArrayWithHoles(new_length);
1753 array_->CopyTo(0, *extended_array, 0, length_);
1754 array_ = extended_array;
1755 }
1756 }
1757
1758 void Add(Object* value) {
1759 ASSERT(length_ < capacity());
1760 array_->set(length_, value);
1761 length_++;
1762 }
1763
1764 void Add(Smi* value) {
1765 ASSERT(length_ < capacity());
1766 array_->set(length_, value);
1767 length_++;
1768 }
1769
1770 Handle<FixedArray> array() {
1771 return array_;
1772 }
1773
1774 int length() {
1775 return length_;
1776 }
1777
1778 int capacity() {
1779 return array_->length();
1780 }
1781
1782 Handle<JSArray> ToJSArray() {
1783 Handle<JSArray> result_array = Factory::NewJSArrayWithElements(array_);
1784 result_array->set_length(Smi::FromInt(length_));
1785 return result_array;
1786 }
1787
1788 Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
1789 target_array->set_elements(*array_);
1790 target_array->set_length(Smi::FromInt(length_));
1791 return target_array;
1792 }
1793
1794 private:
1795 Handle<FixedArray> array_;
1796 int length_;
1797};
1798
1799
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001800// Forward declarations.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001801const int kStringBuilderConcatHelperLengthBits = 11;
1802const int kStringBuilderConcatHelperPositionBits = 19;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001803
1804template <typename schar>
1805static inline void StringBuilderConcatHelper(String*,
1806 schar*,
1807 FixedArray*,
1808 int);
1809
lrn@chromium.org25156de2010-04-06 13:10:27 +00001810typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
1811 StringBuilderSubstringLength;
1812typedef BitField<int,
1813 kStringBuilderConcatHelperLengthBits,
1814 kStringBuilderConcatHelperPositionBits>
1815 StringBuilderSubstringPosition;
1816
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001817
1818class ReplacementStringBuilder {
1819 public:
1820 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
lrn@chromium.org25156de2010-04-06 13:10:27 +00001821 : array_builder_(estimated_part_count),
1822 subject_(subject),
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001823 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001824 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001825 // Require a non-zero initial size. Ensures that doubling the size to
1826 // extend the array will work.
1827 ASSERT(estimated_part_count > 0);
1828 }
1829
lrn@chromium.org25156de2010-04-06 13:10:27 +00001830 static inline void AddSubjectSlice(FixedArrayBuilder* builder,
1831 int from,
1832 int to) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001833 ASSERT(from >= 0);
1834 int length = to - from;
1835 ASSERT(length > 0);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001836 if (StringBuilderSubstringLength::is_valid(length) &&
1837 StringBuilderSubstringPosition::is_valid(from)) {
1838 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1839 StringBuilderSubstringPosition::encode(from);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001840 builder->Add(Smi::FromInt(encoded_slice));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001841 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001842 // Otherwise encode as two smis.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001843 builder->Add(Smi::FromInt(-length));
1844 builder->Add(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001845 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00001846 }
1847
1848
1849 void EnsureCapacity(int elements) {
1850 array_builder_.EnsureCapacity(elements);
1851 }
1852
1853
1854 void AddSubjectSlice(int from, int to) {
1855 AddSubjectSlice(&array_builder_, from, to);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001856 IncrementCharacterCount(to - from);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001857 }
1858
1859
1860 void AddString(Handle<String> string) {
1861 int length = string->length();
1862 ASSERT(length > 0);
1863 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001864 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001865 is_ascii_ = false;
1866 }
1867 IncrementCharacterCount(length);
1868 }
1869
1870
1871 Handle<String> ToString() {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001872 if (array_builder_.length() == 0) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001873 return Factory::empty_string();
1874 }
1875
1876 Handle<String> joined_string;
1877 if (is_ascii_) {
1878 joined_string = NewRawAsciiString(character_count_);
1879 AssertNoAllocation no_alloc;
1880 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1881 char* char_buffer = seq->GetChars();
1882 StringBuilderConcatHelper(*subject_,
1883 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001884 *array_builder_.array(),
1885 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001886 } else {
1887 // Non-ASCII.
1888 joined_string = NewRawTwoByteString(character_count_);
1889 AssertNoAllocation no_alloc;
1890 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1891 uc16* char_buffer = seq->GetChars();
1892 StringBuilderConcatHelper(*subject_,
1893 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001894 *array_builder_.array(),
1895 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001896 }
1897 return joined_string;
1898 }
1899
1900
1901 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001902 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001903 V8::FatalProcessOutOfMemory("String.replace result too large.");
1904 }
1905 character_count_ += by;
1906 }
1907
lrn@chromium.org25156de2010-04-06 13:10:27 +00001908 Handle<JSArray> GetParts() {
1909 Handle<JSArray> result =
1910 Factory::NewJSArrayWithElements(array_builder_.array());
1911 result->set_length(Smi::FromInt(array_builder_.length()));
1912 return result;
1913 }
kmillikin@chromium.orgd9825192010-03-30 08:36:16 +00001914
lrn@chromium.org25156de2010-04-06 13:10:27 +00001915 private:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001916 Handle<String> NewRawAsciiString(int size) {
1917 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1918 }
1919
1920
1921 Handle<String> NewRawTwoByteString(int size) {
1922 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1923 }
1924
1925
1926 void AddElement(Object* element) {
1927 ASSERT(element->IsSmi() || element->IsString());
lrn@chromium.org25156de2010-04-06 13:10:27 +00001928 ASSERT(array_builder_.capacity() > array_builder_.length());
1929 array_builder_.Add(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001930 }
1931
lrn@chromium.org25156de2010-04-06 13:10:27 +00001932 FixedArrayBuilder array_builder_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001933 Handle<String> subject_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001934 int character_count_;
1935 bool is_ascii_;
1936};
1937
1938
1939class CompiledReplacement {
1940 public:
1941 CompiledReplacement()
1942 : parts_(1), replacement_substrings_(0) {}
1943
1944 void Compile(Handle<String> replacement,
1945 int capture_count,
1946 int subject_length);
1947
1948 void Apply(ReplacementStringBuilder* builder,
1949 int match_from,
1950 int match_to,
1951 Handle<JSArray> last_match_info);
1952
1953 // Number of distinct parts of the replacement pattern.
1954 int parts() {
1955 return parts_.length();
1956 }
1957 private:
1958 enum PartType {
1959 SUBJECT_PREFIX = 1,
1960 SUBJECT_SUFFIX,
1961 SUBJECT_CAPTURE,
1962 REPLACEMENT_SUBSTRING,
1963 REPLACEMENT_STRING,
1964
1965 NUMBER_OF_PART_TYPES
1966 };
1967
1968 struct ReplacementPart {
1969 static inline ReplacementPart SubjectMatch() {
1970 return ReplacementPart(SUBJECT_CAPTURE, 0);
1971 }
1972 static inline ReplacementPart SubjectCapture(int capture_index) {
1973 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1974 }
1975 static inline ReplacementPart SubjectPrefix() {
1976 return ReplacementPart(SUBJECT_PREFIX, 0);
1977 }
1978 static inline ReplacementPart SubjectSuffix(int subject_length) {
1979 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1980 }
1981 static inline ReplacementPart ReplacementString() {
1982 return ReplacementPart(REPLACEMENT_STRING, 0);
1983 }
1984 static inline ReplacementPart ReplacementSubString(int from, int to) {
1985 ASSERT(from >= 0);
1986 ASSERT(to > from);
1987 return ReplacementPart(-from, to);
1988 }
1989
1990 // If tag <= 0 then it is the negation of a start index of a substring of
1991 // the replacement pattern, otherwise it's a value from PartType.
1992 ReplacementPart(int tag, int data)
1993 : tag(tag), data(data) {
1994 // Must be non-positive or a PartType value.
1995 ASSERT(tag < NUMBER_OF_PART_TYPES);
1996 }
1997 // Either a value of PartType or a non-positive number that is
1998 // the negation of an index into the replacement string.
1999 int tag;
2000 // The data value's interpretation depends on the value of tag:
2001 // tag == SUBJECT_PREFIX ||
2002 // tag == SUBJECT_SUFFIX: data is unused.
2003 // tag == SUBJECT_CAPTURE: data is the number of the capture.
2004 // tag == REPLACEMENT_SUBSTRING ||
2005 // tag == REPLACEMENT_STRING: data is index into array of substrings
2006 // of the replacement string.
2007 // tag <= 0: Temporary representation of the substring of the replacement
2008 // string ranging over -tag .. data.
2009 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
2010 // substring objects.
2011 int data;
2012 };
2013
2014 template<typename Char>
2015 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
2016 Vector<Char> characters,
2017 int capture_count,
2018 int subject_length) {
2019 int length = characters.length();
2020 int last = 0;
2021 for (int i = 0; i < length; i++) {
2022 Char c = characters[i];
2023 if (c == '$') {
2024 int next_index = i + 1;
2025 if (next_index == length) { // No next character!
2026 break;
2027 }
2028 Char c2 = characters[next_index];
2029 switch (c2) {
2030 case '$':
2031 if (i > last) {
2032 // There is a substring before. Include the first "$".
2033 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
2034 last = next_index + 1; // Continue after the second "$".
2035 } else {
2036 // Let the next substring start with the second "$".
2037 last = next_index;
2038 }
2039 i = next_index;
2040 break;
2041 case '`':
2042 if (i > last) {
2043 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2044 }
2045 parts->Add(ReplacementPart::SubjectPrefix());
2046 i = next_index;
2047 last = i + 1;
2048 break;
2049 case '\'':
2050 if (i > last) {
2051 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2052 }
2053 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
2054 i = next_index;
2055 last = i + 1;
2056 break;
2057 case '&':
2058 if (i > last) {
2059 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2060 }
2061 parts->Add(ReplacementPart::SubjectMatch());
2062 i = next_index;
2063 last = i + 1;
2064 break;
2065 case '0':
2066 case '1':
2067 case '2':
2068 case '3':
2069 case '4':
2070 case '5':
2071 case '6':
2072 case '7':
2073 case '8':
2074 case '9': {
2075 int capture_ref = c2 - '0';
2076 if (capture_ref > capture_count) {
2077 i = next_index;
2078 continue;
2079 }
2080 int second_digit_index = next_index + 1;
2081 if (second_digit_index < length) {
2082 // Peek ahead to see if we have two digits.
2083 Char c3 = characters[second_digit_index];
2084 if ('0' <= c3 && c3 <= '9') { // Double digits.
2085 int double_digit_ref = capture_ref * 10 + c3 - '0';
2086 if (double_digit_ref <= capture_count) {
2087 next_index = second_digit_index;
2088 capture_ref = double_digit_ref;
2089 }
2090 }
2091 }
2092 if (capture_ref > 0) {
2093 if (i > last) {
2094 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2095 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002096 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002097 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
2098 last = next_index + 1;
2099 }
2100 i = next_index;
2101 break;
2102 }
2103 default:
2104 i = next_index;
2105 break;
2106 }
2107 }
2108 }
2109 if (length > last) {
2110 if (last == 0) {
2111 parts->Add(ReplacementPart::ReplacementString());
2112 } else {
2113 parts->Add(ReplacementPart::ReplacementSubString(last, length));
2114 }
2115 }
2116 }
2117
2118 ZoneList<ReplacementPart> parts_;
2119 ZoneList<Handle<String> > replacement_substrings_;
2120};
2121
2122
2123void CompiledReplacement::Compile(Handle<String> replacement,
2124 int capture_count,
2125 int subject_length) {
2126 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00002127 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002128 AssertNoAllocation no_alloc;
2129 ParseReplacementPattern(&parts_,
2130 replacement->ToAsciiVector(),
2131 capture_count,
2132 subject_length);
2133 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00002134 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002135 AssertNoAllocation no_alloc;
2136
2137 ParseReplacementPattern(&parts_,
2138 replacement->ToUC16Vector(),
2139 capture_count,
2140 subject_length);
2141 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002142 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002143 int substring_index = 0;
2144 for (int i = 0, n = parts_.length(); i < n; i++) {
2145 int tag = parts_[i].tag;
2146 if (tag <= 0) { // A replacement string slice.
2147 int from = -tag;
2148 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002149 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002150 parts_[i].tag = REPLACEMENT_SUBSTRING;
2151 parts_[i].data = substring_index;
2152 substring_index++;
2153 } else if (tag == REPLACEMENT_STRING) {
2154 replacement_substrings_.Add(replacement);
2155 parts_[i].data = substring_index;
2156 substring_index++;
2157 }
2158 }
2159}
2160
2161
2162void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
2163 int match_from,
2164 int match_to,
2165 Handle<JSArray> last_match_info) {
2166 for (int i = 0, n = parts_.length(); i < n; i++) {
2167 ReplacementPart part = parts_[i];
2168 switch (part.tag) {
2169 case SUBJECT_PREFIX:
2170 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
2171 break;
2172 case SUBJECT_SUFFIX: {
2173 int subject_length = part.data;
2174 if (match_to < subject_length) {
2175 builder->AddSubjectSlice(match_to, subject_length);
2176 }
2177 break;
2178 }
2179 case SUBJECT_CAPTURE: {
2180 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002181 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002182 int from = RegExpImpl::GetCapture(match_info, capture * 2);
2183 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
2184 if (from >= 0 && to > from) {
2185 builder->AddSubjectSlice(from, to);
2186 }
2187 break;
2188 }
2189 case REPLACEMENT_SUBSTRING:
2190 case REPLACEMENT_STRING:
2191 builder->AddString(replacement_substrings_[part.data]);
2192 break;
2193 default:
2194 UNREACHABLE();
2195 }
2196 }
2197}
2198
2199
2200
2201static Object* StringReplaceRegExpWithString(String* subject,
2202 JSRegExp* regexp,
2203 String* replacement,
2204 JSArray* last_match_info) {
2205 ASSERT(subject->IsFlat());
2206 ASSERT(replacement->IsFlat());
2207
2208 HandleScope handles;
2209
2210 int length = subject->length();
2211 Handle<String> subject_handle(subject);
2212 Handle<JSRegExp> regexp_handle(regexp);
2213 Handle<String> replacement_handle(replacement);
2214 Handle<JSArray> last_match_info_handle(last_match_info);
2215 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2216 subject_handle,
2217 0,
2218 last_match_info_handle);
2219 if (match.is_null()) {
2220 return Failure::Exception();
2221 }
2222 if (match->IsNull()) {
2223 return *subject_handle;
2224 }
2225
2226 int capture_count = regexp_handle->CaptureCount();
2227
2228 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002229 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002230 CompiledReplacement compiled_replacement;
2231 compiled_replacement.Compile(replacement_handle,
2232 capture_count,
2233 length);
2234
2235 bool is_global = regexp_handle->GetFlags().is_global();
2236
2237 // Guessing the number of parts that the final result string is built
2238 // from. Global regexps can match any number of times, so we guess
2239 // conservatively.
2240 int expected_parts =
2241 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
2242 ReplacementStringBuilder builder(subject_handle, expected_parts);
2243
2244 // Index of end of last match.
2245 int prev = 0;
2246
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002247 // Number of parts added by compiled replacement plus preceeding
2248 // string and possibly suffix after last match. It is possible for
2249 // all components to use two elements when encoded as two smis.
2250 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002251 bool matched = true;
2252 do {
2253 ASSERT(last_match_info_handle->HasFastElements());
2254 // Increase the capacity of the builder before entering local handle-scope,
2255 // so its internal buffer can safely allocate a new handle if it grows.
2256 builder.EnsureCapacity(parts_added_per_loop);
2257
2258 HandleScope loop_scope;
2259 int start, end;
2260 {
2261 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002262 FixedArray* match_info_array =
2263 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002264
2265 ASSERT_EQ(capture_count * 2 + 2,
2266 RegExpImpl::GetLastCaptureCount(match_info_array));
2267 start = RegExpImpl::GetCapture(match_info_array, 0);
2268 end = RegExpImpl::GetCapture(match_info_array, 1);
2269 }
2270
2271 if (prev < start) {
2272 builder.AddSubjectSlice(prev, start);
2273 }
2274 compiled_replacement.Apply(&builder,
2275 start,
2276 end,
2277 last_match_info_handle);
2278 prev = end;
2279
2280 // Only continue checking for global regexps.
2281 if (!is_global) break;
2282
2283 // Continue from where the match ended, unless it was an empty match.
2284 int next = end;
2285 if (start == end) {
2286 next = end + 1;
2287 if (next > length) break;
2288 }
2289
2290 match = RegExpImpl::Exec(regexp_handle,
2291 subject_handle,
2292 next,
2293 last_match_info_handle);
2294 if (match.is_null()) {
2295 return Failure::Exception();
2296 }
2297 matched = !match->IsNull();
2298 } while (matched);
2299
2300 if (prev < length) {
2301 builder.AddSubjectSlice(prev, length);
2302 }
2303
2304 return *(builder.ToString());
2305}
2306
2307
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00002308template <typename ResultSeqString>
2309static Object* StringReplaceRegExpWithEmptyString(String* subject,
2310 JSRegExp* regexp,
2311 JSArray* last_match_info) {
2312 ASSERT(subject->IsFlat());
2313
2314 HandleScope handles;
2315
2316 Handle<String> subject_handle(subject);
2317 Handle<JSRegExp> regexp_handle(regexp);
2318 Handle<JSArray> last_match_info_handle(last_match_info);
2319 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2320 subject_handle,
2321 0,
2322 last_match_info_handle);
2323 if (match.is_null()) return Failure::Exception();
2324 if (match->IsNull()) return *subject_handle;
2325
2326 ASSERT(last_match_info_handle->HasFastElements());
2327
2328 HandleScope loop_scope;
2329 int start, end;
2330 {
2331 AssertNoAllocation match_info_array_is_not_in_a_handle;
2332 FixedArray* match_info_array =
2333 FixedArray::cast(last_match_info_handle->elements());
2334
2335 start = RegExpImpl::GetCapture(match_info_array, 0);
2336 end = RegExpImpl::GetCapture(match_info_array, 1);
2337 }
2338
2339 int length = subject->length();
2340 int new_length = length - (end - start);
2341 if (new_length == 0) {
2342 return Heap::empty_string();
2343 }
2344 Handle<ResultSeqString> answer;
2345 if (ResultSeqString::kHasAsciiEncoding) {
2346 answer =
2347 Handle<ResultSeqString>::cast(Factory::NewRawAsciiString(new_length));
2348 } else {
2349 answer =
2350 Handle<ResultSeqString>::cast(Factory::NewRawTwoByteString(new_length));
2351 }
2352
2353 // If the regexp isn't global, only match once.
2354 if (!regexp_handle->GetFlags().is_global()) {
2355 if (start > 0) {
2356 String::WriteToFlat(*subject_handle,
2357 answer->GetChars(),
2358 0,
2359 start);
2360 }
2361 if (end < length) {
2362 String::WriteToFlat(*subject_handle,
2363 answer->GetChars() + start,
2364 end,
2365 length);
2366 }
2367 return *answer;
2368 }
2369
2370 int prev = 0; // Index of end of last match.
2371 int next = 0; // Start of next search (prev unless last match was empty).
2372 int position = 0;
2373
2374 do {
2375 if (prev < start) {
2376 // Add substring subject[prev;start] to answer string.
2377 String::WriteToFlat(*subject_handle,
2378 answer->GetChars() + position,
2379 prev,
2380 start);
2381 position += start - prev;
2382 }
2383 prev = end;
2384 next = end;
2385 // Continue from where the match ended, unless it was an empty match.
2386 if (start == end) {
2387 next++;
2388 if (next > length) break;
2389 }
2390 match = RegExpImpl::Exec(regexp_handle,
2391 subject_handle,
2392 next,
2393 last_match_info_handle);
2394 if (match.is_null()) return Failure::Exception();
2395 if (match->IsNull()) break;
2396
2397 ASSERT(last_match_info_handle->HasFastElements());
2398 HandleScope loop_scope;
2399 {
2400 AssertNoAllocation match_info_array_is_not_in_a_handle;
2401 FixedArray* match_info_array =
2402 FixedArray::cast(last_match_info_handle->elements());
2403 start = RegExpImpl::GetCapture(match_info_array, 0);
2404 end = RegExpImpl::GetCapture(match_info_array, 1);
2405 }
2406 } while (true);
2407
2408 if (prev < length) {
2409 // Add substring subject[prev;length] to answer string.
2410 String::WriteToFlat(*subject_handle,
2411 answer->GetChars() + position,
2412 prev,
2413 length);
2414 position += length - prev;
2415 }
2416
2417 if (position == 0) {
2418 return Heap::empty_string();
2419 }
2420
2421 // Shorten string and fill
2422 int string_size = ResultSeqString::SizeFor(position);
2423 int allocated_string_size = ResultSeqString::SizeFor(new_length);
2424 int delta = allocated_string_size - string_size;
2425
2426 answer->set_length(position);
2427 if (delta == 0) return *answer;
2428
2429 Address end_of_string = answer->address() + string_size;
2430 Heap::CreateFillerObjectAt(end_of_string, delta);
2431
2432 return *answer;
2433}
2434
2435
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002436static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2437 ASSERT(args.length() == 4);
2438
2439 CONVERT_CHECKED(String, subject, args[0]);
2440 if (!subject->IsFlat()) {
2441 Object* flat_subject = subject->TryFlatten();
2442 if (flat_subject->IsFailure()) {
2443 return flat_subject;
2444 }
2445 subject = String::cast(flat_subject);
2446 }
2447
2448 CONVERT_CHECKED(String, replacement, args[2]);
2449 if (!replacement->IsFlat()) {
2450 Object* flat_replacement = replacement->TryFlatten();
2451 if (flat_replacement->IsFailure()) {
2452 return flat_replacement;
2453 }
2454 replacement = String::cast(flat_replacement);
2455 }
2456
2457 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2458 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2459
2460 ASSERT(last_match_info->HasFastElements());
2461
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00002462 if (replacement->length() == 0) {
2463 if (subject->HasOnlyAsciiChars()) {
2464 return StringReplaceRegExpWithEmptyString<SeqAsciiString>(
2465 subject, regexp, last_match_info);
2466 } else {
2467 return StringReplaceRegExpWithEmptyString<SeqTwoByteString>(
2468 subject, regexp, last_match_info);
2469 }
2470 }
2471
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002472 return StringReplaceRegExpWithString(subject,
2473 regexp,
2474 replacement,
2475 last_match_info);
2476}
2477
2478
ager@chromium.org7c537e22008-10-16 08:43:32 +00002479// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2480// limit, we can fix the size of tables.
2481static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002482// Reduce alphabet to this size.
2483static const int kBMAlphabetSize = 0x100;
2484// For patterns below this length, the skip length of Boyer-Moore is too short
2485// to compensate for the algorithmic overhead compared to simple brute force.
2486static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002487
ager@chromium.org7c537e22008-10-16 08:43:32 +00002488// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2489// shift. Only allows the last kBMMaxShift characters of the needle
2490// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002491class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002492 public:
2493 BMGoodSuffixBuffers() {}
2494 inline void init(int needle_length) {
2495 ASSERT(needle_length > 1);
2496 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2497 int len = needle_length - start;
2498 biased_suffixes_ = suffixes_ - start;
2499 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2500 for (int i = 0; i <= len; i++) {
2501 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002502 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002503 }
2504 inline int& suffix(int index) {
2505 ASSERT(biased_suffixes_ + index >= suffixes_);
2506 return biased_suffixes_[index];
2507 }
2508 inline int& shift(int index) {
2509 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2510 return biased_good_suffix_shift_[index];
2511 }
2512 private:
2513 int suffixes_[kBMMaxShift + 1];
2514 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002515 int* biased_suffixes_;
2516 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002517 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2518};
2519
2520// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002521static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002522static BMGoodSuffixBuffers bmgs_buffers;
2523
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002524// State of the string match tables.
2525// SIMPLE: No usable content in the buffers.
2526// BOYER_MOORE_HORSPOOL: The bad_char_occurences table has been populated.
2527// BOYER_MOORE: The bmgs_buffers tables have also been populated.
2528// Whenever starting with a new needle, one should call InitializeStringSearch
2529// to determine which search strategy to use, and in the case of a long-needle
2530// strategy, the call also initializes the algorithm to SIMPLE.
2531enum StringSearchAlgorithm { SIMPLE_SEARCH, BOYER_MOORE_HORSPOOL, BOYER_MOORE };
2532static StringSearchAlgorithm algorithm;
2533
2534
ager@chromium.org7c537e22008-10-16 08:43:32 +00002535// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002536template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002537static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern) {
2538 // Only preprocess at most kBMMaxShift last characters of pattern.
2539 int start = pattern.length() < kBMMaxShift ? 0
2540 : pattern.length() - kBMMaxShift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002541 // Run forwards to populate bad_char_table, so that *last* instance
2542 // of character equivalence class is the one registered.
2543 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002544 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2545 : kBMAlphabetSize;
2546 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002547 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002548 } else {
2549 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002550 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002551 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002552 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002553 for (int i = start; i < pattern.length() - 1; i++) {
2554 pchar c = pattern[i];
2555 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002556 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002557 }
2558}
2559
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002560
ager@chromium.org7c537e22008-10-16 08:43:32 +00002561template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002562static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002563 int m = pattern.length();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002564 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002565 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002566 // Compute Good Suffix tables.
2567 bmgs_buffers.init(m);
2568
2569 bmgs_buffers.shift(m-1) = 1;
2570 bmgs_buffers.suffix(m) = m + 1;
2571 pchar last_char = pattern[m - 1];
2572 int suffix = m + 1;
2573 for (int i = m; i > start;) {
2574 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2575 if (bmgs_buffers.shift(suffix) == len) {
2576 bmgs_buffers.shift(suffix) = suffix - i;
2577 }
2578 suffix = bmgs_buffers.suffix(suffix);
2579 }
2580 i--;
2581 suffix--;
2582 bmgs_buffers.suffix(i) = suffix;
2583 if (suffix == m) {
2584 // No suffix to extend, so we check against last_char only.
2585 while (i > start && pattern[i - 1] != last_char) {
2586 if (bmgs_buffers.shift(m) == len) {
2587 bmgs_buffers.shift(m) = m - i;
2588 }
2589 i--;
2590 bmgs_buffers.suffix(i) = m;
2591 }
2592 if (i > start) {
2593 i--;
2594 suffix--;
2595 bmgs_buffers.suffix(i) = suffix;
2596 }
2597 }
2598 }
2599 if (suffix < m) {
2600 for (int i = start; i <= m; i++) {
2601 if (bmgs_buffers.shift(i) == len) {
2602 bmgs_buffers.shift(i) = suffix - start;
2603 }
2604 if (i == suffix) {
2605 suffix = bmgs_buffers.suffix(suffix);
2606 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002607 }
2608 }
2609}
2610
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002611
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002612template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002613static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002614 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002615 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002616 }
2617 if (sizeof(pchar) == 1) {
2618 if (char_code > String::kMaxAsciiCharCode) {
2619 return -1;
2620 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002621 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002622 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002623 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002624}
2625
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002626
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002627// Restricted simplified Boyer-Moore string matching.
2628// Uses only the bad-shift table of Boyer-Moore and only uses it
2629// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002630template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002631static int BoyerMooreHorspool(Vector<const schar> subject,
2632 Vector<const pchar> pattern,
2633 int start_index,
2634 bool* complete) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002635 ASSERT(algorithm <= BOYER_MOORE_HORSPOOL);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002636 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002637 int m = pattern.length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002638
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002639 int badness = -m;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002640
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002641 // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002642 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002643 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002644 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002645 // Perform search
2646 for (idx = start_index; idx <= n - m;) {
2647 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002648 int c;
2649 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002650 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002651 int shift = j - bc_occ;
2652 idx += shift;
2653 badness += 1 - shift; // at most zero, so badness cannot increase.
2654 if (idx > n - m) {
2655 *complete = true;
2656 return -1;
2657 }
2658 }
2659 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002660 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002661 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002662 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002663 return idx;
2664 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002665 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002666 // Badness increases by the number of characters we have
2667 // checked, and decreases by the number of characters we
2668 // can skip by shifting. It's a measure of how we are doing
2669 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002670 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002671 if (badness > 0) {
2672 *complete = false;
2673 return idx;
2674 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002675 }
2676 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002677 *complete = true;
2678 return -1;
2679}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002680
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002681
2682template <typename schar, typename pchar>
2683static int BoyerMooreIndexOf(Vector<const schar> subject,
2684 Vector<const pchar> pattern,
2685 int idx) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002686 ASSERT(algorithm <= BOYER_MOORE);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002687 int n = subject.length();
2688 int m = pattern.length();
2689 // Only preprocess at most kBMMaxShift last characters of pattern.
2690 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2691
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002692 pchar last_char = pattern[m - 1];
2693 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002694 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002695 int j = m - 1;
2696 schar c;
2697 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002698 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002699 idx += shift;
2700 if (idx > n - m) {
2701 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002702 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002703 }
2704 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2705 if (j < 0) {
2706 return idx;
2707 } else if (j < start) {
2708 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002709 // Fall back on BMH shift.
2710 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002711 } else {
2712 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002713 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002714 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002715 if (gs_shift > shift) {
2716 shift = gs_shift;
2717 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002718 idx += shift;
2719 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002720 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002721
2722 return -1;
2723}
2724
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002725
2726template <typename schar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002727static inline int SingleCharIndexOf(Vector<const schar> string,
2728 schar pattern_char,
2729 int start_index) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002730 if (sizeof(schar) == 1) {
2731 const schar* pos = reinterpret_cast<const schar*>(
2732 memchr(string.start() + start_index,
2733 pattern_char,
2734 string.length() - start_index));
2735 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002736 return static_cast<int>(pos - string.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002737 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002738 for (int i = start_index, n = string.length(); i < n; i++) {
2739 if (pattern_char == string[i]) {
2740 return i;
2741 }
2742 }
2743 return -1;
2744}
2745
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002746
2747template <typename schar>
2748static int SingleCharLastIndexOf(Vector<const schar> string,
2749 schar pattern_char,
2750 int start_index) {
2751 for (int i = start_index; i >= 0; i--) {
2752 if (pattern_char == string[i]) {
2753 return i;
2754 }
2755 }
2756 return -1;
2757}
2758
2759
ager@chromium.org7c537e22008-10-16 08:43:32 +00002760// Trivial string search for shorter strings.
2761// On return, if "complete" is set to true, the return value is the
2762// final result of searching for the patter in the subject.
2763// If "complete" is set to false, the return value is the index where
2764// further checking should start, i.e., it's guaranteed that the pattern
2765// does not occur at a position prior to the returned index.
2766template <typename pchar, typename schar>
2767static int SimpleIndexOf(Vector<const schar> subject,
2768 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002769 int idx,
2770 bool* complete) {
2771 // Badness is a count of how much work we have done. When we have
2772 // done enough work we decide it's probably worth switching to a better
2773 // algorithm.
2774 int badness = -10 - (pattern.length() << 2);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002775
ager@chromium.org7c537e22008-10-16 08:43:32 +00002776 // We know our pattern is at least 2 characters, we cache the first so
2777 // the common case of the first character not matching is faster.
2778 pchar pattern_first_char = pattern[0];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002779 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2780 badness++;
2781 if (badness > 0) {
2782 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002783 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002784 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002785 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2786 const schar* pos = reinterpret_cast<const schar*>(
2787 memchr(subject.start() + i,
2788 pattern_first_char,
2789 n - i + 1));
2790 if (pos == NULL) {
2791 *complete = true;
2792 return -1;
2793 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002794 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002795 } else {
2796 if (subject[i] != pattern_first_char) continue;
2797 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002798 int j = 1;
2799 do {
2800 if (pattern[j] != subject[i+j]) {
2801 break;
2802 }
2803 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002804 } while (j < pattern.length());
2805 if (j == pattern.length()) {
2806 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002807 return i;
2808 }
2809 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002810 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002811 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002812 return -1;
2813}
2814
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002815// Simple indexOf that never bails out. For short patterns only.
2816template <typename pchar, typename schar>
2817static int SimpleIndexOf(Vector<const schar> subject,
2818 Vector<const pchar> pattern,
2819 int idx) {
2820 pchar pattern_first_char = pattern[0];
2821 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002822 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2823 const schar* pos = reinterpret_cast<const schar*>(
2824 memchr(subject.start() + i,
2825 pattern_first_char,
2826 n - i + 1));
2827 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002828 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002829 } else {
2830 if (subject[i] != pattern_first_char) continue;
2831 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002832 int j = 1;
2833 do {
2834 if (pattern[j] != subject[i+j]) {
2835 break;
2836 }
2837 j++;
2838 } while (j < pattern.length());
2839 if (j == pattern.length()) {
2840 return i;
2841 }
2842 }
2843 return -1;
2844}
2845
2846
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002847// Strategy for searching for a string in another string.
2848enum StringSearchStrategy { SEARCH_FAIL, SEARCH_SHORT, SEARCH_LONG };
ager@chromium.org7c537e22008-10-16 08:43:32 +00002849
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002850
2851template <typename pchar>
2852static inline StringSearchStrategy InitializeStringSearch(
2853 Vector<const pchar> pat, bool ascii_subject) {
2854 ASSERT(pat.length() > 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002855 // We have an ASCII haystack and a non-ASCII needle. Check if there
2856 // really is a non-ASCII character in the needle and bail out if there
2857 // is.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002858 if (ascii_subject && sizeof(pchar) > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002859 for (int i = 0; i < pat.length(); i++) {
2860 uc16 c = pat[i];
2861 if (c > String::kMaxAsciiCharCode) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002862 return SEARCH_FAIL;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002863 }
2864 }
2865 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002866 if (pat.length() < kBMMinPatternLength) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002867 return SEARCH_SHORT;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002868 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002869 algorithm = SIMPLE_SEARCH;
2870 return SEARCH_LONG;
2871}
2872
2873
2874// Dispatch long needle searches to different algorithms.
2875template <typename schar, typename pchar>
2876static int ComplexIndexOf(Vector<const schar> sub,
2877 Vector<const pchar> pat,
2878 int start_index) {
2879 ASSERT(pat.length() >= kBMMinPatternLength);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002880 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002881 bool complete;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002882 int idx = start_index;
2883 switch (algorithm) {
2884 case SIMPLE_SEARCH:
2885 idx = SimpleIndexOf(sub, pat, idx, &complete);
2886 if (complete) return idx;
2887 BoyerMoorePopulateBadCharTable(pat);
2888 algorithm = BOYER_MOORE_HORSPOOL;
2889 // FALLTHROUGH.
2890 case BOYER_MOORE_HORSPOOL:
2891 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
2892 if (complete) return idx;
2893 // Build the Good Suffix table and continue searching.
2894 BoyerMoorePopulateGoodSuffixTable(pat);
2895 algorithm = BOYER_MOORE;
2896 // FALLTHROUGH.
2897 case BOYER_MOORE:
2898 return BoyerMooreIndexOf(sub, pat, idx);
2899 }
2900 UNREACHABLE();
2901 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002902}
2903
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002904
2905// Dispatch to different search strategies for a single search.
2906// If searching multiple times on the same needle, the search
2907// strategy should only be computed once and then dispatch to different
2908// loops.
2909template <typename schar, typename pchar>
2910static int StringSearch(Vector<const schar> sub,
2911 Vector<const pchar> pat,
2912 int start_index) {
2913 bool ascii_subject = (sizeof(schar) == 1);
2914 StringSearchStrategy strategy = InitializeStringSearch(pat, ascii_subject);
2915 switch (strategy) {
2916 case SEARCH_FAIL: return -1;
2917 case SEARCH_SHORT: return SimpleIndexOf(sub, pat, start_index);
2918 case SEARCH_LONG: return ComplexIndexOf(sub, pat, start_index);
2919 }
2920 UNREACHABLE();
2921 return -1;
2922}
2923
2924
ager@chromium.org7c537e22008-10-16 08:43:32 +00002925// Perform string match of pattern on subject, starting at start index.
2926// Caller must ensure that 0 <= start_index <= sub->length(),
2927// and should check that pat->length() + start_index <= sub->length()
2928int Runtime::StringMatch(Handle<String> sub,
2929 Handle<String> pat,
2930 int start_index) {
2931 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002932 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002933
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002934 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002935 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002936
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002937 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002938 if (start_index + pattern_length > subject_length) return -1;
2939
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002940 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002941 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002942 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002943
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002944 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002945 // character patterns linear search is necessary, so any smart
2946 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002947 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002948 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002949 String* seq_sub = *sub;
2950 if (seq_sub->IsConsString()) {
2951 seq_sub = ConsString::cast(seq_sub)->first();
2952 }
2953 if (seq_sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002954 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002955 if (pchar > String::kMaxAsciiCharCode) {
2956 return -1;
2957 }
2958 Vector<const char> ascii_vector =
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002959 seq_sub->ToAsciiVector().SubVector(start_index, subject_length);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002960 const void* pos = memchr(ascii_vector.start(),
2961 static_cast<const char>(pchar),
2962 static_cast<size_t>(ascii_vector.length()));
2963 if (pos == NULL) {
2964 return -1;
2965 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002966 return static_cast<int>(reinterpret_cast<const char*>(pos)
2967 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002968 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002969 return SingleCharIndexOf(seq_sub->ToUC16Vector(),
2970 pat->Get(0),
2971 start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002972 }
2973
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002974 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002975 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002976 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002977
ager@chromium.org7c537e22008-10-16 08:43:32 +00002978 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002979 // Extract flattened substrings of cons strings before determining asciiness.
2980 String* seq_sub = *sub;
2981 if (seq_sub->IsConsString()) {
2982 seq_sub = ConsString::cast(seq_sub)->first();
2983 }
2984 String* seq_pat = *pat;
2985 if (seq_pat->IsConsString()) {
2986 seq_pat = ConsString::cast(seq_pat)->first();
2987 }
2988
ager@chromium.org7c537e22008-10-16 08:43:32 +00002989 // dispatch on type of strings
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002990 if (seq_pat->IsAsciiRepresentation()) {
2991 Vector<const char> pat_vector = seq_pat->ToAsciiVector();
2992 if (seq_sub->IsAsciiRepresentation()) {
2993 return StringSearch(seq_sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002994 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002995 return StringSearch(seq_sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002996 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002997 Vector<const uc16> pat_vector = seq_pat->ToUC16Vector();
2998 if (seq_sub->IsAsciiRepresentation()) {
2999 return StringSearch(seq_sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003000 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00003001 return StringSearch(seq_sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003002}
3003
3004
3005static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003006 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003007 ASSERT(args.length() == 3);
3008
ager@chromium.org7c537e22008-10-16 08:43:32 +00003009 CONVERT_ARG_CHECKED(String, sub, 0);
3010 CONVERT_ARG_CHECKED(String, pat, 1);
3011
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003012 Object* index = args[2];
3013 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003014 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003015
ager@chromium.org870a0b62008-11-04 11:43:05 +00003016 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00003017 int position = Runtime::StringMatch(sub, pat, start_index);
3018 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003019}
3020
3021
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003022template <typename schar, typename pchar>
3023static int StringMatchBackwards(Vector<const schar> sub,
3024 Vector<const pchar> pat,
3025 int idx) {
3026 ASSERT(pat.length() >= 1);
3027 ASSERT(idx + pat.length() <= sub.length());
3028
3029 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
3030 for (int i = 0; i < pat.length(); i++) {
3031 uc16 c = pat[i];
3032 if (c > String::kMaxAsciiCharCode) {
3033 return -1;
3034 }
3035 }
3036 }
3037
3038 pchar pattern_first_char = pat[0];
3039 for (int i = idx; i >= 0; i--) {
3040 if (sub[i] != pattern_first_char) continue;
3041 int j = 1;
3042 while (j < pat.length()) {
3043 if (pat[j] != sub[i+j]) {
3044 break;
3045 }
3046 j++;
3047 }
3048 if (j == pat.length()) {
3049 return i;
3050 }
3051 }
3052 return -1;
3053}
3054
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003055static Object* Runtime_StringLastIndexOf(Arguments args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003056 HandleScope scope; // create a new handle scope
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003057 ASSERT(args.length() == 3);
3058
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003059 CONVERT_ARG_CHECKED(String, sub, 0);
3060 CONVERT_ARG_CHECKED(String, pat, 1);
3061
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003062 Object* index = args[2];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003063 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003064 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003065
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003066 uint32_t pat_length = pat->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003067 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003068
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003069 if (start_index + pat_length > sub_length) {
3070 start_index = sub_length - pat_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00003071 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003072
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003073 if (pat_length == 0) {
3074 return Smi::FromInt(start_index);
3075 }
3076
3077 if (!sub->IsFlat()) {
3078 FlattenString(sub);
3079 }
3080
3081 if (pat_length == 1) {
3082 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
3083 if (sub->IsAsciiRepresentation()) {
3084 uc16 pchar = pat->Get(0);
3085 if (pchar > String::kMaxAsciiCharCode) {
3086 return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003087 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003088 return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
3089 static_cast<char>(pat->Get(0)),
3090 start_index));
3091 } else {
3092 return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
3093 pat->Get(0),
3094 start_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003095 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003096 }
3097
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003098 if (!pat->IsFlat()) {
3099 FlattenString(pat);
3100 }
3101
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
3278template <typename schar>
3279static bool SearchCharMultiple(Vector<schar> subject,
3280 String* pattern,
3281 schar pattern_char,
3282 FixedArrayBuilder* builder,
3283 int* match_pos) {
3284 // Position of last match.
3285 int pos = *match_pos;
3286 int subject_length = subject.length();
3287 while (pos < subject_length) {
3288 int match_end = pos + 1;
3289 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3290 *match_pos = pos;
3291 return false;
3292 }
3293 int new_pos = SingleCharIndexOf(subject, pattern_char, match_end);
3294 if (new_pos >= 0) {
3295 // Match has been found.
3296 if (new_pos > match_end) {
3297 ReplacementStringBuilder::AddSubjectSlice(builder, match_end, new_pos);
3298 }
3299 pos = new_pos;
3300 builder->Add(pattern);
3301 } else {
3302 break;
3303 }
3304 }
3305 if (pos + 1 < subject_length) {
3306 ReplacementStringBuilder::AddSubjectSlice(builder, pos + 1, subject_length);
3307 }
3308 *match_pos = pos;
3309 return true;
3310}
3311
3312
3313static bool SearchCharMultiple(Handle<String> subject,
3314 Handle<String> pattern,
3315 Handle<JSArray> last_match_info,
3316 FixedArrayBuilder* builder) {
3317 ASSERT(subject->IsFlat());
3318 ASSERT_EQ(1, pattern->length());
3319 uc16 pattern_char = pattern->Get(0);
3320 // Treating position before first as initial "previous match position".
3321 int match_pos = -1;
3322
3323 for (;;) { // Break when search complete.
3324 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3325 AssertNoAllocation no_gc;
3326 if (subject->IsAsciiRepresentation()) {
3327 if (pattern_char > String::kMaxAsciiCharCode) {
3328 break;
3329 }
3330 Vector<const char> subject_vector = subject->ToAsciiVector();
3331 char pattern_ascii_char = static_cast<char>(pattern_char);
3332 bool complete = SearchCharMultiple<const char>(subject_vector,
3333 *pattern,
3334 pattern_ascii_char,
3335 builder,
3336 &match_pos);
3337 if (complete) break;
3338 } else {
3339 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3340 bool complete = SearchCharMultiple<const uc16>(subject_vector,
3341 *pattern,
3342 pattern_char,
3343 builder,
3344 &match_pos);
3345 if (complete) break;
3346 }
3347 }
3348
3349 if (match_pos >= 0) {
3350 SetLastMatchInfoNoCaptures(subject,
3351 last_match_info,
3352 match_pos,
3353 match_pos + 1);
3354 return true;
3355 }
3356 return false; // No matches at all.
3357}
3358
3359
3360template <typename schar, typename pchar>
3361static bool SearchStringMultiple(Vector<schar> subject,
3362 String* pattern,
3363 Vector<pchar> pattern_string,
3364 FixedArrayBuilder* builder,
3365 int* match_pos) {
3366 int pos = *match_pos;
3367 int subject_length = subject.length();
3368 int pattern_length = pattern_string.length();
3369 int max_search_start = subject_length - pattern_length;
3370 bool is_ascii = (sizeof(schar) == 1);
3371 StringSearchStrategy strategy =
3372 InitializeStringSearch(pattern_string, is_ascii);
3373 switch (strategy) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003374 case SEARCH_FAIL: break;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003375 case SEARCH_SHORT:
3376 while (pos <= max_search_start) {
3377 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3378 *match_pos = pos;
3379 return false;
3380 }
3381 // Position of end of previous match.
3382 int match_end = pos + pattern_length;
3383 int new_pos = SimpleIndexOf(subject, pattern_string, match_end);
3384 if (new_pos >= 0) {
3385 // A match.
3386 if (new_pos > match_end) {
3387 ReplacementStringBuilder::AddSubjectSlice(builder,
3388 match_end,
3389 new_pos);
3390 }
3391 pos = new_pos;
3392 builder->Add(pattern);
3393 } else {
3394 break;
3395 }
3396 }
3397 break;
3398 case SEARCH_LONG:
3399 while (pos <= max_search_start) {
3400 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003401 *match_pos = pos;
3402 return false;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003403 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003404 int match_end = pos + pattern_length;
3405 int new_pos = ComplexIndexOf(subject, pattern_string, match_end);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003406 if (new_pos >= 0) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003407 // A match has been found.
3408 if (new_pos > match_end) {
3409 ReplacementStringBuilder::AddSubjectSlice(builder,
3410 match_end,
3411 new_pos);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003412 }
3413 pos = new_pos;
3414 builder->Add(pattern);
3415 } else {
3416 break;
3417 }
3418 }
3419 break;
3420 }
3421 if (pos < max_search_start) {
3422 ReplacementStringBuilder::AddSubjectSlice(builder,
3423 pos + pattern_length,
3424 subject_length);
3425 }
3426 *match_pos = pos;
3427 return true;
3428}
3429
3430
3431static bool SearchStringMultiple(Handle<String> subject,
3432 Handle<String> pattern,
3433 Handle<JSArray> last_match_info,
3434 FixedArrayBuilder* builder) {
3435 ASSERT(subject->IsFlat());
3436 ASSERT(pattern->IsFlat());
3437 ASSERT(pattern->length() > 1);
3438
3439 // Treating as if a previous match was before first character.
3440 int match_pos = -pattern->length();
3441
3442 for (;;) { // Break when search complete.
3443 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3444 AssertNoAllocation no_gc;
3445 if (subject->IsAsciiRepresentation()) {
3446 Vector<const char> subject_vector = subject->ToAsciiVector();
3447 if (pattern->IsAsciiRepresentation()) {
3448 if (SearchStringMultiple(subject_vector,
3449 *pattern,
3450 pattern->ToAsciiVector(),
3451 builder,
3452 &match_pos)) break;
3453 } else {
3454 if (SearchStringMultiple(subject_vector,
3455 *pattern,
3456 pattern->ToUC16Vector(),
3457 builder,
3458 &match_pos)) break;
3459 }
3460 } else {
3461 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3462 if (pattern->IsAsciiRepresentation()) {
3463 if (SearchStringMultiple(subject_vector,
3464 *pattern,
3465 pattern->ToAsciiVector(),
3466 builder,
3467 &match_pos)) break;
3468 } else {
3469 if (SearchStringMultiple(subject_vector,
3470 *pattern,
3471 pattern->ToUC16Vector(),
3472 builder,
3473 &match_pos)) break;
3474 }
3475 }
3476 }
3477
3478 if (match_pos >= 0) {
3479 SetLastMatchInfoNoCaptures(subject,
3480 last_match_info,
3481 match_pos,
3482 match_pos + pattern->length());
3483 return true;
3484 }
3485 return false; // No matches at all.
3486}
3487
3488
3489static RegExpImpl::IrregexpResult SearchRegExpNoCaptureMultiple(
3490 Handle<String> subject,
3491 Handle<JSRegExp> regexp,
3492 Handle<JSArray> last_match_array,
3493 FixedArrayBuilder* builder) {
3494 ASSERT(subject->IsFlat());
3495 int match_start = -1;
3496 int match_end = 0;
3497 int pos = 0;
3498 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3499 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3500
3501 OffsetsVector registers(required_registers);
3502 Vector<int> register_vector(registers.vector(), registers.length());
3503 int subject_length = subject->length();
3504
3505 for (;;) { // Break on failure, return on exception.
3506 RegExpImpl::IrregexpResult result =
3507 RegExpImpl::IrregexpExecOnce(regexp,
3508 subject,
3509 pos,
3510 register_vector);
3511 if (result == RegExpImpl::RE_SUCCESS) {
3512 match_start = register_vector[0];
3513 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3514 if (match_end < match_start) {
3515 ReplacementStringBuilder::AddSubjectSlice(builder,
3516 match_end,
3517 match_start);
3518 }
3519 match_end = register_vector[1];
3520 HandleScope loop_scope;
3521 builder->Add(*Factory::NewSubString(subject, match_start, match_end));
3522 if (match_start != match_end) {
3523 pos = match_end;
3524 } else {
3525 pos = match_end + 1;
3526 if (pos > subject_length) break;
3527 }
3528 } else if (result == RegExpImpl::RE_FAILURE) {
3529 break;
3530 } else {
3531 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3532 return result;
3533 }
3534 }
3535
3536 if (match_start >= 0) {
3537 if (match_end < subject_length) {
3538 ReplacementStringBuilder::AddSubjectSlice(builder,
3539 match_end,
3540 subject_length);
3541 }
3542 SetLastMatchInfoNoCaptures(subject,
3543 last_match_array,
3544 match_start,
3545 match_end);
3546 return RegExpImpl::RE_SUCCESS;
3547 } else {
3548 return RegExpImpl::RE_FAILURE; // No matches at all.
3549 }
3550}
3551
3552
3553static RegExpImpl::IrregexpResult SearchRegExpMultiple(
3554 Handle<String> subject,
3555 Handle<JSRegExp> regexp,
3556 Handle<JSArray> last_match_array,
3557 FixedArrayBuilder* builder) {
3558
3559 ASSERT(subject->IsFlat());
3560 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3561 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3562
3563 OffsetsVector registers(required_registers);
3564 Vector<int> register_vector(registers.vector(), registers.length());
3565
3566 RegExpImpl::IrregexpResult result =
3567 RegExpImpl::IrregexpExecOnce(regexp,
3568 subject,
3569 0,
3570 register_vector);
3571
3572 int capture_count = regexp->CaptureCount();
3573 int subject_length = subject->length();
3574
3575 // Position to search from.
3576 int pos = 0;
3577 // End of previous match. Differs from pos if match was empty.
3578 int match_end = 0;
3579 if (result == RegExpImpl::RE_SUCCESS) {
3580 // Need to keep a copy of the previous match for creating last_match_info
3581 // at the end, so we have two vectors that we swap between.
3582 OffsetsVector registers2(required_registers);
3583 Vector<int> prev_register_vector(registers2.vector(), registers2.length());
3584
3585 do {
3586 int match_start = register_vector[0];
3587 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3588 if (match_end < match_start) {
3589 ReplacementStringBuilder::AddSubjectSlice(builder,
3590 match_end,
3591 match_start);
3592 }
3593 match_end = register_vector[1];
3594
3595 {
3596 // Avoid accumulating new handles inside loop.
3597 HandleScope temp_scope;
3598 // Arguments array to replace function is match, captures, index and
3599 // subject, i.e., 3 + capture count in total.
3600 Handle<FixedArray> elements = Factory::NewFixedArray(3 + capture_count);
3601 elements->set(0, *Factory::NewSubString(subject,
3602 match_start,
3603 match_end));
3604 for (int i = 1; i <= capture_count; i++) {
3605 int start = register_vector[i * 2];
3606 if (start >= 0) {
3607 int end = register_vector[i * 2 + 1];
3608 ASSERT(start <= end);
3609 Handle<String> substring = Factory::NewSubString(subject,
3610 start,
3611 end);
3612 elements->set(i, *substring);
3613 } else {
3614 ASSERT(register_vector[i * 2 + 1] < 0);
3615 elements->set(i, Heap::undefined_value());
3616 }
3617 }
3618 elements->set(capture_count + 1, Smi::FromInt(match_start));
3619 elements->set(capture_count + 2, *subject);
3620 builder->Add(*Factory::NewJSArrayWithElements(elements));
3621 }
3622 // Swap register vectors, so the last successful match is in
3623 // prev_register_vector.
3624 Vector<int> tmp = prev_register_vector;
3625 prev_register_vector = register_vector;
3626 register_vector = tmp;
3627
3628 if (match_end > match_start) {
3629 pos = match_end;
3630 } else {
3631 pos = match_end + 1;
3632 if (pos > subject_length) {
3633 break;
3634 }
3635 }
3636
3637 result = RegExpImpl::IrregexpExecOnce(regexp,
3638 subject,
3639 pos,
3640 register_vector);
3641 } while (result == RegExpImpl::RE_SUCCESS);
3642
3643 if (result != RegExpImpl::RE_EXCEPTION) {
3644 // Finished matching, with at least one match.
3645 if (match_end < subject_length) {
3646 ReplacementStringBuilder::AddSubjectSlice(builder,
3647 match_end,
3648 subject_length);
3649 }
3650
3651 int last_match_capture_count = (capture_count + 1) * 2;
3652 int last_match_array_size =
3653 last_match_capture_count + RegExpImpl::kLastMatchOverhead;
3654 last_match_array->EnsureSize(last_match_array_size);
3655 AssertNoAllocation no_gc;
3656 FixedArray* elements = FixedArray::cast(last_match_array->elements());
3657 RegExpImpl::SetLastCaptureCount(elements, last_match_capture_count);
3658 RegExpImpl::SetLastSubject(elements, *subject);
3659 RegExpImpl::SetLastInput(elements, *subject);
3660 for (int i = 0; i < last_match_capture_count; i++) {
3661 RegExpImpl::SetCapture(elements, i, prev_register_vector[i]);
3662 }
3663 return RegExpImpl::RE_SUCCESS;
3664 }
3665 }
3666 // No matches at all, return failure or exception result directly.
3667 return result;
3668}
3669
3670
3671static Object* Runtime_RegExpExecMultiple(Arguments args) {
3672 ASSERT(args.length() == 4);
3673 HandleScope handles;
3674
3675 CONVERT_ARG_CHECKED(String, subject, 1);
3676 if (!subject->IsFlat()) { FlattenString(subject); }
3677 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
3678 CONVERT_ARG_CHECKED(JSArray, last_match_info, 2);
3679 CONVERT_ARG_CHECKED(JSArray, result_array, 3);
3680
3681 ASSERT(last_match_info->HasFastElements());
3682 ASSERT(regexp->GetFlags().is_global());
3683 Handle<FixedArray> result_elements;
3684 if (result_array->HasFastElements()) {
3685 result_elements =
3686 Handle<FixedArray>(FixedArray::cast(result_array->elements()));
3687 } else {
3688 result_elements = Factory::NewFixedArrayWithHoles(16);
3689 }
3690 FixedArrayBuilder builder(result_elements);
3691
3692 if (regexp->TypeTag() == JSRegExp::ATOM) {
3693 Handle<String> pattern(
3694 String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex)));
3695 int pattern_length = pattern->length();
3696 if (pattern_length == 1) {
3697 if (SearchCharMultiple(subject, pattern, last_match_info, &builder)) {
3698 return *builder.ToJSArray(result_array);
3699 }
3700 return Heap::null_value();
3701 }
3702
3703 if (!pattern->IsFlat()) FlattenString(pattern);
3704 if (SearchStringMultiple(subject, pattern, last_match_info, &builder)) {
3705 return *builder.ToJSArray(result_array);
3706 }
3707 return Heap::null_value();
3708 }
3709
3710 ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
3711
3712 RegExpImpl::IrregexpResult result;
3713 if (regexp->CaptureCount() == 0) {
3714 result = SearchRegExpNoCaptureMultiple(subject,
3715 regexp,
3716 last_match_info,
3717 &builder);
3718 } else {
3719 result = SearchRegExpMultiple(subject, regexp, last_match_info, &builder);
3720 }
3721 if (result == RegExpImpl::RE_SUCCESS) return *builder.ToJSArray(result_array);
3722 if (result == RegExpImpl::RE_FAILURE) return Heap::null_value();
3723 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3724 return Failure::Exception();
3725}
3726
3727
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003728static Object* Runtime_NumberToRadixString(Arguments args) {
3729 NoHandleAllocation ha;
3730 ASSERT(args.length() == 2);
3731
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003732 // Fast case where the result is a one character string.
3733 if (args[0]->IsSmi() && args[1]->IsSmi()) {
3734 int value = Smi::cast(args[0])->value();
3735 int radix = Smi::cast(args[1])->value();
3736 if (value >= 0 && value < radix) {
3737 RUNTIME_ASSERT(radix <= 36);
3738 // Character array used for conversion.
3739 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
3740 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
3741 }
3742 }
3743
3744 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003745 CONVERT_DOUBLE_CHECKED(value, args[0]);
3746 if (isnan(value)) {
3747 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3748 }
3749 if (isinf(value)) {
3750 if (value < 0) {
3751 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3752 }
3753 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3754 }
3755 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
3756 int radix = FastD2I(radix_number);
3757 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3758 char* str = DoubleToRadixCString(value, radix);
3759 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3760 DeleteArray(str);
3761 return result;
3762}
3763
3764
3765static Object* Runtime_NumberToFixed(Arguments args) {
3766 NoHandleAllocation ha;
3767 ASSERT(args.length() == 2);
3768
3769 CONVERT_DOUBLE_CHECKED(value, args[0]);
3770 if (isnan(value)) {
3771 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3772 }
3773 if (isinf(value)) {
3774 if (value < 0) {
3775 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3776 }
3777 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3778 }
3779 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3780 int f = FastD2I(f_number);
3781 RUNTIME_ASSERT(f >= 0);
3782 char* str = DoubleToFixedCString(value, f);
3783 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3784 DeleteArray(str);
3785 return res;
3786}
3787
3788
3789static Object* Runtime_NumberToExponential(Arguments args) {
3790 NoHandleAllocation ha;
3791 ASSERT(args.length() == 2);
3792
3793 CONVERT_DOUBLE_CHECKED(value, args[0]);
3794 if (isnan(value)) {
3795 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3796 }
3797 if (isinf(value)) {
3798 if (value < 0) {
3799 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3800 }
3801 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3802 }
3803 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3804 int f = FastD2I(f_number);
3805 RUNTIME_ASSERT(f >= -1 && f <= 20);
3806 char* str = DoubleToExponentialCString(value, f);
3807 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3808 DeleteArray(str);
3809 return res;
3810}
3811
3812
3813static Object* Runtime_NumberToPrecision(Arguments args) {
3814 NoHandleAllocation ha;
3815 ASSERT(args.length() == 2);
3816
3817 CONVERT_DOUBLE_CHECKED(value, args[0]);
3818 if (isnan(value)) {
3819 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3820 }
3821 if (isinf(value)) {
3822 if (value < 0) {
3823 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3824 }
3825 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3826 }
3827 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3828 int f = FastD2I(f_number);
3829 RUNTIME_ASSERT(f >= 1 && f <= 21);
3830 char* str = DoubleToPrecisionCString(value, f);
3831 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3832 DeleteArray(str);
3833 return res;
3834}
3835
3836
3837// Returns a single character string where first character equals
3838// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003839static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003840 if (index < static_cast<uint32_t>(string->length())) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003841 string->TryFlatten();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003842 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003843 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003844 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003845 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003846}
3847
3848
3849Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
3850 // Handle [] indexing on Strings
3851 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003852 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
3853 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003854 }
3855
3856 // Handle [] indexing on String objects
3857 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003858 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
3859 Handle<Object> result =
3860 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
3861 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003862 }
3863
3864 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003865 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003866 return prototype->GetElement(index);
3867 }
3868
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003869 return GetElement(object, index);
3870}
3871
3872
3873Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003874 return object->GetElement(index);
3875}
3876
3877
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003878Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
3879 HandleScope scope;
3880
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003881 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003882 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003883 Handle<Object> error =
3884 Factory::NewTypeError("non_object_property_load",
3885 HandleVector(args, 2));
3886 return Top::Throw(*error);
3887 }
3888
3889 // Check if the given key is an array index.
3890 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003891 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003892 return GetElementOrCharAt(object, index);
3893 }
3894
3895 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003896 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003897 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003898 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003899 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003900 bool has_pending_exception = false;
3901 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003902 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003903 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003904 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003905 }
3906
ager@chromium.org32912102009-01-16 10:38:43 +00003907 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003908 // the element if so.
3909 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003910 return GetElementOrCharAt(object, index);
3911 } else {
3912 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003913 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003914 }
3915}
3916
3917
3918static Object* Runtime_GetProperty(Arguments args) {
3919 NoHandleAllocation ha;
3920 ASSERT(args.length() == 2);
3921
3922 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003923 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003924
3925 return Runtime::GetObjectProperty(object, key);
3926}
3927
3928
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003929// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003930static Object* Runtime_KeyedGetProperty(Arguments args) {
3931 NoHandleAllocation ha;
3932 ASSERT(args.length() == 2);
3933
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003934 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00003935 // itself.
3936 //
3937 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00003938 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00003939 // global proxy object never has properties. This is the case
3940 // because the global proxy object forwards everything to its hidden
3941 // prototype including local lookups.
3942 //
3943 // Additionally, we need to make sure that we do not cache results
3944 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003945 if (args[0]->IsJSObject() &&
3946 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00003947 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003948 args[1]->IsString()) {
3949 JSObject* receiver = JSObject::cast(args[0]);
3950 String* key = String::cast(args[1]);
3951 if (receiver->HasFastProperties()) {
3952 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003953 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003954 int offset = KeyedLookupCache::Lookup(receiver_map, key);
3955 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003956 Object* value = receiver->FastPropertyAt(offset);
3957 return value->IsTheHole() ? Heap::undefined_value() : value;
3958 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003959 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003960 LookupResult result;
3961 receiver->LocalLookup(key, &result);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00003962 if (result.IsProperty() && result.type() == FIELD) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003963 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003964 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003965 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003966 }
3967 } else {
3968 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003969 StringDictionary* dictionary = receiver->property_dictionary();
3970 int entry = dictionary->FindEntry(key);
3971 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003972 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003973 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003974 if (!receiver->IsGlobalObject()) return value;
3975 value = JSGlobalPropertyCell::cast(value)->value();
3976 if (!value->IsTheHole()) return value;
3977 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003978 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003979 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003980 } else if (args[0]->IsString() && args[1]->IsSmi()) {
3981 // Fast case for string indexing using [] with a smi index.
3982 HandleScope scope;
3983 Handle<String> str = args.at<String>(0);
3984 int index = Smi::cast(args[1])->value();
3985 Handle<Object> result = GetCharAt(str, index);
3986 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003987 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003988
3989 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003990 return Runtime::GetObjectProperty(args.at<Object>(0),
3991 args.at<Object>(1));
3992}
3993
3994
ager@chromium.org5c838252010-02-19 08:53:10 +00003995static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
3996 ASSERT(args.length() == 5);
3997 HandleScope scope;
3998 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3999 CONVERT_CHECKED(String, name, args[1]);
4000 CONVERT_CHECKED(Smi, flag_setter, args[2]);
4001 CONVERT_CHECKED(JSFunction, fun, args[3]);
4002 CONVERT_CHECKED(Smi, flag_attr, args[4]);
4003 int unchecked = flag_attr->value();
4004 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4005 RUNTIME_ASSERT(!obj->IsNull());
4006 LookupResult result;
4007 obj->LocalLookupRealNamedProperty(name, &result);
4008
4009 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
4010 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
4011 // delete it to avoid running into trouble in DefineAccessor, which
4012 // handles this incorrectly if the property is readonly (does nothing)
4013 if (result.IsProperty() &&
4014 (result.type() == FIELD || result.type() == NORMAL
4015 || result.type() == CONSTANT_FUNCTION)) {
4016 obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
4017 }
4018 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
4019}
4020
4021static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
4022 ASSERT(args.length() == 4);
4023 HandleScope scope;
4024 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
4025 CONVERT_ARG_CHECKED(String, name, 1);
4026 Handle<Object> obj_value = args.at<Object>(2);
4027
4028 CONVERT_CHECKED(Smi, flag, args[3]);
4029 int unchecked = flag->value();
4030 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4031
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004032 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
4033
4034 // Check if this is an element.
4035 uint32_t index;
4036 bool is_element = name->AsArrayIndex(&index);
4037
4038 // Special case for elements if any of the flags are true.
4039 // If elements are in fast case we always implicitly assume that:
4040 // DONT_DELETE: false, DONT_ENUM: false, READ_ONLY: false.
4041 if (((unchecked & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) &&
4042 is_element) {
4043 // Normalize the elements to enable attributes on the property.
4044 js_object->NormalizeElements();
4045 NumberDictionary* dictionary = js_object->element_dictionary();
4046 // Make sure that we never go back to fast case.
4047 dictionary->set_requires_slow_elements();
4048 PropertyDetails details = PropertyDetails(attr, NORMAL);
4049 dictionary->Set(index, *obj_value, details);
4050 }
4051
ager@chromium.org5c838252010-02-19 08:53:10 +00004052 LookupResult result;
4053 js_object->LocalLookupRealNamedProperty(*name, &result);
4054
ager@chromium.org5c838252010-02-19 08:53:10 +00004055 // Take special care when attributes are different and there is already
4056 // a property. For simplicity we normalize the property which enables us
4057 // to not worry about changing the instance_descriptor and creating a new
4058 // map. The current version of SetObjectProperty does not handle attributes
4059 // correctly in the case where a property is a field and is reset with
4060 // new attributes.
4061 if (result.IsProperty() && attr != result.GetAttributes()) {
4062 // New attributes - normalize to avoid writing to instance descriptor
4063 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
4064 // Use IgnoreAttributes version since a readonly property may be
4065 // overridden and SetProperty does not allow this.
4066 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
4067 *obj_value,
4068 attr);
4069 }
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004070
ager@chromium.org5c838252010-02-19 08:53:10 +00004071 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
4072}
4073
4074
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004075Object* Runtime::SetObjectProperty(Handle<Object> object,
4076 Handle<Object> key,
4077 Handle<Object> value,
4078 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004079 HandleScope scope;
4080
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004081 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004082 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004083 Handle<Object> error =
4084 Factory::NewTypeError("non_object_property_store",
4085 HandleVector(args, 2));
4086 return Top::Throw(*error);
4087 }
4088
4089 // If the object isn't a JavaScript object, we ignore the store.
4090 if (!object->IsJSObject()) return *value;
4091
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004092 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
4093
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004094 // Check if the given key is an array index.
4095 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004096 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004097 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
4098 // of a string using [] notation. We need to support this too in
4099 // JavaScript.
4100 // In the case of a String object we just need to redirect the assignment to
4101 // the underlying string if the index is in range. Since the underlying
4102 // string does nothing with the assignment then we can ignore such
4103 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004104 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004105 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004106 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004107
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004108 Handle<Object> result = SetElement(js_object, index, value);
4109 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004110 return *value;
4111 }
4112
4113 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004114 Handle<Object> result;
4115 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004116 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004117 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004118 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004119 key_string->TryFlatten();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004120 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004121 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004122 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004123 return *value;
4124 }
4125
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004126 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004127 bool has_pending_exception = false;
4128 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4129 if (has_pending_exception) return Failure::Exception();
4130 Handle<String> name = Handle<String>::cast(converted);
4131
4132 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004133 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004134 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004135 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004136 }
4137}
4138
4139
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004140Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
4141 Handle<Object> key,
4142 Handle<Object> value,
4143 PropertyAttributes attr) {
4144 HandleScope scope;
4145
4146 // Check if the given key is an array index.
4147 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004148 if (key->ToArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004149 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
4150 // of a string using [] notation. We need to support this too in
4151 // JavaScript.
4152 // In the case of a String object we just need to redirect the assignment to
4153 // the underlying string if the index is in range. Since the underlying
4154 // string does nothing with the assignment then we can ignore such
4155 // assignments.
4156 if (js_object->IsStringObjectWithCharacterAt(index)) {
4157 return *value;
4158 }
4159
4160 return js_object->SetElement(index, *value);
4161 }
4162
4163 if (key->IsString()) {
4164 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004165 return js_object->SetElement(index, *value);
4166 } else {
4167 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004168 key_string->TryFlatten();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004169 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
4170 *value,
4171 attr);
4172 }
4173 }
4174
4175 // Call-back into JavaScript to convert the key to a string.
4176 bool has_pending_exception = false;
4177 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4178 if (has_pending_exception) return Failure::Exception();
4179 Handle<String> name = Handle<String>::cast(converted);
4180
4181 if (name->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004182 return js_object->SetElement(index, *value);
4183 } else {
4184 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
4185 }
4186}
4187
4188
ager@chromium.orge2902be2009-06-08 12:21:35 +00004189Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
4190 Handle<Object> key) {
4191 HandleScope scope;
4192
4193 // Check if the given key is an array index.
4194 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004195 if (key->ToArrayIndex(&index)) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00004196 // In Firefox/SpiderMonkey, Safari and Opera you can access the
4197 // characters of a string using [] notation. In the case of a
4198 // String object we just need to redirect the deletion to the
4199 // underlying string if the index is in range. Since the
4200 // underlying string does nothing with the deletion, we can ignore
4201 // such deletions.
4202 if (js_object->IsStringObjectWithCharacterAt(index)) {
4203 return Heap::true_value();
4204 }
4205
4206 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
4207 }
4208
4209 Handle<String> key_string;
4210 if (key->IsString()) {
4211 key_string = Handle<String>::cast(key);
4212 } else {
4213 // Call-back into JavaScript to convert the key to a string.
4214 bool has_pending_exception = false;
4215 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4216 if (has_pending_exception) return Failure::Exception();
4217 key_string = Handle<String>::cast(converted);
4218 }
4219
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004220 key_string->TryFlatten();
ager@chromium.orge2902be2009-06-08 12:21:35 +00004221 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
4222}
4223
4224
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004225static Object* Runtime_SetProperty(Arguments args) {
4226 NoHandleAllocation ha;
4227 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
4228
4229 Handle<Object> object = args.at<Object>(0);
4230 Handle<Object> key = args.at<Object>(1);
4231 Handle<Object> value = args.at<Object>(2);
4232
4233 // Compute attributes.
4234 PropertyAttributes attributes = NONE;
4235 if (args.length() == 4) {
4236 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004237 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004238 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004239 RUNTIME_ASSERT(
4240 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4241 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004242 }
4243 return Runtime::SetObjectProperty(object, key, value, attributes);
4244}
4245
4246
4247// Set a local property, even if it is READ_ONLY. If the property does not
4248// exist, it will be added with attributes NONE.
4249static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
4250 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004251 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004252 CONVERT_CHECKED(JSObject, object, args[0]);
4253 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004254 // Compute attributes.
4255 PropertyAttributes attributes = NONE;
4256 if (args.length() == 4) {
4257 CONVERT_CHECKED(Smi, value_obj, args[3]);
4258 int unchecked_value = value_obj->value();
4259 // Only attribute bits should be set.
4260 RUNTIME_ASSERT(
4261 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4262 attributes = static_cast<PropertyAttributes>(unchecked_value);
4263 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004264
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004265 return object->
4266 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004267}
4268
4269
4270static Object* Runtime_DeleteProperty(Arguments args) {
4271 NoHandleAllocation ha;
4272 ASSERT(args.length() == 2);
4273
4274 CONVERT_CHECKED(JSObject, object, args[0]);
4275 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00004276 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004277}
4278
4279
ager@chromium.org9085a012009-05-11 19:22:57 +00004280static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
4281 Handle<String> key) {
4282 if (object->HasLocalProperty(*key)) return Heap::true_value();
4283 // Handle hidden prototypes. If there's a hidden prototype above this thing
4284 // then we have to check it for properties, because they are supposed to
4285 // look like they are on this object.
4286 Handle<Object> proto(object->GetPrototype());
4287 if (proto->IsJSObject() &&
4288 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
4289 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
4290 }
4291 return Heap::false_value();
4292}
4293
4294
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004295static Object* Runtime_HasLocalProperty(Arguments args) {
4296 NoHandleAllocation ha;
4297 ASSERT(args.length() == 2);
4298 CONVERT_CHECKED(String, key, args[1]);
4299
ager@chromium.org9085a012009-05-11 19:22:57 +00004300 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004301 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00004302 if (obj->IsJSObject()) {
4303 JSObject* object = JSObject::cast(obj);
4304 // Fast case - no interceptors.
4305 if (object->HasRealNamedProperty(key)) return Heap::true_value();
4306 // Slow case. Either it's not there or we have an interceptor. We should
4307 // have handles for this kind of deal.
4308 HandleScope scope;
4309 return HasLocalPropertyImplementation(Handle<JSObject>(object),
4310 Handle<String>(key));
4311 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004312 // Well, there is one exception: Handle [] on strings.
4313 uint32_t index;
4314 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00004315 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004316 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004317 return Heap::true_value();
4318 }
4319 }
4320 return Heap::false_value();
4321}
4322
4323
4324static Object* Runtime_HasProperty(Arguments args) {
4325 NoHandleAllocation na;
4326 ASSERT(args.length() == 2);
4327
4328 // Only JS objects can have properties.
4329 if (args[0]->IsJSObject()) {
4330 JSObject* object = JSObject::cast(args[0]);
4331 CONVERT_CHECKED(String, key, args[1]);
4332 if (object->HasProperty(key)) return Heap::true_value();
4333 }
4334 return Heap::false_value();
4335}
4336
4337
4338static Object* Runtime_HasElement(Arguments args) {
4339 NoHandleAllocation na;
4340 ASSERT(args.length() == 2);
4341
4342 // Only JS objects can have elements.
4343 if (args[0]->IsJSObject()) {
4344 JSObject* object = JSObject::cast(args[0]);
4345 CONVERT_CHECKED(Smi, index_obj, args[1]);
4346 uint32_t index = index_obj->value();
4347 if (object->HasElement(index)) return Heap::true_value();
4348 }
4349 return Heap::false_value();
4350}
4351
4352
4353static Object* Runtime_IsPropertyEnumerable(Arguments args) {
4354 NoHandleAllocation ha;
4355 ASSERT(args.length() == 2);
4356
4357 CONVERT_CHECKED(JSObject, object, args[0]);
4358 CONVERT_CHECKED(String, key, args[1]);
4359
4360 uint32_t index;
4361 if (key->AsArrayIndex(&index)) {
4362 return Heap::ToBoolean(object->HasElement(index));
4363 }
4364
ager@chromium.org870a0b62008-11-04 11:43:05 +00004365 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
4366 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004367}
4368
4369
4370static Object* Runtime_GetPropertyNames(Arguments args) {
4371 HandleScope scope;
4372 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004373 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004374 return *GetKeysFor(object);
4375}
4376
4377
4378// Returns either a FixedArray as Runtime_GetPropertyNames,
4379// or, if the given object has an enum cache that contains
4380// all enumerable properties of the object and its prototypes
4381// have none, the map of the object. This is used to speed up
4382// the check for deletions during a for-in.
4383static Object* Runtime_GetPropertyNamesFast(Arguments args) {
4384 ASSERT(args.length() == 1);
4385
4386 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4387
4388 if (raw_object->IsSimpleEnum()) return raw_object->map();
4389
4390 HandleScope scope;
4391 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004392 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
4393 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004394
4395 // Test again, since cache may have been built by preceding call.
4396 if (object->IsSimpleEnum()) return object->map();
4397
4398 return *content;
4399}
4400
4401
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004402// Find the length of the prototype chain that is to to handled as one. If a
4403// prototype object is hidden it is to be viewed as part of the the object it
4404// is prototype for.
4405static int LocalPrototypeChainLength(JSObject* obj) {
4406 int count = 1;
4407 Object* proto = obj->GetPrototype();
4408 while (proto->IsJSObject() &&
4409 JSObject::cast(proto)->map()->is_hidden_prototype()) {
4410 count++;
4411 proto = JSObject::cast(proto)->GetPrototype();
4412 }
4413 return count;
4414}
4415
4416
4417// Return the names of the local named properties.
4418// args[0]: object
4419static Object* Runtime_GetLocalPropertyNames(Arguments args) {
4420 HandleScope scope;
4421 ASSERT(args.length() == 1);
4422 if (!args[0]->IsJSObject()) {
4423 return Heap::undefined_value();
4424 }
4425 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4426
4427 // Skip the global proxy as it has no properties and always delegates to the
4428 // real global object.
4429 if (obj->IsJSGlobalProxy()) {
4430 // Only collect names if access is permitted.
4431 if (obj->IsAccessCheckNeeded() &&
4432 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
4433 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
4434 return *Factory::NewJSArray(0);
4435 }
4436 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
4437 }
4438
4439 // Find the number of objects making up this.
4440 int length = LocalPrototypeChainLength(*obj);
4441
4442 // Find the number of local properties for each of the objects.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004443 ScopedVector<int> local_property_count(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004444 int total_property_count = 0;
4445 Handle<JSObject> jsproto = obj;
4446 for (int i = 0; i < length; i++) {
4447 // Only collect names if access is permitted.
4448 if (jsproto->IsAccessCheckNeeded() &&
4449 !Top::MayNamedAccess(*jsproto,
4450 Heap::undefined_value(),
4451 v8::ACCESS_KEYS)) {
4452 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
4453 return *Factory::NewJSArray(0);
4454 }
4455 int n;
4456 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4457 local_property_count[i] = n;
4458 total_property_count += n;
4459 if (i < length - 1) {
4460 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4461 }
4462 }
4463
4464 // Allocate an array with storage for all the property names.
4465 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
4466
4467 // Get the property names.
4468 jsproto = obj;
4469 int proto_with_hidden_properties = 0;
4470 for (int i = 0; i < length; i++) {
4471 jsproto->GetLocalPropertyNames(*names,
4472 i == 0 ? 0 : local_property_count[i - 1]);
4473 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
4474 proto_with_hidden_properties++;
4475 }
4476 if (i < length - 1) {
4477 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4478 }
4479 }
4480
4481 // Filter out name of hidden propeties object.
4482 if (proto_with_hidden_properties > 0) {
4483 Handle<FixedArray> old_names = names;
4484 names = Factory::NewFixedArray(
4485 names->length() - proto_with_hidden_properties);
4486 int dest_pos = 0;
4487 for (int i = 0; i < total_property_count; i++) {
4488 Object* name = old_names->get(i);
4489 if (name == Heap::hidden_symbol()) {
4490 continue;
4491 }
4492 names->set(dest_pos++, name);
4493 }
4494 }
4495
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004496 return *Factory::NewJSArrayWithElements(names);
4497}
4498
4499
4500// Return the names of the local indexed properties.
4501// args[0]: object
4502static Object* Runtime_GetLocalElementNames(Arguments args) {
4503 HandleScope scope;
4504 ASSERT(args.length() == 1);
4505 if (!args[0]->IsJSObject()) {
4506 return Heap::undefined_value();
4507 }
4508 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4509
4510 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4511 Handle<FixedArray> names = Factory::NewFixedArray(n);
4512 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4513 return *Factory::NewJSArrayWithElements(names);
4514}
4515
4516
4517// Return information on whether an object has a named or indexed interceptor.
4518// args[0]: object
4519static Object* Runtime_GetInterceptorInfo(Arguments args) {
4520 HandleScope scope;
4521 ASSERT(args.length() == 1);
4522 if (!args[0]->IsJSObject()) {
4523 return Smi::FromInt(0);
4524 }
4525 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4526
4527 int result = 0;
4528 if (obj->HasNamedInterceptor()) result |= 2;
4529 if (obj->HasIndexedInterceptor()) result |= 1;
4530
4531 return Smi::FromInt(result);
4532}
4533
4534
4535// Return property names from named interceptor.
4536// args[0]: object
4537static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
4538 HandleScope scope;
4539 ASSERT(args.length() == 1);
4540 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4541
4542 if (obj->HasNamedInterceptor()) {
4543 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4544 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4545 }
4546 return Heap::undefined_value();
4547}
4548
4549
4550// Return element names from indexed interceptor.
4551// args[0]: object
4552static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
4553 HandleScope scope;
4554 ASSERT(args.length() == 1);
4555 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4556
4557 if (obj->HasIndexedInterceptor()) {
4558 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4559 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4560 }
4561 return Heap::undefined_value();
4562}
4563
4564
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004565static Object* Runtime_LocalKeys(Arguments args) {
4566 ASSERT_EQ(args.length(), 1);
4567 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4568 HandleScope scope;
4569 Handle<JSObject> object(raw_object);
4570 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
4571 LOCAL_ONLY);
4572 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
4573 // property array and since the result is mutable we have to create
4574 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004575 int length = contents->length();
4576 Handle<FixedArray> copy = Factory::NewFixedArray(length);
4577 for (int i = 0; i < length; i++) {
4578 Object* entry = contents->get(i);
4579 if (entry->IsString()) {
4580 copy->set(i, entry);
4581 } else {
4582 ASSERT(entry->IsNumber());
4583 HandleScope scope;
4584 Handle<Object> entry_handle(entry);
4585 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
4586 copy->set(i, *entry_str);
4587 }
4588 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004589 return *Factory::NewJSArrayWithElements(copy);
4590}
4591
4592
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004593static Object* Runtime_GetArgumentsProperty(Arguments args) {
4594 NoHandleAllocation ha;
4595 ASSERT(args.length() == 1);
4596
4597 // Compute the frame holding the arguments.
4598 JavaScriptFrameIterator it;
4599 it.AdvanceToArgumentsFrame();
4600 JavaScriptFrame* frame = it.frame();
4601
4602 // Get the actual number of provided arguments.
4603 const uint32_t n = frame->GetProvidedParametersCount();
4604
4605 // Try to convert the key to an index. If successful and within
4606 // index return the the argument from the frame.
4607 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004608 if (args[0]->ToArrayIndex(&index) && index < n) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004609 return frame->GetParameter(index);
4610 }
4611
4612 // Convert the key to a string.
4613 HandleScope scope;
4614 bool exception = false;
4615 Handle<Object> converted =
4616 Execution::ToString(args.at<Object>(0), &exception);
4617 if (exception) return Failure::Exception();
4618 Handle<String> key = Handle<String>::cast(converted);
4619
4620 // Try to convert the string key into an array index.
4621 if (key->AsArrayIndex(&index)) {
4622 if (index < n) {
4623 return frame->GetParameter(index);
4624 } else {
4625 return Top::initial_object_prototype()->GetElement(index);
4626 }
4627 }
4628
4629 // Handle special arguments properties.
4630 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
4631 if (key->Equals(Heap::callee_symbol())) return frame->function();
4632
4633 // Lookup in the initial Object.prototype object.
4634 return Top::initial_object_prototype()->GetProperty(*key);
4635}
4636
4637
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004638static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004639 HandleScope scope;
4640
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004641 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004642 Handle<Object> object = args.at<Object>(0);
4643 if (object->IsJSObject()) {
4644 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00004645 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
4646 js_object->TransformToFastProperties(0);
4647 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004648 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004649 return *object;
4650}
4651
4652
4653static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004654 HandleScope scope;
4655
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004656 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004657 Handle<Object> object = args.at<Object>(0);
4658 if (object->IsJSObject()) {
4659 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004660 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004661 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004662 return *object;
4663}
4664
4665
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004666static Object* Runtime_ToBool(Arguments args) {
4667 NoHandleAllocation ha;
4668 ASSERT(args.length() == 1);
4669
4670 return args[0]->ToBoolean();
4671}
4672
4673
4674// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
4675// Possible optimizations: put the type string into the oddballs.
4676static Object* Runtime_Typeof(Arguments args) {
4677 NoHandleAllocation ha;
4678
4679 Object* obj = args[0];
4680 if (obj->IsNumber()) return Heap::number_symbol();
4681 HeapObject* heap_obj = HeapObject::cast(obj);
4682
4683 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004684 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004685
4686 InstanceType instance_type = heap_obj->map()->instance_type();
4687 if (instance_type < FIRST_NONSTRING_TYPE) {
4688 return Heap::string_symbol();
4689 }
4690
4691 switch (instance_type) {
4692 case ODDBALL_TYPE:
4693 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
4694 return Heap::boolean_symbol();
4695 }
4696 if (heap_obj->IsNull()) {
4697 return Heap::object_symbol();
4698 }
4699 ASSERT(heap_obj->IsUndefined());
4700 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004701 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004702 return Heap::function_symbol();
4703 default:
4704 // For any kind of object not handled above, the spec rule for
4705 // host objects gives that it is okay to return "object"
4706 return Heap::object_symbol();
4707 }
4708}
4709
4710
lrn@chromium.org25156de2010-04-06 13:10:27 +00004711static bool AreDigits(const char*s, int from, int to) {
4712 for (int i = from; i < to; i++) {
4713 if (s[i] < '0' || s[i] > '9') return false;
4714 }
4715
4716 return true;
4717}
4718
4719
4720static int ParseDecimalInteger(const char*s, int from, int to) {
4721 ASSERT(to - from < 10); // Overflow is not possible.
4722 ASSERT(from < to);
4723 int d = s[from] - '0';
4724
4725 for (int i = from + 1; i < to; i++) {
4726 d = 10 * d + (s[i] - '0');
4727 }
4728
4729 return d;
4730}
4731
4732
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004733static Object* Runtime_StringToNumber(Arguments args) {
4734 NoHandleAllocation ha;
4735 ASSERT(args.length() == 1);
4736 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004737 subject->TryFlatten();
lrn@chromium.org25156de2010-04-06 13:10:27 +00004738
4739 // Fast case: short integer or some sorts of junk values.
4740 int len = subject->length();
4741 if (subject->IsSeqAsciiString()) {
4742 if (len == 0) return Smi::FromInt(0);
4743
4744 char const* data = SeqAsciiString::cast(subject)->GetChars();
4745 bool minus = (data[0] == '-');
4746 int start_pos = (minus ? 1 : 0);
4747
4748 if (start_pos == len) {
4749 return Heap::nan_value();
4750 } else if (data[start_pos] > '9') {
4751 // Fast check for a junk value. A valid string may start from a
4752 // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit or
4753 // the 'I' character ('Infinity'). All of that have codes not greater than
4754 // '9' except 'I'.
4755 if (data[start_pos] != 'I') {
4756 return Heap::nan_value();
4757 }
4758 } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
4759 // The maximal/minimal smi has 10 digits. If the string has less digits we
4760 // know it will fit into the smi-data type.
4761 int d = ParseDecimalInteger(data, start_pos, len);
4762 if (minus) {
4763 if (d == 0) return Heap::minus_zero_value();
4764 d = -d;
4765 }
4766 return Smi::FromInt(d);
4767 }
4768 }
4769
4770 // Slower case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004771 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
4772}
4773
4774
4775static Object* Runtime_StringFromCharCodeArray(Arguments args) {
4776 NoHandleAllocation ha;
4777 ASSERT(args.length() == 1);
4778
4779 CONVERT_CHECKED(JSArray, codes, args[0]);
4780 int length = Smi::cast(codes->length())->value();
4781
4782 // Check if the string can be ASCII.
4783 int i;
4784 for (i = 0; i < length; i++) {
4785 Object* element = codes->GetElement(i);
4786 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
4787 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
4788 break;
4789 }
4790
4791 Object* object = NULL;
4792 if (i == length) { // The string is ASCII.
4793 object = Heap::AllocateRawAsciiString(length);
4794 } else { // The string is not ASCII.
4795 object = Heap::AllocateRawTwoByteString(length);
4796 }
4797
4798 if (object->IsFailure()) return object;
4799 String* result = String::cast(object);
4800 for (int i = 0; i < length; i++) {
4801 Object* element = codes->GetElement(i);
4802 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004803 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004804 }
4805 return result;
4806}
4807
4808
4809// kNotEscaped is generated by the following:
4810//
4811// #!/bin/perl
4812// for (my $i = 0; $i < 256; $i++) {
4813// print "\n" if $i % 16 == 0;
4814// my $c = chr($i);
4815// my $escaped = 1;
4816// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
4817// print $escaped ? "0, " : "1, ";
4818// }
4819
4820
4821static bool IsNotEscaped(uint16_t character) {
4822 // Only for 8 bit characters, the rest are always escaped (in a different way)
4823 ASSERT(character < 256);
4824 static const char kNotEscaped[256] = {
4825 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4826 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4827 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
4828 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
4829 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4830 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
4831 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4832 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
4833 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4834 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4835 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4836 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4837 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4838 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4839 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4840 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4841 };
4842 return kNotEscaped[character] != 0;
4843}
4844
4845
4846static Object* Runtime_URIEscape(Arguments args) {
4847 const char hex_chars[] = "0123456789ABCDEF";
4848 NoHandleAllocation ha;
4849 ASSERT(args.length() == 1);
4850 CONVERT_CHECKED(String, source, args[0]);
4851
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004852 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004853
4854 int escaped_length = 0;
4855 int length = source->length();
4856 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004857 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004858 buffer->Reset(source);
4859 while (buffer->has_more()) {
4860 uint16_t character = buffer->GetNext();
4861 if (character >= 256) {
4862 escaped_length += 6;
4863 } else if (IsNotEscaped(character)) {
4864 escaped_length++;
4865 } else {
4866 escaped_length += 3;
4867 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004868 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004869 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004870 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004871 Top::context()->mark_out_of_memory();
4872 return Failure::OutOfMemoryException();
4873 }
4874 }
4875 }
4876 // No length change implies no change. Return original string if no change.
4877 if (escaped_length == length) {
4878 return source;
4879 }
4880 Object* o = Heap::AllocateRawAsciiString(escaped_length);
4881 if (o->IsFailure()) return o;
4882 String* destination = String::cast(o);
4883 int dest_position = 0;
4884
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004885 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004886 buffer->Rewind();
4887 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00004888 uint16_t chr = buffer->GetNext();
4889 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004890 destination->Set(dest_position, '%');
4891 destination->Set(dest_position+1, 'u');
4892 destination->Set(dest_position+2, hex_chars[chr >> 12]);
4893 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
4894 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
4895 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004896 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00004897 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004898 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004899 dest_position++;
4900 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004901 destination->Set(dest_position, '%');
4902 destination->Set(dest_position+1, hex_chars[chr >> 4]);
4903 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004904 dest_position += 3;
4905 }
4906 }
4907 return destination;
4908}
4909
4910
4911static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
4912 static const signed char kHexValue['g'] = {
4913 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4914 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4915 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4916 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
4917 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4918 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4919 -1, 10, 11, 12, 13, 14, 15 };
4920
4921 if (character1 > 'f') return -1;
4922 int hi = kHexValue[character1];
4923 if (hi == -1) return -1;
4924 if (character2 > 'f') return -1;
4925 int lo = kHexValue[character2];
4926 if (lo == -1) return -1;
4927 return (hi << 4) + lo;
4928}
4929
4930
ager@chromium.org870a0b62008-11-04 11:43:05 +00004931static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004932 int i,
4933 int length,
4934 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004935 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00004936 int32_t hi = 0;
4937 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004938 if (character == '%' &&
4939 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004940 source->Get(i + 1) == 'u' &&
4941 (hi = TwoDigitHex(source->Get(i + 2),
4942 source->Get(i + 3))) != -1 &&
4943 (lo = TwoDigitHex(source->Get(i + 4),
4944 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004945 *step = 6;
4946 return (hi << 8) + lo;
4947 } else if (character == '%' &&
4948 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004949 (lo = TwoDigitHex(source->Get(i + 1),
4950 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004951 *step = 3;
4952 return lo;
4953 } else {
4954 *step = 1;
4955 return character;
4956 }
4957}
4958
4959
4960static Object* Runtime_URIUnescape(Arguments args) {
4961 NoHandleAllocation ha;
4962 ASSERT(args.length() == 1);
4963 CONVERT_CHECKED(String, source, args[0]);
4964
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004965 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004966
4967 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004968 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004969
4970 int unescaped_length = 0;
4971 for (int i = 0; i < length; unescaped_length++) {
4972 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004973 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004974 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004975 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004976 i += step;
4977 }
4978
4979 // No length change implies no change. Return original string if no change.
4980 if (unescaped_length == length)
4981 return source;
4982
4983 Object* o = ascii ?
4984 Heap::AllocateRawAsciiString(unescaped_length) :
4985 Heap::AllocateRawTwoByteString(unescaped_length);
4986 if (o->IsFailure()) return o;
4987 String* destination = String::cast(o);
4988
4989 int dest_position = 0;
4990 for (int i = 0; i < length; dest_position++) {
4991 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004992 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004993 i += step;
4994 }
4995 return destination;
4996}
4997
4998
4999static Object* Runtime_StringParseInt(Arguments args) {
5000 NoHandleAllocation ha;
5001
5002 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005003 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005004
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005005 s->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005006
lrn@chromium.org25156de2010-04-06 13:10:27 +00005007 RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
5008 double value = StringToInt(s, radix);
5009 return Heap::NumberFromDouble(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005010 return Heap::nan_value();
5011}
5012
5013
5014static Object* Runtime_StringParseFloat(Arguments args) {
5015 NoHandleAllocation ha;
5016 CONVERT_CHECKED(String, str, args[0]);
5017
5018 // ECMA-262 section 15.1.2.3, empty string is NaN
5019 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
5020
5021 // Create a number object from the value.
5022 return Heap::NumberFromDouble(value);
5023}
5024
5025
5026static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
5027static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
5028
5029
5030template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005031static Object* ConvertCaseHelper(String* s,
5032 int length,
5033 int input_string_length,
5034 unibrow::Mapping<Converter, 128>* mapping) {
5035 // We try this twice, once with the assumption that the result is no longer
5036 // than the input and, if that assumption breaks, again with the exact
5037 // length. This may not be pretty, but it is nicer than what was here before
5038 // and I hereby claim my vaffel-is.
5039 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005040 // Allocate the resulting string.
5041 //
5042 // NOTE: This assumes that the upper/lower case of an ascii
5043 // character is also ascii. This is currently the case, but it
5044 // might break in the future if we implement more context and locale
5045 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00005046 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005047 ? Heap::AllocateRawAsciiString(length)
5048 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005049 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005050 String* result = String::cast(o);
5051 bool has_changed_character = false;
5052
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005053 // Convert all characters to upper case, assuming that they will fit
5054 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005055 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005056 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005057 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005058 // We can assume that the string is not empty
5059 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005060 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00005061 bool has_next = buffer->has_more();
5062 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005063 int char_length = mapping->get(current, next, chars);
5064 if (char_length == 0) {
5065 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005066 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005067 i++;
5068 } else if (char_length == 1) {
5069 // Common case: converting the letter resulted in one character.
5070 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005071 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005072 has_changed_character = true;
5073 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005074 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005075 // We've assumed that the result would be as long as the
5076 // input but here is a character that converts to several
5077 // characters. No matter, we calculate the exact length
5078 // of the result and try the whole thing again.
5079 //
5080 // Note that this leaves room for optimization. We could just
5081 // memcpy what we already have to the result string. Also,
5082 // the result string is the last object allocated we could
5083 // "realloc" it and probably, in the vast majority of cases,
5084 // extend the existing string to be able to hold the full
5085 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00005086 int next_length = 0;
5087 if (has_next) {
5088 next_length = mapping->get(next, 0, chars);
5089 if (next_length == 0) next_length = 1;
5090 }
5091 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005092 while (buffer->has_more()) {
5093 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00005094 // NOTE: we use 0 as the next character here because, while
5095 // the next character may affect what a character converts to,
5096 // it does not in any case affect the length of what it convert
5097 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005098 int char_length = mapping->get(current, 0, chars);
5099 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00005100 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005101 if (current_length > Smi::kMaxValue) {
5102 Top::context()->mark_out_of_memory();
5103 return Failure::OutOfMemoryException();
5104 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005105 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005106 // Try again with the real length.
5107 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005108 } else {
5109 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005110 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005111 i++;
5112 }
5113 has_changed_character = true;
5114 }
5115 current = next;
5116 }
5117 if (has_changed_character) {
5118 return result;
5119 } else {
5120 // If we didn't actually change anything in doing the conversion
5121 // we simple return the result and let the converted string
5122 // become garbage; there is no reason to keep two identical strings
5123 // alive.
5124 return s;
5125 }
5126}
5127
5128
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005129namespace {
5130
5131struct ToLowerTraits {
5132 typedef unibrow::ToLowercase UnibrowConverter;
5133
5134 static bool ConvertAscii(char* dst, char* src, int length) {
5135 bool changed = false;
5136 for (int i = 0; i < length; ++i) {
5137 char c = src[i];
5138 if ('A' <= c && c <= 'Z') {
5139 c += ('a' - 'A');
5140 changed = true;
5141 }
5142 dst[i] = c;
5143 }
5144 return changed;
5145 }
5146};
5147
5148
5149struct ToUpperTraits {
5150 typedef unibrow::ToUppercase UnibrowConverter;
5151
5152 static bool ConvertAscii(char* dst, char* src, int length) {
5153 bool changed = false;
5154 for (int i = 0; i < length; ++i) {
5155 char c = src[i];
5156 if ('a' <= c && c <= 'z') {
5157 c -= ('a' - 'A');
5158 changed = true;
5159 }
5160 dst[i] = c;
5161 }
5162 return changed;
5163 }
5164};
5165
5166} // namespace
5167
5168
5169template <typename ConvertTraits>
5170static Object* ConvertCase(
5171 Arguments args,
5172 unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005173 NoHandleAllocation ha;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005174 CONVERT_CHECKED(String, s, args[0]);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005175 s = s->TryFlattenGetString();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005176
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005177 const int length = s->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005178 // Assume that the string is not empty; we need this assumption later
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005179 if (length == 0) return s;
5180
5181 // Simpler handling of ascii strings.
5182 //
5183 // NOTE: This assumes that the upper/lower case of an ascii
5184 // character is also ascii. This is currently the case, but it
5185 // might break in the future if we implement more context and locale
5186 // dependent upper/lower conversions.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005187 if (s->IsSeqAsciiString()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005188 Object* o = Heap::AllocateRawAsciiString(length);
5189 if (o->IsFailure()) return o;
5190 SeqAsciiString* result = SeqAsciiString::cast(o);
5191 bool has_changed_character = ConvertTraits::ConvertAscii(
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005192 result->GetChars(), SeqAsciiString::cast(s)->GetChars(), length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005193 return has_changed_character ? result : s;
5194 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005195
5196 Object* answer = ConvertCaseHelper(s, length, length, mapping);
5197 if (answer->IsSmi()) {
5198 // Retry with correct length.
5199 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
5200 }
5201 return answer; // This may be a failure.
5202}
5203
5204
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005205static Object* Runtime_StringToLowerCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005206 return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005207}
5208
5209
5210static Object* Runtime_StringToUpperCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005211 return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005212}
5213
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005214
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005215static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
5216 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
5217}
5218
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005219
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005220static Object* Runtime_StringTrim(Arguments args) {
5221 NoHandleAllocation ha;
5222 ASSERT(args.length() == 3);
5223
5224 CONVERT_CHECKED(String, s, args[0]);
5225 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
5226 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
5227
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005228 s->TryFlatten();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005229 int length = s->length();
5230
5231 int left = 0;
5232 if (trimLeft) {
5233 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
5234 left++;
5235 }
5236 }
5237
5238 int right = length;
5239 if (trimRight) {
5240 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
5241 right--;
5242 }
5243 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005244 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005245}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005246
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005247
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005248template <typename schar, typename pchar>
5249void FindStringIndices(Vector<const schar> subject,
5250 Vector<const pchar> pattern,
5251 ZoneList<int>* indices,
5252 unsigned int limit) {
5253 ASSERT(limit > 0);
5254 // Collect indices of pattern in subject, and the end-of-string index.
5255 // Stop after finding at most limit values.
5256 StringSearchStrategy strategy =
5257 InitializeStringSearch(pattern, sizeof(schar) == 1);
5258 switch (strategy) {
5259 case SEARCH_FAIL: return;
5260 case SEARCH_SHORT: {
5261 int pattern_length = pattern.length();
5262 int index = 0;
5263 while (limit > 0) {
5264 index = SimpleIndexOf(subject, pattern, index);
5265 if (index < 0) return;
5266 indices->Add(index);
5267 index += pattern_length;
5268 limit--;
5269 }
5270 return;
5271 }
5272 case SEARCH_LONG: {
5273 int pattern_length = pattern.length();
5274 int index = 0;
5275 while (limit > 0) {
5276 index = ComplexIndexOf(subject, pattern, index);
5277 if (index < 0) return;
5278 indices->Add(index);
5279 index += pattern_length;
5280 limit--;
5281 }
5282 return;
5283 }
5284 default:
5285 UNREACHABLE();
5286 return;
5287 }
5288}
5289
5290template <typename schar>
5291inline void FindCharIndices(Vector<const schar> subject,
5292 const schar pattern_char,
5293 ZoneList<int>* indices,
5294 unsigned int limit) {
5295 // Collect indices of pattern_char in subject, and the end-of-string index.
5296 // Stop after finding at most limit values.
5297 int index = 0;
5298 while (limit > 0) {
5299 index = SingleCharIndexOf(subject, pattern_char, index);
5300 if (index < 0) return;
5301 indices->Add(index);
5302 index++;
5303 limit--;
5304 }
5305}
5306
5307
5308static Object* Runtime_StringSplit(Arguments args) {
5309 ASSERT(args.length() == 3);
5310 HandleScope handle_scope;
5311 CONVERT_ARG_CHECKED(String, subject, 0);
5312 CONVERT_ARG_CHECKED(String, pattern, 1);
5313 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
5314
5315 int subject_length = subject->length();
5316 int pattern_length = pattern->length();
5317 RUNTIME_ASSERT(pattern_length > 0);
5318
5319 // The limit can be very large (0xffffffffu), but since the pattern
5320 // isn't empty, we can never create more parts than ~half the length
5321 // of the subject.
5322
5323 if (!subject->IsFlat()) FlattenString(subject);
5324
5325 static const int kMaxInitialListCapacity = 16;
5326
5327 ZoneScope scope(DELETE_ON_EXIT);
5328
5329 // Find (up to limit) indices of separator and end-of-string in subject
5330 int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
5331 ZoneList<int> indices(initial_capacity);
5332 if (pattern_length == 1) {
5333 // Special case, go directly to fast single-character split.
5334 AssertNoAllocation nogc;
5335 uc16 pattern_char = pattern->Get(0);
5336 if (subject->IsTwoByteRepresentation()) {
5337 FindCharIndices(subject->ToUC16Vector(), pattern_char,
5338 &indices,
5339 limit);
5340 } else if (pattern_char <= String::kMaxAsciiCharCode) {
5341 FindCharIndices(subject->ToAsciiVector(),
5342 static_cast<char>(pattern_char),
5343 &indices,
5344 limit);
5345 }
5346 } else {
5347 if (!pattern->IsFlat()) FlattenString(pattern);
5348 AssertNoAllocation nogc;
5349 if (subject->IsAsciiRepresentation()) {
5350 Vector<const char> subject_vector = subject->ToAsciiVector();
5351 if (pattern->IsAsciiRepresentation()) {
5352 FindStringIndices(subject_vector,
5353 pattern->ToAsciiVector(),
5354 &indices,
5355 limit);
5356 } else {
5357 FindStringIndices(subject_vector,
5358 pattern->ToUC16Vector(),
5359 &indices,
5360 limit);
5361 }
5362 } else {
5363 Vector<const uc16> subject_vector = subject->ToUC16Vector();
5364 if (pattern->IsAsciiRepresentation()) {
5365 FindStringIndices(subject_vector,
5366 pattern->ToAsciiVector(),
5367 &indices,
5368 limit);
5369 } else {
5370 FindStringIndices(subject_vector,
5371 pattern->ToUC16Vector(),
5372 &indices,
5373 limit);
5374 }
5375 }
5376 }
5377 if (static_cast<uint32_t>(indices.length()) < limit) {
5378 indices.Add(subject_length);
5379 }
5380 // The list indices now contains the end of each part to create.
5381
5382
5383 // Create JSArray of substrings separated by separator.
5384 int part_count = indices.length();
5385
5386 Handle<JSArray> result = Factory::NewJSArray(part_count);
5387 result->set_length(Smi::FromInt(part_count));
5388
5389 ASSERT(result->HasFastElements());
5390
5391 if (part_count == 1 && indices.at(0) == subject_length) {
5392 FixedArray::cast(result->elements())->set(0, *subject);
5393 return *result;
5394 }
5395
5396 Handle<FixedArray> elements(FixedArray::cast(result->elements()));
5397 int part_start = 0;
5398 for (int i = 0; i < part_count; i++) {
5399 HandleScope local_loop_handle;
5400 int part_end = indices.at(i);
5401 Handle<String> substring =
5402 Factory::NewSubString(subject, part_start, part_end);
5403 elements->set(i, *substring);
5404 part_start = part_end + pattern_length;
5405 }
5406
5407 return *result;
5408}
5409
5410
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005411// Copies ascii characters to the given fixed array looking up
5412// one-char strings in the cache. Gives up on the first char that is
5413// not in the cache and fills the remainder with smi zeros. Returns
5414// the length of the successfully copied prefix.
5415static int CopyCachedAsciiCharsToArray(const char* chars,
5416 FixedArray* elements,
5417 int length) {
5418 AssertNoAllocation nogc;
5419 FixedArray* ascii_cache = Heap::single_character_string_cache();
5420 Object* undefined = Heap::undefined_value();
5421 int i;
5422 for (i = 0; i < length; ++i) {
5423 Object* value = ascii_cache->get(chars[i]);
5424 if (value == undefined) break;
5425 ASSERT(!Heap::InNewSpace(value));
5426 elements->set(i, value, SKIP_WRITE_BARRIER);
5427 }
5428 if (i < length) {
5429 ASSERT(Smi::FromInt(0) == 0);
5430 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
5431 }
5432#ifdef DEBUG
5433 for (int j = 0; j < length; ++j) {
5434 Object* element = elements->get(j);
5435 ASSERT(element == Smi::FromInt(0) ||
5436 (element->IsString() && String::cast(element)->LooksValid()));
5437 }
5438#endif
5439 return i;
5440}
5441
5442
5443// Converts a String to JSArray.
5444// For example, "foo" => ["f", "o", "o"].
5445static Object* Runtime_StringToArray(Arguments args) {
5446 HandleScope scope;
5447 ASSERT(args.length() == 1);
5448 CONVERT_ARG_CHECKED(String, s, 0);
5449
5450 s->TryFlatten();
5451 const int length = s->length();
5452
5453 Handle<FixedArray> elements;
5454 if (s->IsFlat() && s->IsAsciiRepresentation()) {
5455 Object* obj = Heap::AllocateUninitializedFixedArray(length);
5456 if (obj->IsFailure()) return obj;
5457 elements = Handle<FixedArray>(FixedArray::cast(obj));
5458
5459 Vector<const char> chars = s->ToAsciiVector();
5460 // Note, this will initialize all elements (not only the prefix)
5461 // to prevent GC from seeing partially initialized array.
5462 int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
5463 *elements,
5464 length);
5465
5466 for (int i = num_copied_from_cache; i < length; ++i) {
5467 elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
5468 }
5469 } else {
5470 elements = Factory::NewFixedArray(length);
5471 for (int i = 0; i < length; ++i) {
5472 elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
5473 }
5474 }
5475
5476#ifdef DEBUG
5477 for (int i = 0; i < length; ++i) {
5478 ASSERT(String::cast(elements->get(i))->length() == 1);
5479 }
5480#endif
5481
5482 return *Factory::NewJSArrayWithElements(elements);
5483}
5484
5485
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00005486bool Runtime::IsUpperCaseChar(uint16_t ch) {
5487 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
5488 int char_length = to_upper_mapping.get(ch, 0, chars);
5489 return char_length == 0;
5490}
5491
5492
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005493static Object* Runtime_NumberToString(Arguments args) {
5494 NoHandleAllocation ha;
5495 ASSERT(args.length() == 1);
5496
5497 Object* number = args[0];
5498 RUNTIME_ASSERT(number->IsNumber());
5499
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005500 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005501}
5502
5503
ager@chromium.org357bf652010-04-12 11:30:10 +00005504static Object* Runtime_NumberToStringSkipCache(Arguments args) {
5505 NoHandleAllocation ha;
5506 ASSERT(args.length() == 1);
5507
5508 Object* number = args[0];
5509 RUNTIME_ASSERT(number->IsNumber());
5510
5511 return Heap::NumberToString(number, false);
5512}
5513
5514
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005515static Object* Runtime_NumberToInteger(Arguments args) {
5516 NoHandleAllocation ha;
5517 ASSERT(args.length() == 1);
5518
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005519 CONVERT_DOUBLE_CHECKED(number, args[0]);
5520
5521 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5522 if (number > 0 && number <= Smi::kMaxValue) {
5523 return Smi::FromInt(static_cast<int>(number));
5524 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005525 return Heap::NumberFromDouble(DoubleToInteger(number));
5526}
5527
5528
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005529static Object* Runtime_NumberToIntegerMapMinusZero(Arguments args) {
5530 NoHandleAllocation ha;
5531 ASSERT(args.length() == 1);
5532
5533 CONVERT_DOUBLE_CHECKED(number, args[0]);
5534
5535 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5536 if (number > 0 && number <= Smi::kMaxValue) {
5537 return Smi::FromInt(static_cast<int>(number));
5538 }
5539
5540 double double_value = DoubleToInteger(number);
5541 // Map both -0 and +0 to +0.
5542 if (double_value == 0) double_value = 0;
5543
5544 return Heap::NumberFromDouble(double_value);
5545}
5546
5547
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005548static Object* Runtime_NumberToJSUint32(Arguments args) {
5549 NoHandleAllocation ha;
5550 ASSERT(args.length() == 1);
5551
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005552 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005553 return Heap::NumberFromUint32(number);
5554}
5555
5556
5557static Object* Runtime_NumberToJSInt32(Arguments args) {
5558 NoHandleAllocation ha;
5559 ASSERT(args.length() == 1);
5560
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005561 CONVERT_DOUBLE_CHECKED(number, args[0]);
5562
5563 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5564 if (number > 0 && number <= Smi::kMaxValue) {
5565 return Smi::FromInt(static_cast<int>(number));
5566 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005567 return Heap::NumberFromInt32(DoubleToInt32(number));
5568}
5569
5570
ager@chromium.org870a0b62008-11-04 11:43:05 +00005571// Converts a Number to a Smi, if possible. Returns NaN if the number is not
5572// a small integer.
5573static Object* Runtime_NumberToSmi(Arguments args) {
5574 NoHandleAllocation ha;
5575 ASSERT(args.length() == 1);
5576
5577 Object* obj = args[0];
5578 if (obj->IsSmi()) {
5579 return obj;
5580 }
5581 if (obj->IsHeapNumber()) {
5582 double value = HeapNumber::cast(obj)->value();
5583 int int_value = FastD2I(value);
5584 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
5585 return Smi::FromInt(int_value);
5586 }
5587 }
5588 return Heap::nan_value();
5589}
5590
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005591
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005592static Object* Runtime_NumberAdd(Arguments args) {
5593 NoHandleAllocation ha;
5594 ASSERT(args.length() == 2);
5595
5596 CONVERT_DOUBLE_CHECKED(x, args[0]);
5597 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005598 return Heap::NumberFromDouble(x + y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005599}
5600
5601
5602static Object* Runtime_NumberSub(Arguments args) {
5603 NoHandleAllocation ha;
5604 ASSERT(args.length() == 2);
5605
5606 CONVERT_DOUBLE_CHECKED(x, args[0]);
5607 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005608 return Heap::NumberFromDouble(x - y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005609}
5610
5611
5612static Object* Runtime_NumberMul(Arguments args) {
5613 NoHandleAllocation ha;
5614 ASSERT(args.length() == 2);
5615
5616 CONVERT_DOUBLE_CHECKED(x, args[0]);
5617 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005618 return Heap::NumberFromDouble(x * y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005619}
5620
5621
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005622static Object* Runtime_NumberUnaryMinus(Arguments args) {
5623 NoHandleAllocation ha;
5624 ASSERT(args.length() == 1);
5625
5626 CONVERT_DOUBLE_CHECKED(x, args[0]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005627 return Heap::NumberFromDouble(-x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005628}
5629
5630
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00005631static Object* Runtime_NumberAlloc(Arguments args) {
5632 NoHandleAllocation ha;
5633 ASSERT(args.length() == 0);
5634
5635 return Heap::NumberFromDouble(9876543210.0);
5636}
5637
5638
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005639static Object* Runtime_NumberDiv(Arguments args) {
5640 NoHandleAllocation ha;
5641 ASSERT(args.length() == 2);
5642
5643 CONVERT_DOUBLE_CHECKED(x, args[0]);
5644 CONVERT_DOUBLE_CHECKED(y, args[1]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005645 return Heap::NumberFromDouble(x / y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005646}
5647
5648
5649static Object* Runtime_NumberMod(Arguments args) {
5650 NoHandleAllocation ha;
5651 ASSERT(args.length() == 2);
5652
5653 CONVERT_DOUBLE_CHECKED(x, args[0]);
5654 CONVERT_DOUBLE_CHECKED(y, args[1]);
5655
ager@chromium.org3811b432009-10-28 14:53:37 +00005656 x = modulo(x, y);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005657 // NumberFromDouble may return a Smi instead of a Number object
5658 return Heap::NumberFromDouble(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005659}
5660
5661
5662static Object* Runtime_StringAdd(Arguments args) {
5663 NoHandleAllocation ha;
5664 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005665 CONVERT_CHECKED(String, str1, args[0]);
5666 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005667 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00005668 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005669}
5670
5671
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005672template <typename sinkchar>
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005673static inline void StringBuilderConcatHelper(String* special,
5674 sinkchar* sink,
5675 FixedArray* fixed_array,
5676 int array_length) {
5677 int position = 0;
5678 for (int i = 0; i < array_length; i++) {
5679 Object* element = fixed_array->get(i);
5680 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005681 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005682 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005683 int pos;
5684 int len;
5685 if (encoded_slice > 0) {
5686 // Position and length encoded in one smi.
5687 pos = StringBuilderSubstringPosition::decode(encoded_slice);
5688 len = StringBuilderSubstringLength::decode(encoded_slice);
5689 } else {
5690 // Position and length encoded in two smis.
5691 Object* obj = fixed_array->get(++i);
5692 ASSERT(obj->IsSmi());
5693 pos = Smi::cast(obj)->value();
5694 len = -encoded_slice;
5695 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00005696 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00005697 sink + position,
5698 pos,
5699 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005700 position += len;
5701 } else {
5702 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005703 int element_length = string->length();
5704 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005705 position += element_length;
5706 }
5707 }
5708}
5709
5710
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005711static Object* Runtime_StringBuilderConcat(Arguments args) {
5712 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005713 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005714 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005715 if (!args[1]->IsSmi()) {
5716 Top::context()->mark_out_of_memory();
5717 return Failure::OutOfMemoryException();
5718 }
5719 int array_length = Smi::cast(args[1])->value();
5720 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005721
5722 // This assumption is used by the slice encoding in one or two smis.
5723 ASSERT(Smi::kMaxValue >= String::kMaxLength);
5724
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005725 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005726 if (!array->HasFastElements()) {
5727 return Top::Throw(Heap::illegal_argument_symbol());
5728 }
5729 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005730 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005731 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005732 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005733
5734 if (array_length == 0) {
5735 return Heap::empty_string();
5736 } else if (array_length == 1) {
5737 Object* first = fixed_array->get(0);
5738 if (first->IsString()) return first;
5739 }
5740
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005741 bool ascii = special->HasOnlyAsciiChars();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005742 int position = 0;
5743 for (int i = 0; i < array_length; i++) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005744 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005745 Object* elt = fixed_array->get(i);
5746 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005747 // Smi encoding of position and length.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005748 int smi_value = Smi::cast(elt)->value();
5749 int pos;
5750 int len;
5751 if (smi_value > 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005752 // Position and length encoded in one smi.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005753 pos = StringBuilderSubstringPosition::decode(smi_value);
5754 len = StringBuilderSubstringLength::decode(smi_value);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005755 } else {
5756 // Position and length encoded in two smis.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005757 len = -smi_value;
5758 // Get the position and check that it is a positive smi.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005759 i++;
5760 if (i >= array_length) {
5761 return Top::Throw(Heap::illegal_argument_symbol());
5762 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005763 Object* next_smi = fixed_array->get(i);
5764 if (!next_smi->IsSmi()) {
5765 return Top::Throw(Heap::illegal_argument_symbol());
5766 }
5767 pos = Smi::cast(next_smi)->value();
5768 if (pos < 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005769 return Top::Throw(Heap::illegal_argument_symbol());
5770 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005771 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005772 ASSERT(pos >= 0);
5773 ASSERT(len >= 0);
5774 if (pos > special_length || len > special_length - pos) {
5775 return Top::Throw(Heap::illegal_argument_symbol());
5776 }
5777 increment = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005778 } else if (elt->IsString()) {
5779 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005780 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005781 increment = element_length;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005782 if (ascii && !element->HasOnlyAsciiChars()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005783 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005784 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005785 } else {
5786 return Top::Throw(Heap::illegal_argument_symbol());
5787 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005788 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005789 Top::context()->mark_out_of_memory();
5790 return Failure::OutOfMemoryException();
5791 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005792 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005793 }
5794
5795 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005796 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005797
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005798 if (ascii) {
5799 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005800 if (object->IsFailure()) return object;
5801 SeqAsciiString* answer = SeqAsciiString::cast(object);
5802 StringBuilderConcatHelper(special,
5803 answer->GetChars(),
5804 fixed_array,
5805 array_length);
5806 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005807 } else {
5808 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005809 if (object->IsFailure()) return object;
5810 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
5811 StringBuilderConcatHelper(special,
5812 answer->GetChars(),
5813 fixed_array,
5814 array_length);
5815 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005816 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005817}
5818
5819
5820static Object* Runtime_NumberOr(Arguments args) {
5821 NoHandleAllocation ha;
5822 ASSERT(args.length() == 2);
5823
5824 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5825 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5826 return Heap::NumberFromInt32(x | y);
5827}
5828
5829
5830static Object* Runtime_NumberAnd(Arguments args) {
5831 NoHandleAllocation ha;
5832 ASSERT(args.length() == 2);
5833
5834 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5835 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5836 return Heap::NumberFromInt32(x & y);
5837}
5838
5839
5840static Object* Runtime_NumberXor(Arguments args) {
5841 NoHandleAllocation ha;
5842 ASSERT(args.length() == 2);
5843
5844 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5845 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5846 return Heap::NumberFromInt32(x ^ y);
5847}
5848
5849
5850static Object* Runtime_NumberNot(Arguments args) {
5851 NoHandleAllocation ha;
5852 ASSERT(args.length() == 1);
5853
5854 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5855 return Heap::NumberFromInt32(~x);
5856}
5857
5858
5859static Object* Runtime_NumberShl(Arguments args) {
5860 NoHandleAllocation ha;
5861 ASSERT(args.length() == 2);
5862
5863 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5864 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5865 return Heap::NumberFromInt32(x << (y & 0x1f));
5866}
5867
5868
5869static Object* Runtime_NumberShr(Arguments args) {
5870 NoHandleAllocation ha;
5871 ASSERT(args.length() == 2);
5872
5873 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
5874 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5875 return Heap::NumberFromUint32(x >> (y & 0x1f));
5876}
5877
5878
5879static Object* Runtime_NumberSar(Arguments args) {
5880 NoHandleAllocation ha;
5881 ASSERT(args.length() == 2);
5882
5883 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5884 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5885 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
5886}
5887
5888
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005889static Object* Runtime_NumberEquals(Arguments args) {
5890 NoHandleAllocation ha;
5891 ASSERT(args.length() == 2);
5892
5893 CONVERT_DOUBLE_CHECKED(x, args[0]);
5894 CONVERT_DOUBLE_CHECKED(y, args[1]);
5895 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
5896 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
5897 if (x == y) return Smi::FromInt(EQUAL);
5898 Object* result;
5899 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
5900 result = Smi::FromInt(EQUAL);
5901 } else {
5902 result = Smi::FromInt(NOT_EQUAL);
5903 }
5904 return result;
5905}
5906
5907
5908static Object* Runtime_StringEquals(Arguments args) {
5909 NoHandleAllocation ha;
5910 ASSERT(args.length() == 2);
5911
5912 CONVERT_CHECKED(String, x, args[0]);
5913 CONVERT_CHECKED(String, y, args[1]);
5914
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005915 bool not_equal = !x->Equals(y);
5916 // This is slightly convoluted because the value that signifies
5917 // equality is 0 and inequality is 1 so we have to negate the result
5918 // from String::Equals.
5919 ASSERT(not_equal == 0 || not_equal == 1);
5920 STATIC_CHECK(EQUAL == 0);
5921 STATIC_CHECK(NOT_EQUAL == 1);
5922 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005923}
5924
5925
5926static Object* Runtime_NumberCompare(Arguments args) {
5927 NoHandleAllocation ha;
5928 ASSERT(args.length() == 3);
5929
5930 CONVERT_DOUBLE_CHECKED(x, args[0]);
5931 CONVERT_DOUBLE_CHECKED(y, args[1]);
5932 if (isnan(x) || isnan(y)) return args[2];
5933 if (x == y) return Smi::FromInt(EQUAL);
5934 if (isless(x, y)) return Smi::FromInt(LESS);
5935 return Smi::FromInt(GREATER);
5936}
5937
5938
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005939// Compare two Smis as if they were converted to strings and then
5940// compared lexicographically.
5941static Object* Runtime_SmiLexicographicCompare(Arguments args) {
5942 NoHandleAllocation ha;
5943 ASSERT(args.length() == 2);
5944
5945 // Arrays for the individual characters of the two Smis. Smis are
5946 // 31 bit integers and 10 decimal digits are therefore enough.
5947 static int x_elms[10];
5948 static int y_elms[10];
5949
5950 // Extract the integer values from the Smis.
5951 CONVERT_CHECKED(Smi, x, args[0]);
5952 CONVERT_CHECKED(Smi, y, args[1]);
5953 int x_value = x->value();
5954 int y_value = y->value();
5955
5956 // If the integers are equal so are the string representations.
5957 if (x_value == y_value) return Smi::FromInt(EQUAL);
5958
5959 // If one of the integers are zero the normal integer order is the
5960 // same as the lexicographic order of the string representations.
5961 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
5962
ager@chromium.org32912102009-01-16 10:38:43 +00005963 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005964 // smallest because the char code of '-' is less than the char code
5965 // of any digit. Otherwise, we make both values positive.
5966 if (x_value < 0 || y_value < 0) {
5967 if (y_value >= 0) return Smi::FromInt(LESS);
5968 if (x_value >= 0) return Smi::FromInt(GREATER);
5969 x_value = -x_value;
5970 y_value = -y_value;
5971 }
5972
5973 // Convert the integers to arrays of their decimal digits.
5974 int x_index = 0;
5975 int y_index = 0;
5976 while (x_value > 0) {
5977 x_elms[x_index++] = x_value % 10;
5978 x_value /= 10;
5979 }
5980 while (y_value > 0) {
5981 y_elms[y_index++] = y_value % 10;
5982 y_value /= 10;
5983 }
5984
5985 // Loop through the arrays of decimal digits finding the first place
5986 // where they differ.
5987 while (--x_index >= 0 && --y_index >= 0) {
5988 int diff = x_elms[x_index] - y_elms[y_index];
5989 if (diff != 0) return Smi::FromInt(diff);
5990 }
5991
5992 // If one array is a suffix of the other array, the longest array is
5993 // the representation of the largest of the Smis in the
5994 // lexicographic ordering.
5995 return Smi::FromInt(x_index - y_index);
5996}
5997
5998
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005999static Object* StringInputBufferCompare(String* x, String* y) {
6000 static StringInputBuffer bufx;
6001 static StringInputBuffer bufy;
6002 bufx.Reset(x);
6003 bufy.Reset(y);
6004 while (bufx.has_more() && bufy.has_more()) {
6005 int d = bufx.GetNext() - bufy.GetNext();
6006 if (d < 0) return Smi::FromInt(LESS);
6007 else if (d > 0) return Smi::FromInt(GREATER);
6008 }
6009
6010 // x is (non-trivial) prefix of y:
6011 if (bufy.has_more()) return Smi::FromInt(LESS);
6012 // y is prefix of x:
6013 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
6014}
6015
6016
6017static Object* FlatStringCompare(String* x, String* y) {
6018 ASSERT(x->IsFlat());
6019 ASSERT(y->IsFlat());
6020 Object* equal_prefix_result = Smi::FromInt(EQUAL);
6021 int prefix_length = x->length();
6022 if (y->length() < prefix_length) {
6023 prefix_length = y->length();
6024 equal_prefix_result = Smi::FromInt(GREATER);
6025 } else if (y->length() > prefix_length) {
6026 equal_prefix_result = Smi::FromInt(LESS);
6027 }
6028 int r;
6029 if (x->IsAsciiRepresentation()) {
6030 Vector<const char> x_chars = x->ToAsciiVector();
6031 if (y->IsAsciiRepresentation()) {
6032 Vector<const char> y_chars = y->ToAsciiVector();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006033 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006034 } else {
6035 Vector<const uc16> y_chars = y->ToUC16Vector();
6036 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
6037 }
6038 } else {
6039 Vector<const uc16> x_chars = x->ToUC16Vector();
6040 if (y->IsAsciiRepresentation()) {
6041 Vector<const char> y_chars = y->ToAsciiVector();
6042 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
6043 } else {
6044 Vector<const uc16> y_chars = y->ToUC16Vector();
6045 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
6046 }
6047 }
6048 Object* result;
6049 if (r == 0) {
6050 result = equal_prefix_result;
6051 } else {
6052 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
6053 }
6054 ASSERT(result == StringInputBufferCompare(x, y));
6055 return result;
6056}
6057
6058
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006059static Object* Runtime_StringCompare(Arguments args) {
6060 NoHandleAllocation ha;
6061 ASSERT(args.length() == 2);
6062
6063 CONVERT_CHECKED(String, x, args[0]);
6064 CONVERT_CHECKED(String, y, args[1]);
6065
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006066 Counters::string_compare_runtime.Increment();
6067
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006068 // A few fast case tests before we flatten.
6069 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006070 if (y->length() == 0) {
6071 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006072 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006073 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006074 return Smi::FromInt(LESS);
6075 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006076
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006077 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006078 if (d < 0) return Smi::FromInt(LESS);
6079 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006080
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006081 Object* obj = Heap::PrepareForCompare(x);
6082 if (obj->IsFailure()) return obj;
6083 obj = Heap::PrepareForCompare(y);
6084 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006085
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006086 return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
6087 : StringInputBufferCompare(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006088}
6089
6090
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006091static Object* Runtime_Math_acos(Arguments args) {
6092 NoHandleAllocation ha;
6093 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006094 Counters::math_acos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006095
6096 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006097 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006098}
6099
6100
6101static Object* Runtime_Math_asin(Arguments args) {
6102 NoHandleAllocation ha;
6103 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006104 Counters::math_asin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006105
6106 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006107 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006108}
6109
6110
6111static Object* Runtime_Math_atan(Arguments args) {
6112 NoHandleAllocation ha;
6113 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006114 Counters::math_atan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006115
6116 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006117 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006118}
6119
6120
6121static Object* Runtime_Math_atan2(Arguments args) {
6122 NoHandleAllocation ha;
6123 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006124 Counters::math_atan2.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006125
6126 CONVERT_DOUBLE_CHECKED(x, args[0]);
6127 CONVERT_DOUBLE_CHECKED(y, args[1]);
6128 double result;
6129 if (isinf(x) && isinf(y)) {
6130 // Make sure that the result in case of two infinite arguments
6131 // is a multiple of Pi / 4. The sign of the result is determined
6132 // by the first argument (x) and the sign of the second argument
6133 // determines the multiplier: one or three.
6134 static double kPiDividedBy4 = 0.78539816339744830962;
6135 int multiplier = (x < 0) ? -1 : 1;
6136 if (y < 0) multiplier *= 3;
6137 result = multiplier * kPiDividedBy4;
6138 } else {
6139 result = atan2(x, y);
6140 }
6141 return Heap::AllocateHeapNumber(result);
6142}
6143
6144
6145static Object* Runtime_Math_ceil(Arguments args) {
6146 NoHandleAllocation ha;
6147 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006148 Counters::math_ceil.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006149
6150 CONVERT_DOUBLE_CHECKED(x, args[0]);
6151 return Heap::NumberFromDouble(ceiling(x));
6152}
6153
6154
6155static Object* Runtime_Math_cos(Arguments args) {
6156 NoHandleAllocation ha;
6157 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006158 Counters::math_cos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006159
6160 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006161 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006162}
6163
6164
6165static Object* Runtime_Math_exp(Arguments args) {
6166 NoHandleAllocation ha;
6167 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006168 Counters::math_exp.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006169
6170 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006171 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006172}
6173
6174
6175static Object* Runtime_Math_floor(Arguments args) {
6176 NoHandleAllocation ha;
6177 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006178 Counters::math_floor.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006179
6180 CONVERT_DOUBLE_CHECKED(x, args[0]);
6181 return Heap::NumberFromDouble(floor(x));
6182}
6183
6184
6185static Object* Runtime_Math_log(Arguments args) {
6186 NoHandleAllocation ha;
6187 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006188 Counters::math_log.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006189
6190 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006191 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006192}
6193
6194
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006195// Helper function to compute x^y, where y is known to be an
6196// integer. Uses binary decomposition to limit the number of
6197// multiplications; see the discussion in "Hacker's Delight" by Henry
6198// S. Warren, Jr., figure 11-6, page 213.
6199static double powi(double x, int y) {
6200 ASSERT(y != kMinInt);
6201 unsigned n = (y < 0) ? -y : y;
6202 double m = x;
6203 double p = 1;
6204 while (true) {
6205 if ((n & 1) != 0) p *= m;
6206 n >>= 1;
6207 if (n == 0) {
6208 if (y < 0) {
6209 // Unfortunately, we have to be careful when p has reached
6210 // infinity in the computation, because sometimes the higher
6211 // internal precision in the pow() implementation would have
6212 // given us a finite p. This happens very rarely.
6213 double result = 1.0 / p;
6214 return (result == 0 && isinf(p))
6215 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
6216 : result;
6217 } else {
6218 return p;
6219 }
6220 }
6221 m *= m;
6222 }
6223}
6224
6225
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006226static Object* Runtime_Math_pow(Arguments args) {
6227 NoHandleAllocation ha;
6228 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006229 Counters::math_pow.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006230
6231 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006232
6233 // If the second argument is a smi, it is much faster to call the
6234 // custom powi() function than the generic pow().
6235 if (args[1]->IsSmi()) {
6236 int y = Smi::cast(args[1])->value();
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00006237 return Heap::NumberFromDouble(powi(x, y));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006238 }
6239
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006240 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00006241
6242 if (!isinf(x)) {
6243 if (y == 0.5) {
6244 // It's not uncommon to use Math.pow(x, 0.5) to compute the
6245 // square root of a number. To speed up such computations, we
6246 // explictly check for this case and use the sqrt() function
6247 // which is faster than pow().
6248 return Heap::AllocateHeapNumber(sqrt(x));
6249 } else if (y == -0.5) {
6250 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
6251 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
6252 }
6253 }
6254
6255 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006256 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006257 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6258 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006259 } else {
6260 return Heap::AllocateHeapNumber(pow(x, y));
6261 }
6262}
6263
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006264// Fast version of Math.pow if we know that y is not an integer and
6265// y is not -0.5 or 0.5. Used as slowcase from codegen.
6266static Object* Runtime_Math_pow_cfunction(Arguments args) {
6267 NoHandleAllocation ha;
6268 ASSERT(args.length() == 2);
6269 CONVERT_DOUBLE_CHECKED(x, args[0]);
6270 CONVERT_DOUBLE_CHECKED(y, args[1]);
6271 if (y == 0) {
6272 return Smi::FromInt(1);
6273 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6274 return Heap::nan_value();
6275 } else {
6276 return Heap::AllocateHeapNumber(pow(x, y));
6277 }
6278}
6279
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006280
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006281static Object* Runtime_RoundNumber(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006282 NoHandleAllocation ha;
6283 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006284 Counters::math_round.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006285
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006286 if (!args[0]->IsHeapNumber()) {
6287 // Must be smi. Return the argument unchanged for all the other types
6288 // to make fuzz-natives test happy.
6289 return args[0];
6290 }
6291
6292 HeapNumber* number = reinterpret_cast<HeapNumber*>(args[0]);
6293
6294 double value = number->value();
6295 int exponent = number->get_exponent();
6296 int sign = number->get_sign();
6297
6298 // We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
6299 // should be rounded to 2^30, which is not smi.
6300 if (!sign && exponent <= kSmiValueSize - 3) {
6301 return Smi::FromInt(static_cast<int>(value + 0.5));
6302 }
6303
6304 // If the magnitude is big enough, there's no place for fraction part. If we
6305 // try to add 0.5 to this number, 1.0 will be added instead.
6306 if (exponent >= 52) {
6307 return number;
6308 }
6309
6310 if (sign && value >= -0.5) return Heap::minus_zero_value();
6311
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006312 // Do not call NumberFromDouble() to avoid extra checks.
6313 return Heap::AllocateHeapNumber(floor(value + 0.5));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006314}
6315
6316
6317static Object* Runtime_Math_sin(Arguments args) {
6318 NoHandleAllocation ha;
6319 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006320 Counters::math_sin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006321
6322 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006323 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006324}
6325
6326
6327static Object* Runtime_Math_sqrt(Arguments args) {
6328 NoHandleAllocation ha;
6329 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006330 Counters::math_sqrt.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006331
6332 CONVERT_DOUBLE_CHECKED(x, args[0]);
6333 return Heap::AllocateHeapNumber(sqrt(x));
6334}
6335
6336
6337static Object* Runtime_Math_tan(Arguments args) {
6338 NoHandleAllocation ha;
6339 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006340 Counters::math_tan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006341
6342 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006343 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006344}
6345
6346
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006347static int MakeDay(int year, int month, int day) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006348 static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
6349 181, 212, 243, 273, 304, 334};
6350 static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
6351 182, 213, 244, 274, 305, 335};
6352
6353 year += month / 12;
6354 month %= 12;
6355 if (month < 0) {
6356 year--;
6357 month += 12;
6358 }
6359
6360 ASSERT(month >= 0);
6361 ASSERT(month < 12);
6362
6363 // year_delta is an arbitrary number such that:
6364 // a) year_delta = -1 (mod 400)
6365 // b) year + year_delta > 0 for years in the range defined by
6366 // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
6367 // Jan 1 1970. This is required so that we don't run into integer
6368 // division of negative numbers.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006369 // c) there shouldn't be an overflow for 32-bit integers in the following
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006370 // operations.
6371 static const int year_delta = 399999;
6372 static const int base_day = 365 * (1970 + year_delta) +
6373 (1970 + year_delta) / 4 -
6374 (1970 + year_delta) / 100 +
6375 (1970 + year_delta) / 400;
6376
6377 int year1 = year + year_delta;
6378 int day_from_year = 365 * year1 +
6379 year1 / 4 -
6380 year1 / 100 +
6381 year1 / 400 -
6382 base_day;
6383
6384 if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006385 return day_from_year + day_from_month[month] + day - 1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006386 }
6387
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006388 return day_from_year + day_from_month_leap[month] + day - 1;
6389}
6390
6391
6392static Object* Runtime_DateMakeDay(Arguments args) {
6393 NoHandleAllocation ha;
6394 ASSERT(args.length() == 3);
6395
6396 CONVERT_SMI_CHECKED(year, args[0]);
6397 CONVERT_SMI_CHECKED(month, args[1]);
6398 CONVERT_SMI_CHECKED(date, args[2]);
6399
6400 return Smi::FromInt(MakeDay(year, month, date));
6401}
6402
6403
6404static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
6405static const int kDaysIn4Years = 4 * 365 + 1;
6406static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
6407static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
6408static const int kDays1970to2000 = 30 * 365 + 7;
6409static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
6410 kDays1970to2000;
6411static const int kYearsOffset = 400000;
6412
6413static const char kDayInYear[] = {
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 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6417 22, 23, 24, 25, 26, 27, 28,
6418 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6419 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6420 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6421 22, 23, 24, 25, 26, 27, 28, 29, 30,
6422 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6423 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6424 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6425 22, 23, 24, 25, 26, 27, 28, 29, 30,
6426 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6427 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6428 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6429 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6430 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6431 22, 23, 24, 25, 26, 27, 28, 29, 30,
6432 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6433 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6434 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6435 22, 23, 24, 25, 26, 27, 28, 29, 30,
6436 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6437 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6438
6439 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6440 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6441 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6442 22, 23, 24, 25, 26, 27, 28,
6443 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6444 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6445 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6446 22, 23, 24, 25, 26, 27, 28, 29, 30,
6447 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6448 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6449 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6450 22, 23, 24, 25, 26, 27, 28, 29, 30,
6451 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6452 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6453 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6454 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6455 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6456 22, 23, 24, 25, 26, 27, 28, 29, 30,
6457 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6458 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6459 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6460 22, 23, 24, 25, 26, 27, 28, 29, 30,
6461 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6462 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6463
6464 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6465 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6466 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6467 22, 23, 24, 25, 26, 27, 28, 29,
6468 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6469 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6470 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6471 22, 23, 24, 25, 26, 27, 28, 29, 30,
6472 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6473 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6474 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6475 22, 23, 24, 25, 26, 27, 28, 29, 30,
6476 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6477 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6478 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6479 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6480 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6481 22, 23, 24, 25, 26, 27, 28, 29, 30,
6482 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6483 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6484 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6485 22, 23, 24, 25, 26, 27, 28, 29, 30,
6486 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6487 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6488
6489 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6490 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6491 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6492 22, 23, 24, 25, 26, 27, 28,
6493 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6494 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6495 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6496 22, 23, 24, 25, 26, 27, 28, 29, 30,
6497 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6498 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6499 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6500 22, 23, 24, 25, 26, 27, 28, 29, 30,
6501 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6502 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6503 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6504 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6505 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6506 22, 23, 24, 25, 26, 27, 28, 29, 30,
6507 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6508 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6509 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6510 22, 23, 24, 25, 26, 27, 28, 29, 30,
6511 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6512 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
6513
6514static const char kMonthInYear[] = {
6515 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,
6516 0, 0, 0, 0, 0, 0,
6517 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,
6518 1, 1, 1,
6519 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,
6520 2, 2, 2, 2, 2, 2,
6521 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,
6522 3, 3, 3, 3, 3,
6523 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,
6524 4, 4, 4, 4, 4, 4,
6525 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,
6526 5, 5, 5, 5, 5,
6527 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,
6528 6, 6, 6, 6, 6, 6,
6529 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,
6530 7, 7, 7, 7, 7, 7,
6531 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,
6532 8, 8, 8, 8, 8,
6533 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,
6534 9, 9, 9, 9, 9, 9,
6535 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6536 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6537 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6538 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6539
6540 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
6541 0, 0, 0, 0, 0, 0,
6542 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
6543 1, 1, 1,
6544 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
6545 2, 2, 2, 2, 2, 2,
6546 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
6547 3, 3, 3, 3, 3,
6548 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
6549 4, 4, 4, 4, 4, 4,
6550 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6551 5, 5, 5, 5, 5,
6552 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6553 6, 6, 6, 6, 6, 6,
6554 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
6555 7, 7, 7, 7, 7, 7,
6556 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
6557 8, 8, 8, 8, 8,
6558 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
6559 9, 9, 9, 9, 9, 9,
6560 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6561 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6562 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6563 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6564
6565 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
6566 0, 0, 0, 0, 0, 0,
6567 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
6568 1, 1, 1, 1,
6569 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
6570 2, 2, 2, 2, 2, 2,
6571 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
6572 3, 3, 3, 3, 3,
6573 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
6574 4, 4, 4, 4, 4, 4,
6575 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6576 5, 5, 5, 5, 5,
6577 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6578 6, 6, 6, 6, 6, 6,
6579 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
6580 7, 7, 7, 7, 7, 7,
6581 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
6582 8, 8, 8, 8, 8,
6583 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
6584 9, 9, 9, 9, 9, 9,
6585 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6586 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6587 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6588 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6589
6590 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
6591 0, 0, 0, 0, 0, 0,
6592 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
6593 1, 1, 1,
6594 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
6595 2, 2, 2, 2, 2, 2,
6596 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
6597 3, 3, 3, 3, 3,
6598 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
6599 4, 4, 4, 4, 4, 4,
6600 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6601 5, 5, 5, 5, 5,
6602 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6603 6, 6, 6, 6, 6, 6,
6604 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
6605 7, 7, 7, 7, 7, 7,
6606 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
6607 8, 8, 8, 8, 8,
6608 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
6609 9, 9, 9, 9, 9, 9,
6610 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6611 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6612 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6613 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
6614
6615
6616// This function works for dates from 1970 to 2099.
6617static inline void DateYMDFromTimeAfter1970(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006618 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006619#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006620 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006621#endif
6622
6623 year = 1970 + (4 * date + 2) / kDaysIn4Years;
6624 date %= kDaysIn4Years;
6625
6626 month = kMonthInYear[date];
6627 day = kDayInYear[date];
6628
6629 ASSERT(MakeDay(year, month, day) == save_date);
6630}
6631
6632
6633static inline void DateYMDFromTimeSlow(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006634 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006635#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006636 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006637#endif
6638
6639 date += kDaysOffset;
6640 year = 400 * (date / kDaysIn400Years) - kYearsOffset;
6641 date %= kDaysIn400Years;
6642
6643 ASSERT(MakeDay(year, 0, 1) + date == save_date);
6644
6645 date--;
6646 int yd1 = date / kDaysIn100Years;
6647 date %= kDaysIn100Years;
6648 year += 100 * yd1;
6649
6650 date++;
6651 int yd2 = date / kDaysIn4Years;
6652 date %= kDaysIn4Years;
6653 year += 4 * yd2;
6654
6655 date--;
6656 int yd3 = date / 365;
6657 date %= 365;
6658 year += yd3;
6659
6660 bool is_leap = (!yd1 || yd2) && !yd3;
6661
6662 ASSERT(date >= -1);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006663 ASSERT(is_leap || (date >= 0));
6664 ASSERT((date < 365) || (is_leap && (date < 366)));
6665 ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0))));
6666 ASSERT(is_leap || ((MakeDay(year, 0, 1) + date) == save_date));
6667 ASSERT(!is_leap || ((MakeDay(year, 0, 1) + date + 1) == save_date));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006668
6669 if (is_leap) {
6670 day = kDayInYear[2*365 + 1 + date];
6671 month = kMonthInYear[2*365 + 1 + date];
6672 } else {
6673 day = kDayInYear[date];
6674 month = kMonthInYear[date];
6675 }
6676
6677 ASSERT(MakeDay(year, month, day) == save_date);
6678}
6679
6680
6681static inline void DateYMDFromTime(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006682 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006683 if (date >= 0 && date < 32 * kDaysIn4Years) {
6684 DateYMDFromTimeAfter1970(date, year, month, day);
6685 } else {
6686 DateYMDFromTimeSlow(date, year, month, day);
6687 }
6688}
6689
6690
6691static Object* Runtime_DateYMDFromTime(Arguments args) {
6692 NoHandleAllocation ha;
6693 ASSERT(args.length() == 2);
6694
6695 CONVERT_DOUBLE_CHECKED(t, args[0]);
6696 CONVERT_CHECKED(JSArray, res_array, args[1]);
6697
6698 int year, month, day;
6699 DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
6700
6701 res_array->SetElement(0, Smi::FromInt(year));
6702 res_array->SetElement(1, Smi::FromInt(month));
6703 res_array->SetElement(2, Smi::FromInt(day));
6704
6705 return Heap::undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006706}
6707
6708
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006709static Object* Runtime_NewArgumentsFast(Arguments args) {
6710 NoHandleAllocation ha;
6711 ASSERT(args.length() == 3);
6712
6713 JSFunction* callee = JSFunction::cast(args[0]);
6714 Object** parameters = reinterpret_cast<Object**>(args[1]);
6715 const int length = Smi::cast(args[2])->value();
6716
6717 Object* result = Heap::AllocateArgumentsObject(callee, length);
6718 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006719 // Allocate the elements if needed.
6720 if (length > 0) {
6721 // Allocate the fixed array.
6722 Object* obj = Heap::AllocateRawFixedArray(length);
6723 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006724
6725 AssertNoAllocation no_gc;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006726 FixedArray* array = reinterpret_cast<FixedArray*>(obj);
6727 array->set_map(Heap::fixed_array_map());
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006728 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006729
6730 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006731 for (int i = 0; i < length; i++) {
6732 array->set(i, *--parameters, mode);
6733 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006734 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006735 }
6736 return result;
6737}
6738
6739
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006740static Object* Runtime_NewClosure(Arguments args) {
6741 HandleScope scope;
6742 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00006743 CONVERT_ARG_CHECKED(Context, context, 0);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006744 CONVERT_ARG_CHECKED(SharedFunctionInfo, shared, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006745
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00006746 PretenureFlag pretenure = (context->global_context() == *context)
6747 ? TENURED // Allocate global closures in old space.
6748 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006749 Handle<JSFunction> result =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006750 Factory::NewFunctionFromSharedFunctionInfo(shared, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006751 return *result;
6752}
6753
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006754static Object* Runtime_NewObjectFromBound(Arguments args) {
6755 HandleScope scope;
6756 ASSERT(args.length() == 2);
6757 CONVERT_ARG_CHECKED(JSFunction, function, 0);
6758 CONVERT_ARG_CHECKED(JSArray, params, 1);
6759
6760 FixedArray* fixed = FixedArray::cast(params->elements());
6761
6762 bool exception = false;
6763 Object*** param_data = NewArray<Object**>(fixed->length());
6764 for (int i = 0; i < fixed->length(); i++) {
6765 Handle<Object> val = Handle<Object>(fixed->get(i));
6766 param_data[i] = val.location();
6767 }
6768
6769 Handle<Object> result = Execution::New(
6770 function, fixed->length(), param_data, &exception);
6771 return *result;
6772}
6773
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006774
ager@chromium.org5c838252010-02-19 08:53:10 +00006775static Code* ComputeConstructStub(Handle<JSFunction> function) {
6776 Handle<Object> prototype = Factory::null_value();
6777 if (function->has_instance_prototype()) {
6778 prototype = Handle<Object>(function->instance_prototype());
6779 }
6780 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006781 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00006782 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006783 if (code->IsFailure()) {
6784 return Builtins::builtin(Builtins::JSConstructStubGeneric);
6785 }
6786 return Code::cast(code);
6787 }
6788
ager@chromium.org5c838252010-02-19 08:53:10 +00006789 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006790}
6791
6792
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006793static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006794 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006795 ASSERT(args.length() == 1);
6796
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006797 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006798
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006799 // If the constructor isn't a proper function we throw a type error.
6800 if (!constructor->IsJSFunction()) {
6801 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6802 Handle<Object> type_error =
6803 Factory::NewTypeError("not_constructor", arguments);
6804 return Top::Throw(*type_error);
6805 }
6806
6807 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006808
6809 // If function should not have prototype, construction is not allowed. In this
6810 // case generated code bailouts here, since function has no initial_map.
6811 if (!function->should_have_prototype()) {
6812 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6813 Handle<Object> type_error =
6814 Factory::NewTypeError("not_constructor", arguments);
6815 return Top::Throw(*type_error);
6816 }
6817
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006818#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006819 // Handle stepping into constructors if step into is active.
6820 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006821 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006822 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006823#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006824
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006825 if (function->has_initial_map()) {
6826 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006827 // The 'Function' function ignores the receiver object when
6828 // called using 'new' and creates a new JSFunction object that
6829 // is returned. The receiver object is only used for error
6830 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006831 // JSFunction. Factory::NewJSObject() should not be used to
6832 // allocate JSFunctions since it does not properly initialize
6833 // the shared part of the function. Since the receiver is
6834 // ignored anyway, we use the global object as the receiver
6835 // instead of a new JSFunction object. This way, errors are
6836 // reported the same way whether or not 'Function' is called
6837 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006838 return Top::context()->global();
6839 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006840 }
6841
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006842 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006843 Handle<SharedFunctionInfo> shared(function->shared());
6844 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006845
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006846 bool first_allocation = !function->has_initial_map();
6847 Handle<JSObject> result = Factory::NewJSObject(function);
6848 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006849 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00006850 ComputeConstructStub(Handle<JSFunction>(function)));
6851 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006852 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006853
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006854 Counters::constructed_objects.Increment();
6855 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006856
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006857 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006858}
6859
6860
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006861static Object* Runtime_LazyCompile(Arguments args) {
6862 HandleScope scope;
6863 ASSERT(args.length() == 1);
6864
6865 Handle<JSFunction> function = args.at<JSFunction>(0);
6866#ifdef DEBUG
6867 if (FLAG_trace_lazy) {
6868 PrintF("[lazy: ");
6869 function->shared()->name()->Print();
6870 PrintF("]\n");
6871 }
6872#endif
6873
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006874 // Compile the target function. Here we compile using CompileLazyInLoop in
6875 // order to get the optimized version. This helps code like delta-blue
6876 // that calls performance-critical routines through constructors. A
6877 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
6878 // direct call. Since the in-loop tracking takes place through CallICs
6879 // this means that things called through constructors are never known to
6880 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006881 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006882 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006883 return Failure::Exception();
6884 }
6885
6886 return function->code();
6887}
6888
6889
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006890static Object* Runtime_GetFunctionDelegate(Arguments args) {
6891 HandleScope scope;
6892 ASSERT(args.length() == 1);
6893 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6894 return *Execution::GetFunctionDelegate(args.at<Object>(0));
6895}
6896
6897
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00006898static Object* Runtime_GetConstructorDelegate(Arguments args) {
6899 HandleScope scope;
6900 ASSERT(args.length() == 1);
6901 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6902 return *Execution::GetConstructorDelegate(args.at<Object>(0));
6903}
6904
6905
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006906static Object* Runtime_NewContext(Arguments args) {
6907 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00006908 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006909
kasper.lund7276f142008-07-30 08:49:36 +00006910 CONVERT_CHECKED(JSFunction, function, args[0]);
ager@chromium.orgb5737492010-07-15 09:29:43 +00006911 int length = function->shared()->scope_info()->NumberOfContextSlots();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006912 Object* result = Heap::AllocateFunctionContext(length, function);
6913 if (result->IsFailure()) return result;
6914
6915 Top::set_context(Context::cast(result));
6916
kasper.lund7276f142008-07-30 08:49:36 +00006917 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006918}
6919
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006920static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006921 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006922 Object* js_object = object;
6923 if (!js_object->IsJSObject()) {
6924 js_object = js_object->ToObject();
6925 if (js_object->IsFailure()) {
6926 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006927 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006928 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006929 Handle<Object> result =
6930 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
6931 return Top::Throw(*result);
6932 }
6933 }
6934
6935 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006936 Heap::AllocateWithContext(Top::context(),
6937 JSObject::cast(js_object),
6938 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006939 if (result->IsFailure()) return result;
6940
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006941 Context* context = Context::cast(result);
6942 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006943
kasper.lund7276f142008-07-30 08:49:36 +00006944 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006945}
6946
6947
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006948static Object* Runtime_PushContext(Arguments args) {
6949 NoHandleAllocation ha;
6950 ASSERT(args.length() == 1);
6951 return PushContextHelper(args[0], false);
6952}
6953
6954
6955static Object* Runtime_PushCatchContext(Arguments args) {
6956 NoHandleAllocation ha;
6957 ASSERT(args.length() == 1);
6958 return PushContextHelper(args[0], true);
6959}
6960
6961
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006962static Object* Runtime_LookupContext(Arguments args) {
6963 HandleScope scope;
6964 ASSERT(args.length() == 2);
6965
6966 CONVERT_ARG_CHECKED(Context, context, 0);
6967 CONVERT_ARG_CHECKED(String, name, 1);
6968
6969 int index;
6970 PropertyAttributes attributes;
6971 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006972 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006973 context->Lookup(name, flags, &index, &attributes);
6974
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006975 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006976 ASSERT(holder->IsJSObject());
6977 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006978 }
6979
6980 // No intermediate context found. Use global object by default.
6981 return Top::context()->global();
6982}
6983
6984
ager@chromium.orga1645e22009-09-09 19:27:10 +00006985// A mechanism to return a pair of Object pointers in registers (if possible).
6986// How this is achieved is calling convention-dependent.
6987// All currently supported x86 compiles uses calling conventions that are cdecl
6988// variants where a 64-bit value is returned in two 32-bit registers
6989// (edx:eax on ia32, r1:r0 on ARM).
6990// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
6991// In Win64 calling convention, a struct of two pointers is returned in memory,
6992// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006993#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006994struct ObjectPair {
6995 Object* x;
6996 Object* y;
6997};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006998
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006999static inline ObjectPair MakePair(Object* x, Object* y) {
7000 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00007001 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
7002 // In Win64 they are assigned to a hidden first argument.
7003 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007004}
7005#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007006typedef uint64_t ObjectPair;
7007static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007008 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007009 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007010}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007011#endif
7012
7013
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007014static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007015 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
7016 USE(attributes);
7017 return x->IsTheHole() ? Heap::undefined_value() : x;
7018}
7019
7020
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007021static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
7022 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007023 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007024 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007025 JSFunction* context_extension_function =
7026 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007027 // If the holder isn't a context extension object, we just return it
7028 // as the receiver. This allows arguments objects to be used as
7029 // receivers, but only if they are put in the context scope chain
7030 // explicitly via a with-statement.
7031 Object* constructor = holder->map()->constructor();
7032 if (constructor != context_extension_function) return holder;
7033 // Fall back to using the global object as the receiver if the
7034 // property turns out to be a local variable allocated in a context
7035 // extension object - introduced via eval.
7036 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007037}
7038
7039
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007040static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007041 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00007042 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007043
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007044 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00007045 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007046 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007047 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007048 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007049
7050 int index;
7051 PropertyAttributes attributes;
7052 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007053 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007054 context->Lookup(name, flags, &index, &attributes);
7055
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007056 // If the index is non-negative, the slot has been found in a local
7057 // variable or a parameter. Read it from the context object or the
7058 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007059 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007060 // If the "property" we were looking for is a local variable or an
7061 // argument in a context, the receiver is the global object; see
7062 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
7063 JSObject* receiver = Top::context()->global()->global_receiver();
7064 Object* value = (holder->IsContext())
7065 ? Context::cast(*holder)->get(index)
7066 : JSObject::cast(*holder)->GetElement(index);
7067 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007068 }
7069
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007070 // If the holder is found, we read the property from it.
7071 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007072 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007073 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007074 JSObject* receiver;
7075 if (object->IsGlobalObject()) {
7076 receiver = GlobalObject::cast(object)->global_receiver();
7077 } else if (context->is_exception_holder(*holder)) {
7078 receiver = Top::context()->global()->global_receiver();
7079 } else {
7080 receiver = ComputeReceiverForNonGlobal(object);
7081 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007082 // No need to unhole the value here. This is taken care of by the
7083 // GetProperty function.
7084 Object* value = object->GetProperty(*name);
7085 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007086 }
7087
7088 if (throw_error) {
7089 // The property doesn't exist - throw exception.
7090 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007091 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007092 return MakePair(Top::Throw(*reference_error), NULL);
7093 } else {
7094 // The property doesn't exist - return undefined
7095 return MakePair(Heap::undefined_value(), Heap::undefined_value());
7096 }
7097}
7098
7099
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007100static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007101 return LoadContextSlotHelper(args, true);
7102}
7103
7104
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007105static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007106 return LoadContextSlotHelper(args, false);
7107}
7108
7109
7110static Object* Runtime_StoreContextSlot(Arguments args) {
7111 HandleScope scope;
7112 ASSERT(args.length() == 3);
7113
7114 Handle<Object> value(args[0]);
7115 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007116 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007117
7118 int index;
7119 PropertyAttributes attributes;
7120 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007121 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007122 context->Lookup(name, flags, &index, &attributes);
7123
7124 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007125 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007126 // Ignore if read_only variable.
7127 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007128 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007129 }
7130 } else {
7131 ASSERT((attributes & READ_ONLY) == 0);
7132 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007133 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007134 USE(result);
7135 ASSERT(!result->IsFailure());
7136 }
7137 return *value;
7138 }
7139
7140 // Slow case: The property is not in a FixedArray context.
7141 // It is either in an JSObject extension context or it was not found.
7142 Handle<JSObject> context_ext;
7143
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007144 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007145 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007146 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007147 } else {
7148 // The property was not found. It needs to be stored in the global context.
7149 ASSERT(attributes == ABSENT);
7150 attributes = NONE;
7151 context_ext = Handle<JSObject>(Top::context()->global());
7152 }
7153
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007154 // Set the property, but ignore if read_only variable on the context
7155 // extension object itself.
7156 if ((attributes & READ_ONLY) == 0 ||
7157 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007158 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
7159 if (set.is_null()) {
7160 // Failure::Exception is converted to a null handle in the
7161 // handle-based methods such as SetProperty. We therefore need
7162 // to convert null handles back to exceptions.
7163 ASSERT(Top::has_pending_exception());
7164 return Failure::Exception();
7165 }
7166 }
7167 return *value;
7168}
7169
7170
7171static Object* Runtime_Throw(Arguments args) {
7172 HandleScope scope;
7173 ASSERT(args.length() == 1);
7174
7175 return Top::Throw(args[0]);
7176}
7177
7178
7179static Object* Runtime_ReThrow(Arguments args) {
7180 HandleScope scope;
7181 ASSERT(args.length() == 1);
7182
7183 return Top::ReThrow(args[0]);
7184}
7185
7186
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007187static Object* Runtime_PromoteScheduledException(Arguments args) {
7188 ASSERT_EQ(0, args.length());
7189 return Top::PromoteScheduledException();
7190}
7191
7192
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007193static Object* Runtime_ThrowReferenceError(Arguments args) {
7194 HandleScope scope;
7195 ASSERT(args.length() == 1);
7196
7197 Handle<Object> name(args[0]);
7198 Handle<Object> reference_error =
7199 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
7200 return Top::Throw(*reference_error);
7201}
7202
7203
7204static Object* Runtime_StackOverflow(Arguments args) {
7205 NoHandleAllocation na;
7206 return Top::StackOverflow();
7207}
7208
7209
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007210static Object* Runtime_StackGuard(Arguments args) {
7211 ASSERT(args.length() == 1);
7212
7213 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007214 if (StackGuard::IsStackOverflow()) {
7215 return Runtime_StackOverflow(args);
7216 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007217
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007218 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007219}
7220
7221
7222// NOTE: These PrintXXX functions are defined for all builds (not just
7223// DEBUG builds) because we may want to be able to trace function
7224// calls in all modes.
7225static void PrintString(String* str) {
7226 // not uncommon to have empty strings
7227 if (str->length() > 0) {
7228 SmartPointer<char> s =
7229 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
7230 PrintF("%s", *s);
7231 }
7232}
7233
7234
7235static void PrintObject(Object* obj) {
7236 if (obj->IsSmi()) {
7237 PrintF("%d", Smi::cast(obj)->value());
7238 } else if (obj->IsString() || obj->IsSymbol()) {
7239 PrintString(String::cast(obj));
7240 } else if (obj->IsNumber()) {
7241 PrintF("%g", obj->Number());
7242 } else if (obj->IsFailure()) {
7243 PrintF("<failure>");
7244 } else if (obj->IsUndefined()) {
7245 PrintF("<undefined>");
7246 } else if (obj->IsNull()) {
7247 PrintF("<null>");
7248 } else if (obj->IsTrue()) {
7249 PrintF("<true>");
7250 } else if (obj->IsFalse()) {
7251 PrintF("<false>");
7252 } else {
7253 PrintF("%p", obj);
7254 }
7255}
7256
7257
7258static int StackSize() {
7259 int n = 0;
7260 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
7261 return n;
7262}
7263
7264
7265static void PrintTransition(Object* result) {
7266 // indentation
7267 { const int nmax = 80;
7268 int n = StackSize();
7269 if (n <= nmax)
7270 PrintF("%4d:%*s", n, n, "");
7271 else
7272 PrintF("%4d:%*s", n, nmax, "...");
7273 }
7274
7275 if (result == NULL) {
7276 // constructor calls
7277 JavaScriptFrameIterator it;
7278 JavaScriptFrame* frame = it.frame();
7279 if (frame->IsConstructor()) PrintF("new ");
7280 // function name
7281 Object* fun = frame->function();
7282 if (fun->IsJSFunction()) {
7283 PrintObject(JSFunction::cast(fun)->shared()->name());
7284 } else {
7285 PrintObject(fun);
7286 }
7287 // function arguments
7288 // (we are intentionally only printing the actually
7289 // supplied parameters, not all parameters required)
7290 PrintF("(this=");
7291 PrintObject(frame->receiver());
7292 const int length = frame->GetProvidedParametersCount();
7293 for (int i = 0; i < length; i++) {
7294 PrintF(", ");
7295 PrintObject(frame->GetParameter(i));
7296 }
7297 PrintF(") {\n");
7298
7299 } else {
7300 // function result
7301 PrintF("} -> ");
7302 PrintObject(result);
7303 PrintF("\n");
7304 }
7305}
7306
7307
7308static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007309 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007310 NoHandleAllocation ha;
7311 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007312 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007313}
7314
7315
7316static Object* Runtime_TraceExit(Arguments args) {
7317 NoHandleAllocation ha;
7318 PrintTransition(args[0]);
7319 return args[0]; // return TOS
7320}
7321
7322
7323static Object* Runtime_DebugPrint(Arguments args) {
7324 NoHandleAllocation ha;
7325 ASSERT(args.length() == 1);
7326
7327#ifdef DEBUG
7328 if (args[0]->IsString()) {
7329 // If we have a string, assume it's a code "marker"
7330 // and print some interesting cpu debugging info.
7331 JavaScriptFrameIterator it;
7332 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007333 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
7334 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007335 } else {
7336 PrintF("DebugPrint: ");
7337 }
7338 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007339 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007340 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007341 HeapObject::cast(args[0])->map()->Print();
7342 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007343#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007344 // ShortPrint is available in release mode. Print is not.
7345 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007346#endif
7347 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00007348 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007349
7350 return args[0]; // return TOS
7351}
7352
7353
7354static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007355 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007356 NoHandleAllocation ha;
7357 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007358 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007359}
7360
7361
mads.s.ager31e71382008-08-13 09:32:07 +00007362static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007363 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007364 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007365
7366 // According to ECMA-262, section 15.9.1, page 117, the precision of
7367 // the number in a Date object representing a particular instant in
7368 // time is milliseconds. Therefore, we floor the result of getting
7369 // the OS time.
7370 double millis = floor(OS::TimeCurrentMillis());
7371 return Heap::NumberFromDouble(millis);
7372}
7373
7374
7375static Object* Runtime_DateParseString(Arguments args) {
7376 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007377 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007378
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007379 CONVERT_ARG_CHECKED(String, str, 0);
7380 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007381
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007382 CONVERT_ARG_CHECKED(JSArray, output, 1);
7383 RUNTIME_ASSERT(output->HasFastElements());
7384
7385 AssertNoAllocation no_allocation;
7386
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007387 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007388 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
7389 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007390 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007391 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007392 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007393 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007394 result = DateParser::Parse(str->ToUC16Vector(), output_array);
7395 }
7396
7397 if (result) {
7398 return *output;
7399 } else {
7400 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007401 }
7402}
7403
7404
7405static Object* Runtime_DateLocalTimezone(Arguments args) {
7406 NoHandleAllocation ha;
7407 ASSERT(args.length() == 1);
7408
7409 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007410 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007411 return Heap::AllocateStringFromUtf8(CStrVector(zone));
7412}
7413
7414
7415static Object* Runtime_DateLocalTimeOffset(Arguments args) {
7416 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007417 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007418
7419 return Heap::NumberFromDouble(OS::LocalTimeOffset());
7420}
7421
7422
7423static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
7424 NoHandleAllocation ha;
7425 ASSERT(args.length() == 1);
7426
7427 CONVERT_DOUBLE_CHECKED(x, args[0]);
7428 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
7429}
7430
7431
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007432static Object* Runtime_GlobalReceiver(Arguments args) {
7433 ASSERT(args.length() == 1);
7434 Object* global = args[0];
7435 if (!global->IsJSGlobalObject()) return Heap::null_value();
7436 return JSGlobalObject::cast(global)->global_receiver();
7437}
7438
7439
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007440static Object* Runtime_CompileString(Arguments args) {
7441 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007442 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007443 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007444 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007445
ager@chromium.org381abbb2009-02-25 13:23:22 +00007446 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007447 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007448 Compiler::ValidationState validate = (is_json->IsTrue())
7449 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007450 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
7451 context,
7452 true,
7453 validate);
7454 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007455 Handle<JSFunction> fun =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007456 Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007457 return *fun;
7458}
7459
7460
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007461static ObjectPair CompileGlobalEval(Handle<String> source,
7462 Handle<Object> receiver) {
7463 // Deal with a normal eval call with a string argument. Compile it
7464 // and return the compiled function bound in the local context.
7465 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
7466 source,
7467 Handle<Context>(Top::context()),
7468 Top::context()->IsGlobalContext(),
7469 Compiler::DONT_VALIDATE_JSON);
7470 if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
7471 Handle<JSFunction> compiled = Factory::NewFunctionFromSharedFunctionInfo(
7472 shared,
7473 Handle<Context>(Top::context()),
7474 NOT_TENURED);
7475 return MakePair(*compiled, *receiver);
7476}
7477
7478
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007479static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
7480 ASSERT(args.length() == 3);
7481 if (!args[0]->IsJSFunction()) {
7482 return MakePair(Top::ThrowIllegalOperation(), NULL);
7483 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007484
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007485 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007486 Handle<JSFunction> callee = args.at<JSFunction>(0);
7487 Handle<Object> receiver; // Will be overwritten.
7488
7489 // Compute the calling context.
7490 Handle<Context> context = Handle<Context>(Top::context());
7491#ifdef DEBUG
7492 // Make sure Top::context() agrees with the old code that traversed
7493 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007494 StackFrameLocator locator;
7495 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007496 ASSERT(Context::cast(frame->context()) == *context);
7497#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007498
7499 // Find where the 'eval' symbol is bound. It is unaliased only if
7500 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007501 int index = -1;
7502 PropertyAttributes attributes = ABSENT;
7503 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007504 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
7505 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007506 // Stop search when eval is found or when the global context is
7507 // reached.
7508 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007509 if (context->is_function_context()) {
7510 context = Handle<Context>(Context::cast(context->closure()->context()));
7511 } else {
7512 context = Handle<Context>(context->previous());
7513 }
7514 }
7515
iposva@chromium.org245aa852009-02-10 00:49:54 +00007516 // If eval could not be resolved, it has been deleted and we need to
7517 // throw a reference error.
7518 if (attributes == ABSENT) {
7519 Handle<Object> name = Factory::eval_symbol();
7520 Handle<Object> reference_error =
7521 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007522 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007523 }
7524
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007525 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007526 // 'eval' is not bound in the global context. Just call the function
7527 // with the given arguments. This is not necessarily the global eval.
7528 if (receiver->IsContext()) {
7529 context = Handle<Context>::cast(receiver);
7530 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007531 } else if (receiver->IsJSContextExtensionObject()) {
7532 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007533 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007534 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007535 }
7536
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007537 // 'eval' is bound in the global context, but it may have been overwritten.
7538 // Compare it to the builtin 'GlobalEval' function to make sure.
7539 if (*callee != Top::global_context()->global_eval_fun() ||
7540 !args[1]->IsString()) {
7541 return MakePair(*callee, Top::context()->global()->global_receiver());
7542 }
7543
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007544 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
7545}
7546
7547
7548static ObjectPair Runtime_ResolvePossiblyDirectEvalNoLookup(Arguments args) {
7549 ASSERT(args.length() == 3);
7550 if (!args[0]->IsJSFunction()) {
7551 return MakePair(Top::ThrowIllegalOperation(), NULL);
7552 }
7553
7554 HandleScope scope;
7555 Handle<JSFunction> callee = args.at<JSFunction>(0);
7556
7557 // 'eval' is bound in the global context, but it may have been overwritten.
7558 // Compare it to the builtin 'GlobalEval' function to make sure.
7559 if (*callee != Top::global_context()->global_eval_fun() ||
7560 !args[1]->IsString()) {
7561 return MakePair(*callee, Top::context()->global()->global_receiver());
7562 }
7563
7564 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007565}
7566
7567
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007568static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
7569 // This utility adjusts the property attributes for newly created Function
7570 // object ("new Function(...)") by changing the map.
7571 // All it does is changing the prototype property to enumerable
7572 // as specified in ECMA262, 15.3.5.2.
7573 HandleScope scope;
7574 ASSERT(args.length() == 1);
7575 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7576 ASSERT(func->map()->instance_type() ==
7577 Top::function_instance_map()->instance_type());
7578 ASSERT(func->map()->instance_size() ==
7579 Top::function_instance_map()->instance_size());
7580 func->set_map(*Top::function_instance_map());
7581 return *func;
7582}
7583
7584
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007585// Push an array unto an array of arrays if it is not already in the
7586// array. Returns true if the element was pushed on the stack and
7587// false otherwise.
7588static Object* Runtime_PushIfAbsent(Arguments args) {
7589 ASSERT(args.length() == 2);
7590 CONVERT_CHECKED(JSArray, array, args[0]);
7591 CONVERT_CHECKED(JSArray, element, args[1]);
7592 RUNTIME_ASSERT(array->HasFastElements());
7593 int length = Smi::cast(array->length())->value();
7594 FixedArray* elements = FixedArray::cast(array->elements());
7595 for (int i = 0; i < length; i++) {
7596 if (elements->get(i) == element) return Heap::false_value();
7597 }
7598 Object* obj = array->SetFastElement(length, element);
7599 if (obj->IsFailure()) return obj;
7600 return Heap::true_value();
7601}
7602
7603
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007604/**
7605 * A simple visitor visits every element of Array's.
7606 * The backend storage can be a fixed array for fast elements case,
7607 * or a dictionary for sparse array. Since Dictionary is a subtype
7608 * of FixedArray, the class can be used by both fast and slow cases.
7609 * The second parameter of the constructor, fast_elements, specifies
7610 * whether the storage is a FixedArray or Dictionary.
7611 *
7612 * An index limit is used to deal with the situation that a result array
7613 * length overflows 32-bit non-negative integer.
7614 */
7615class ArrayConcatVisitor {
7616 public:
7617 ArrayConcatVisitor(Handle<FixedArray> storage,
7618 uint32_t index_limit,
7619 bool fast_elements) :
7620 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007621 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007622
7623 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007624 if (i >= index_limit_ - index_offset_) return;
7625 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007626
7627 if (fast_elements_) {
7628 ASSERT(index < static_cast<uint32_t>(storage_->length()));
7629 storage_->set(index, *elm);
7630
7631 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007632 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
7633 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007634 Factory::DictionaryAtNumberPut(dict, index, elm);
7635 if (!result.is_identical_to(dict))
7636 storage_ = result;
7637 }
7638 }
7639
7640 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007641 if (index_limit_ - index_offset_ < delta) {
7642 index_offset_ = index_limit_;
7643 } else {
7644 index_offset_ += delta;
7645 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007646 }
7647
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007648 Handle<FixedArray> storage() { return storage_; }
7649
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007650 private:
7651 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007652 // Limit on the accepted indices. Elements with indices larger than the
7653 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007654 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007655 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007656 uint32_t index_offset_;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007657 const bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007658};
7659
7660
ager@chromium.org3811b432009-10-28 14:53:37 +00007661template<class ExternalArrayClass, class ElementType>
7662static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
7663 bool elements_are_ints,
7664 bool elements_are_guaranteed_smis,
7665 uint32_t range,
7666 ArrayConcatVisitor* visitor) {
7667 Handle<ExternalArrayClass> array(
7668 ExternalArrayClass::cast(receiver->elements()));
7669 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
7670
7671 if (visitor != NULL) {
7672 if (elements_are_ints) {
7673 if (elements_are_guaranteed_smis) {
7674 for (uint32_t j = 0; j < len; j++) {
7675 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
7676 visitor->visit(j, e);
7677 }
7678 } else {
7679 for (uint32_t j = 0; j < len; j++) {
7680 int64_t val = static_cast<int64_t>(array->get(j));
7681 if (Smi::IsValid(static_cast<intptr_t>(val))) {
7682 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
7683 visitor->visit(j, e);
7684 } else {
7685 Handle<Object> e(
7686 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
7687 visitor->visit(j, e);
7688 }
7689 }
7690 }
7691 } else {
7692 for (uint32_t j = 0; j < len; j++) {
7693 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
7694 visitor->visit(j, e);
7695 }
7696 }
7697 }
7698
7699 return len;
7700}
7701
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007702/**
7703 * A helper function that visits elements of a JSObject. Only elements
7704 * whose index between 0 and range (exclusive) are visited.
7705 *
7706 * If the third parameter, visitor, is not NULL, the visitor is called
7707 * with parameters, 'visitor_index_offset + element index' and the element.
7708 *
7709 * It returns the number of visisted elements.
7710 */
7711static uint32_t IterateElements(Handle<JSObject> receiver,
7712 uint32_t range,
7713 ArrayConcatVisitor* visitor) {
7714 uint32_t num_of_elements = 0;
7715
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007716 switch (receiver->GetElementsKind()) {
7717 case JSObject::FAST_ELEMENTS: {
7718 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
7719 uint32_t len = elements->length();
7720 if (range < len) {
7721 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007722 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007723
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007724 for (uint32_t j = 0; j < len; j++) {
7725 Handle<Object> e(elements->get(j));
7726 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007727 num_of_elements++;
7728 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007729 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007730 }
7731 }
7732 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007733 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007734 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007735 case JSObject::PIXEL_ELEMENTS: {
7736 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
7737 uint32_t len = pixels->length();
7738 if (range < len) {
7739 len = range;
7740 }
7741
7742 for (uint32_t j = 0; j < len; j++) {
7743 num_of_elements++;
7744 if (visitor != NULL) {
7745 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
7746 visitor->visit(j, e);
7747 }
7748 }
7749 break;
7750 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007751 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
7752 num_of_elements =
7753 IterateExternalArrayElements<ExternalByteArray, int8_t>(
7754 receiver, true, true, range, visitor);
7755 break;
7756 }
7757 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7758 num_of_elements =
7759 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
7760 receiver, true, true, range, visitor);
7761 break;
7762 }
7763 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
7764 num_of_elements =
7765 IterateExternalArrayElements<ExternalShortArray, int16_t>(
7766 receiver, true, true, range, visitor);
7767 break;
7768 }
7769 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7770 num_of_elements =
7771 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
7772 receiver, true, true, range, visitor);
7773 break;
7774 }
7775 case JSObject::EXTERNAL_INT_ELEMENTS: {
7776 num_of_elements =
7777 IterateExternalArrayElements<ExternalIntArray, int32_t>(
7778 receiver, true, false, range, visitor);
7779 break;
7780 }
7781 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7782 num_of_elements =
7783 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
7784 receiver, true, false, range, visitor);
7785 break;
7786 }
7787 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
7788 num_of_elements =
7789 IterateExternalArrayElements<ExternalFloatArray, float>(
7790 receiver, false, false, range, visitor);
7791 break;
7792 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007793 case JSObject::DICTIONARY_ELEMENTS: {
7794 Handle<NumberDictionary> dict(receiver->element_dictionary());
7795 uint32_t capacity = dict->Capacity();
7796 for (uint32_t j = 0; j < capacity; j++) {
7797 Handle<Object> k(dict->KeyAt(j));
7798 if (dict->IsKey(*k)) {
7799 ASSERT(k->IsNumber());
7800 uint32_t index = static_cast<uint32_t>(k->Number());
7801 if (index < range) {
7802 num_of_elements++;
7803 if (visitor) {
7804 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
7805 }
7806 }
7807 }
7808 }
7809 break;
7810 }
7811 default:
7812 UNREACHABLE();
7813 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007814 }
7815
7816 return num_of_elements;
7817}
7818
7819
7820/**
7821 * A helper function that visits elements of an Array object, and elements
7822 * on its prototypes.
7823 *
7824 * Elements on prototypes are visited first, and only elements whose indices
7825 * less than Array length are visited.
7826 *
7827 * If a ArrayConcatVisitor object is given, the visitor is called with
7828 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007829 *
7830 * The returned number of elements is an upper bound on the actual number
7831 * of elements added. If the same element occurs in more than one object
7832 * in the array's prototype chain, it will be counted more than once, but
7833 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007834 */
7835static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
7836 ArrayConcatVisitor* visitor) {
7837 uint32_t range = static_cast<uint32_t>(array->length()->Number());
7838 Handle<Object> obj = array;
7839
7840 static const int kEstimatedPrototypes = 3;
7841 List< Handle<JSObject> > objects(kEstimatedPrototypes);
7842
7843 // Visit prototype first. If an element on the prototype is shadowed by
7844 // the inheritor using the same index, the ArrayConcatVisitor visits
7845 // the prototype element before the shadowing element.
7846 // The visitor can simply overwrite the old value by new value using
7847 // the same index. This follows Array::concat semantics.
7848 while (!obj->IsNull()) {
7849 objects.Add(Handle<JSObject>::cast(obj));
7850 obj = Handle<Object>(obj->GetPrototype());
7851 }
7852
7853 uint32_t nof_elements = 0;
7854 for (int i = objects.length() - 1; i >= 0; i--) {
7855 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007856 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007857 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007858
7859 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
7860 nof_elements = JSObject::kMaxElementCount;
7861 } else {
7862 nof_elements += encountered_elements;
7863 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007864 }
7865
7866 return nof_elements;
7867}
7868
7869
7870/**
7871 * A helper function of Runtime_ArrayConcat.
7872 *
7873 * The first argument is an Array of arrays and objects. It is the
7874 * same as the arguments array of Array::concat JS function.
7875 *
7876 * If an argument is an Array object, the function visits array
7877 * elements. If an argument is not an Array object, the function
7878 * visits the object as if it is an one-element array.
7879 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007880 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007881 * non-negative number is used as new length. For example, if one
7882 * array length is 2^32 - 1, second array length is 1, the
7883 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007884 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
7885 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007886 */
7887static uint32_t IterateArguments(Handle<JSArray> arguments,
7888 ArrayConcatVisitor* visitor) {
7889 uint32_t visited_elements = 0;
7890 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7891
7892 for (uint32_t i = 0; i < num_of_args; i++) {
7893 Handle<Object> obj(arguments->GetElement(i));
7894 if (obj->IsJSArray()) {
7895 Handle<JSArray> array = Handle<JSArray>::cast(obj);
7896 uint32_t len = static_cast<uint32_t>(array->length()->Number());
7897 uint32_t nof_elements =
7898 IterateArrayAndPrototypeElements(array, visitor);
7899 // Total elements of array and its prototype chain can be more than
7900 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007901 // the array length number of elements. We use the length as an estimate
7902 // for the actual number of elements added.
7903 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
7904 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
7905 visited_elements = JSArray::kMaxElementCount;
7906 } else {
7907 visited_elements += added_elements;
7908 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007909 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007910 } else {
7911 if (visitor) {
7912 visitor->visit(0, obj);
7913 visitor->increase_index_offset(1);
7914 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007915 if (visited_elements < JSArray::kMaxElementCount) {
7916 visited_elements++;
7917 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007918 }
7919 }
7920 return visited_elements;
7921}
7922
7923
7924/**
7925 * Array::concat implementation.
7926 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007927 * TODO(lrn): Fix non-compliance for very large concatenations and update to
7928 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007929 */
7930static Object* Runtime_ArrayConcat(Arguments args) {
7931 ASSERT(args.length() == 1);
7932 HandleScope handle_scope;
7933
7934 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
7935 Handle<JSArray> arguments(arg_arrays);
7936
7937 // Pass 1: estimate the number of elements of the result
7938 // (it could be more than real numbers if prototype has elements).
7939 uint32_t result_length = 0;
7940 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7941
7942 { AssertNoAllocation nogc;
7943 for (uint32_t i = 0; i < num_of_args; i++) {
7944 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007945 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007946 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007947 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007948 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
7949 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007950 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007951 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007952 if (JSObject::kMaxElementCount - result_length < length_estimate) {
7953 result_length = JSObject::kMaxElementCount;
7954 break;
7955 }
7956 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007957 }
7958 }
7959
7960 // Allocate an empty array, will set length and content later.
7961 Handle<JSArray> result = Factory::NewJSArray(0);
7962
7963 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
7964 // If estimated number of elements is more than half of length, a
7965 // fixed array (fast case) is more time and space-efficient than a
7966 // dictionary.
7967 bool fast_case = (estimate_nof_elements * 2) >= result_length;
7968
7969 Handle<FixedArray> storage;
7970 if (fast_case) {
7971 // The backing storage array must have non-existing elements to
7972 // preserve holes across concat operations.
7973 storage = Factory::NewFixedArrayWithHoles(result_length);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007974 result->set_map(*Factory::GetFastElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007975 } else {
7976 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
7977 uint32_t at_least_space_for = estimate_nof_elements +
7978 (estimate_nof_elements >> 2);
7979 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007980 Factory::NewNumberDictionary(at_least_space_for));
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007981 result->set_map(*Factory::GetSlowElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007982 }
7983
7984 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
7985
7986 ArrayConcatVisitor visitor(storage, result_length, fast_case);
7987
7988 IterateArguments(arguments, &visitor);
7989
7990 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007991 // Please note the storage might have changed in the visitor.
7992 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007993
7994 return *result;
7995}
7996
7997
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007998// This will not allocate (flatten the string), but it may run
7999// very slowly for very deeply nested ConsStrings. For debugging use only.
8000static Object* Runtime_GlobalPrint(Arguments args) {
8001 NoHandleAllocation ha;
8002 ASSERT(args.length() == 1);
8003
8004 CONVERT_CHECKED(String, string, args[0]);
8005 StringInputBuffer buffer(string);
8006 while (buffer.has_more()) {
8007 uint16_t character = buffer.GetNext();
8008 PrintF("%c", character);
8009 }
8010 return string;
8011}
8012
ager@chromium.org5ec48922009-05-05 07:25:34 +00008013// Moves all own elements of an object, that are below a limit, to positions
8014// starting at zero. All undefined values are placed after non-undefined values,
8015// and are followed by non-existing element. Does not change the length
8016// property.
8017// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008018static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00008019 ASSERT(args.length() == 2);
8020 CONVERT_CHECKED(JSObject, object, args[0]);
8021 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
8022 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008023}
8024
8025
8026// Move contents of argument 0 (an array) to argument 1 (an array)
8027static Object* Runtime_MoveArrayContents(Arguments args) {
8028 ASSERT(args.length() == 2);
8029 CONVERT_CHECKED(JSArray, from, args[0]);
8030 CONVERT_CHECKED(JSArray, to, args[1]);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00008031 HeapObject* new_elements = from->elements();
8032 Object* new_map;
8033 if (new_elements->map() == Heap::fixed_array_map()) {
8034 new_map = to->map()->GetFastElementsMap();
8035 } else {
8036 new_map = to->map()->GetSlowElementsMap();
8037 }
8038 if (new_map->IsFailure()) return new_map;
8039 to->set_map(Map::cast(new_map));
8040 to->set_elements(new_elements);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008041 to->set_length(from->length());
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00008042 Object* obj = from->ResetElements();
8043 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008044 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008045 return to;
8046}
8047
8048
8049// How many elements does this array have?
8050static Object* Runtime_EstimateNumberOfElements(Arguments args) {
8051 ASSERT(args.length() == 1);
8052 CONVERT_CHECKED(JSArray, array, args[0]);
8053 HeapObject* elements = array->elements();
8054 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008055 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008056 } else {
8057 return array->length();
8058 }
8059}
8060
8061
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008062static Object* Runtime_SwapElements(Arguments args) {
8063 HandleScope handle_scope;
8064
8065 ASSERT_EQ(3, args.length());
8066
ager@chromium.orgac091b72010-05-05 07:34:42 +00008067 CONVERT_ARG_CHECKED(JSObject, object, 0);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008068 Handle<Object> key1 = args.at<Object>(1);
8069 Handle<Object> key2 = args.at<Object>(2);
8070
8071 uint32_t index1, index2;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008072 if (!key1->ToArrayIndex(&index1)
8073 || !key2->ToArrayIndex(&index2)) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00008074 return Top::ThrowIllegalOperation();
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008075 }
8076
ager@chromium.orgac091b72010-05-05 07:34:42 +00008077 Handle<JSObject> jsobject = Handle<JSObject>::cast(object);
8078 Handle<Object> tmp1 = GetElement(jsobject, index1);
8079 Handle<Object> tmp2 = GetElement(jsobject, index2);
8080
8081 SetElement(jsobject, index1, tmp2);
8082 SetElement(jsobject, index2, tmp1);
8083
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008084 return Heap::undefined_value();
8085}
8086
8087
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008088// Returns an array that tells you where in the [0, length) interval an array
8089// might have elements. Can either return keys or intervals. Keys can have
8090// gaps in (undefined). Intervals can also span over some undefined keys.
8091static Object* Runtime_GetArrayKeys(Arguments args) {
8092 ASSERT(args.length() == 2);
8093 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00008094 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008095 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008096 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008097 // Create an array and get all the keys into it, then remove all the
8098 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008099 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008100 int keys_length = keys->length();
8101 for (int i = 0; i < keys_length; i++) {
8102 Object* key = keys->get(i);
8103 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008104 if (!key->ToArrayIndex(&index) || index >= length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008105 // Zap invalid keys.
8106 keys->set_undefined(i);
8107 }
8108 }
8109 return *Factory::NewJSArrayWithElements(keys);
8110 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008111 ASSERT(array->HasFastElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008112 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
8113 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008114 single_interval->set(0, Smi::FromInt(-1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008115 uint32_t actual_length =
8116 static_cast<uint32_t>(FixedArray::cast(array->elements())->length());
ager@chromium.org5ec48922009-05-05 07:25:34 +00008117 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008118 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00008119 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008120 single_interval->set(1, *length_object);
8121 return *Factory::NewJSArrayWithElements(single_interval);
8122 }
8123}
8124
8125
8126// DefineAccessor takes an optional final argument which is the
8127// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
8128// to the way accessors are implemented, it is set for both the getter
8129// and setter on the first call to DefineAccessor and ignored on
8130// subsequent calls.
8131static Object* Runtime_DefineAccessor(Arguments args) {
8132 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
8133 // Compute attributes.
8134 PropertyAttributes attributes = NONE;
8135 if (args.length() == 5) {
8136 CONVERT_CHECKED(Smi, attrs, args[4]);
8137 int value = attrs->value();
8138 // Only attribute bits should be set.
8139 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
8140 attributes = static_cast<PropertyAttributes>(value);
8141 }
8142
8143 CONVERT_CHECKED(JSObject, obj, args[0]);
8144 CONVERT_CHECKED(String, name, args[1]);
8145 CONVERT_CHECKED(Smi, flag, args[2]);
8146 CONVERT_CHECKED(JSFunction, fun, args[3]);
8147 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
8148}
8149
8150
8151static Object* Runtime_LookupAccessor(Arguments args) {
8152 ASSERT(args.length() == 3);
8153 CONVERT_CHECKED(JSObject, obj, args[0]);
8154 CONVERT_CHECKED(String, name, args[1]);
8155 CONVERT_CHECKED(Smi, flag, args[2]);
8156 return obj->LookupAccessor(name, flag->value() == 0);
8157}
8158
8159
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008160#ifdef ENABLE_DEBUGGER_SUPPORT
8161static Object* Runtime_DebugBreak(Arguments args) {
8162 ASSERT(args.length() == 0);
8163 return Execution::DebugBreakHelper();
8164}
8165
8166
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008167// Helper functions for wrapping and unwrapping stack frame ids.
8168static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008169 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008170 return Smi::FromInt(id >> 2);
8171}
8172
8173
8174static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
8175 return static_cast<StackFrame::Id>(wrapped->value() << 2);
8176}
8177
8178
8179// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00008180// args[0]: debug event listener function to set or null or undefined for
8181// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008182// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00008183static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008184 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00008185 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
8186 args[0]->IsUndefined() ||
8187 args[0]->IsNull());
8188 Handle<Object> callback = args.at<Object>(0);
8189 Handle<Object> data = args.at<Object>(1);
8190 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008191
8192 return Heap::undefined_value();
8193}
8194
8195
8196static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00008197 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008198 StackGuard::DebugBreak();
8199 return Heap::undefined_value();
8200}
8201
8202
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008203static Object* DebugLookupResultValue(Object* receiver, String* name,
8204 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00008205 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008206 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008207 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008208 case NORMAL:
8209 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008210 if (value->IsTheHole()) {
8211 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008212 }
8213 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008214 case FIELD:
8215 value =
8216 JSObject::cast(
8217 result->holder())->FastPropertyAt(result->GetFieldIndex());
8218 if (value->IsTheHole()) {
8219 return Heap::undefined_value();
8220 }
8221 return value;
8222 case CONSTANT_FUNCTION:
8223 return result->GetConstantFunction();
8224 case CALLBACKS: {
8225 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008226 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008227 value = receiver->GetPropertyWithCallback(
8228 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00008229 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008230 value = Top::pending_exception();
8231 Top::clear_pending_exception();
8232 if (caught_exception != NULL) {
8233 *caught_exception = true;
8234 }
8235 }
8236 return value;
8237 } else {
8238 return Heap::undefined_value();
8239 }
8240 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008241 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008242 case MAP_TRANSITION:
8243 case CONSTANT_TRANSITION:
8244 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008245 return Heap::undefined_value();
8246 default:
8247 UNREACHABLE();
8248 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008249 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008250 return Heap::undefined_value();
8251}
8252
8253
ager@chromium.org32912102009-01-16 10:38:43 +00008254// Get debugger related details for an object property.
8255// args[0]: object holding property
8256// args[1]: name of the property
8257//
8258// The array returned contains the following information:
8259// 0: Property value
8260// 1: Property details
8261// 2: Property value is exception
8262// 3: Getter function if defined
8263// 4: Setter function if defined
8264// Items 2-4 are only filled if the property has either a getter or a setter
8265// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00008266static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008267 HandleScope scope;
8268
8269 ASSERT(args.length() == 2);
8270
8271 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8272 CONVERT_ARG_CHECKED(String, name, 1);
8273
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008274 // Make sure to set the current context to the context before the debugger was
8275 // entered (if the debugger is entered). The reason for switching context here
8276 // is that for some property lookups (accessors and interceptors) callbacks
8277 // into the embedding application can occour, and the embedding application
8278 // could have the assumption that its own global context is the current
8279 // context and not some internal debugger context.
8280 SaveContext save;
8281 if (Debug::InDebugger()) {
8282 Top::set_context(*Debug::debugger_entry()->GetContext());
8283 }
8284
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008285 // Skip the global proxy as it has no properties and always delegates to the
8286 // real global object.
8287 if (obj->IsJSGlobalProxy()) {
8288 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
8289 }
8290
8291
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008292 // Check if the name is trivially convertible to an index and get the element
8293 // if so.
8294 uint32_t index;
8295 if (name->AsArrayIndex(&index)) {
8296 Handle<FixedArray> details = Factory::NewFixedArray(2);
8297 details->set(0, Runtime::GetElementOrCharAt(obj, index));
8298 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
8299 return *Factory::NewJSArrayWithElements(details);
8300 }
8301
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008302 // Find the number of objects making up this.
8303 int length = LocalPrototypeChainLength(*obj);
8304
8305 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008306 Handle<JSObject> jsproto = obj;
8307 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008308 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008309 jsproto->LocalLookup(*name, &result);
8310 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008311 // LookupResult is not GC safe as it holds raw object pointers.
8312 // GC can happen later in this code so put the required fields into
8313 // local variables using handles when required for later use.
8314 PropertyType result_type = result.type();
8315 Handle<Object> result_callback_obj;
8316 if (result_type == CALLBACKS) {
8317 result_callback_obj = Handle<Object>(result.GetCallbackObject());
8318 }
8319 Smi* property_details = result.GetPropertyDetails().AsSmi();
8320 // DebugLookupResultValue can cause GC so details from LookupResult needs
8321 // to be copied to handles before this.
8322 bool caught_exception = false;
8323 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
8324 &caught_exception);
8325 if (raw_value->IsFailure()) return raw_value;
8326 Handle<Object> value(raw_value);
8327
8328 // If the callback object is a fixed array then it contains JavaScript
8329 // getter and/or setter.
8330 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
8331 result_callback_obj->IsFixedArray();
8332 Handle<FixedArray> details =
8333 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
8334 details->set(0, *value);
8335 details->set(1, property_details);
8336 if (hasJavaScriptAccessors) {
8337 details->set(2,
8338 caught_exception ? Heap::true_value()
8339 : Heap::false_value());
8340 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
8341 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
8342 }
8343
8344 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008345 }
8346 if (i < length - 1) {
8347 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
8348 }
8349 }
8350
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008351 return Heap::undefined_value();
8352}
8353
8354
8355static Object* Runtime_DebugGetProperty(Arguments args) {
8356 HandleScope scope;
8357
8358 ASSERT(args.length() == 2);
8359
8360 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8361 CONVERT_ARG_CHECKED(String, name, 1);
8362
8363 LookupResult result;
8364 obj->Lookup(*name, &result);
8365 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008366 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008367 }
8368 return Heap::undefined_value();
8369}
8370
8371
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008372// Return the property type calculated from the property details.
8373// args[0]: smi with property details.
8374static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
8375 ASSERT(args.length() == 1);
8376 CONVERT_CHECKED(Smi, details, args[0]);
8377 PropertyType type = PropertyDetails(details).type();
8378 return Smi::FromInt(static_cast<int>(type));
8379}
8380
8381
8382// Return the property attribute calculated from the property details.
8383// args[0]: smi with property details.
8384static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
8385 ASSERT(args.length() == 1);
8386 CONVERT_CHECKED(Smi, details, args[0]);
8387 PropertyAttributes attributes = PropertyDetails(details).attributes();
8388 return Smi::FromInt(static_cast<int>(attributes));
8389}
8390
8391
8392// Return the property insertion index calculated from the property details.
8393// args[0]: smi with property details.
8394static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
8395 ASSERT(args.length() == 1);
8396 CONVERT_CHECKED(Smi, details, args[0]);
8397 int index = PropertyDetails(details).index();
8398 return Smi::FromInt(index);
8399}
8400
8401
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008402// Return property value from named interceptor.
8403// args[0]: object
8404// args[1]: property name
8405static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
8406 HandleScope scope;
8407 ASSERT(args.length() == 2);
8408 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8409 RUNTIME_ASSERT(obj->HasNamedInterceptor());
8410 CONVERT_ARG_CHECKED(String, name, 1);
8411
8412 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008413 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008414}
8415
8416
8417// Return element value from indexed interceptor.
8418// args[0]: object
8419// args[1]: index
8420static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
8421 HandleScope scope;
8422 ASSERT(args.length() == 2);
8423 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8424 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
8425 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
8426
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008427 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008428}
8429
8430
8431static Object* Runtime_CheckExecutionState(Arguments args) {
8432 ASSERT(args.length() >= 1);
8433 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00008434 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008435 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008436 return Top::Throw(Heap::illegal_execution_state_symbol());
8437 }
8438
8439 return Heap::true_value();
8440}
8441
8442
8443static Object* Runtime_GetFrameCount(Arguments args) {
8444 HandleScope scope;
8445 ASSERT(args.length() == 1);
8446
8447 // Check arguments.
8448 Object* result = Runtime_CheckExecutionState(args);
8449 if (result->IsFailure()) return result;
8450
8451 // Count all frames which are relevant to debugging stack trace.
8452 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008453 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008454 if (id == StackFrame::NO_ID) {
8455 // If there is no JavaScript stack frame count is 0.
8456 return Smi::FromInt(0);
8457 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008458 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
8459 return Smi::FromInt(n);
8460}
8461
8462
8463static const int kFrameDetailsFrameIdIndex = 0;
8464static const int kFrameDetailsReceiverIndex = 1;
8465static const int kFrameDetailsFunctionIndex = 2;
8466static const int kFrameDetailsArgumentCountIndex = 3;
8467static const int kFrameDetailsLocalCountIndex = 4;
8468static const int kFrameDetailsSourcePositionIndex = 5;
8469static const int kFrameDetailsConstructCallIndex = 6;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008470static const int kFrameDetailsAtReturnIndex = 7;
8471static const int kFrameDetailsDebuggerFrameIndex = 8;
8472static const int kFrameDetailsFirstDynamicIndex = 9;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008473
8474// Return an array with frame details
8475// args[0]: number: break id
8476// args[1]: number: frame index
8477//
8478// The array returned contains the following information:
8479// 0: Frame id
8480// 1: Receiver
8481// 2: Function
8482// 3: Argument count
8483// 4: Local count
8484// 5: Source position
8485// 6: Constructor call
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008486// 7: Is at return
8487// 8: Debugger frame
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008488// Arguments name, value
8489// Locals name, value
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008490// Return value if any
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008491static Object* Runtime_GetFrameDetails(Arguments args) {
8492 HandleScope scope;
8493 ASSERT(args.length() == 2);
8494
8495 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008496 Object* check = Runtime_CheckExecutionState(args);
8497 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008498 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8499
8500 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008501 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008502 if (id == StackFrame::NO_ID) {
8503 // If there are no JavaScript stack frames return undefined.
8504 return Heap::undefined_value();
8505 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008506 int count = 0;
8507 JavaScriptFrameIterator it(id);
8508 for (; !it.done(); it.Advance()) {
8509 if (count == index) break;
8510 count++;
8511 }
8512 if (it.done()) return Heap::undefined_value();
8513
8514 // Traverse the saved contexts chain to find the active context for the
8515 // selected frame.
8516 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008517 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008518 save = save->prev();
8519 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008520 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008521
8522 // Get the frame id.
8523 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
8524
8525 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008526 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008527
8528 // Check for constructor frame.
8529 bool constructor = it.frame()->IsConstructor();
8530
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008531 // Get scope info and read from it for local variable information.
8532 Handle<JSFunction> function(JSFunction::cast(it.frame()->function()));
ager@chromium.orgb5737492010-07-15 09:29:43 +00008533 Handle<SerializedScopeInfo> scope_info(function->shared()->scope_info());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008534 ScopeInfo<> info(*scope_info);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008535
8536 // Get the context.
8537 Handle<Context> context(Context::cast(it.frame()->context()));
8538
8539 // Get the locals names and values into a temporary array.
8540 //
8541 // TODO(1240907): Hide compiler-introduced stack variables
8542 // (e.g. .result)? For users of the debugger, they will probably be
8543 // confusing.
8544 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
8545 for (int i = 0; i < info.NumberOfLocals(); i++) {
8546 // Name of the local.
8547 locals->set(i * 2, *info.LocalName(i));
8548
8549 // Fetch the value of the local - either from the stack or from a
8550 // heap-allocated context.
8551 if (i < info.number_of_stack_slots()) {
8552 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
8553 } else {
8554 Handle<String> name = info.LocalName(i);
8555 // Traverse the context chain to the function context as all local
8556 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008557 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008558 context = Handle<Context>(context->previous());
8559 }
8560 ASSERT(context->is_function_context());
8561 locals->set(i * 2 + 1,
ager@chromium.orgb5737492010-07-15 09:29:43 +00008562 context->get(scope_info->ContextSlotIndex(*name, NULL)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008563 }
8564 }
8565
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008566 // Check whether this frame is positioned at return.
8567 int at_return = (index == 0) ? Debug::IsBreakAtReturn(it.frame()) : false;
8568
8569 // If positioned just before return find the value to be returned and add it
8570 // to the frame information.
8571 Handle<Object> return_value = Factory::undefined_value();
8572 if (at_return) {
8573 StackFrameIterator it2;
8574 Address internal_frame_sp = NULL;
8575 while (!it2.done()) {
8576 if (it2.frame()->is_internal()) {
8577 internal_frame_sp = it2.frame()->sp();
8578 } else {
8579 if (it2.frame()->is_java_script()) {
8580 if (it2.frame()->id() == it.frame()->id()) {
8581 // The internal frame just before the JavaScript frame contains the
8582 // value to return on top. A debug break at return will create an
8583 // internal frame to store the return value (eax/rax/r0) before
8584 // entering the debug break exit frame.
8585 if (internal_frame_sp != NULL) {
8586 return_value =
8587 Handle<Object>(Memory::Object_at(internal_frame_sp));
8588 break;
8589 }
8590 }
8591 }
8592
8593 // Indicate that the previous frame was not an internal frame.
8594 internal_frame_sp = NULL;
8595 }
8596 it2.Advance();
8597 }
8598 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008599
8600 // Now advance to the arguments adapter frame (if any). It contains all
8601 // the provided parameters whereas the function frame always have the number
8602 // of arguments matching the functions parameters. The rest of the
8603 // information (except for what is collected above) is the same.
8604 it.AdvanceToArgumentsFrame();
8605
8606 // Find the number of arguments to fill. At least fill the number of
8607 // parameters for the function and fill more if more parameters are provided.
8608 int argument_count = info.number_of_parameters();
8609 if (argument_count < it.frame()->GetProvidedParametersCount()) {
8610 argument_count = it.frame()->GetProvidedParametersCount();
8611 }
8612
8613 // Calculate the size of the result.
8614 int details_size = kFrameDetailsFirstDynamicIndex +
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008615 2 * (argument_count + info.NumberOfLocals()) +
8616 (at_return ? 1 : 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008617 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8618
8619 // Add the frame id.
8620 details->set(kFrameDetailsFrameIdIndex, *frame_id);
8621
8622 // Add the function (same as in function frame).
8623 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
8624
8625 // Add the arguments count.
8626 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
8627
8628 // Add the locals count
8629 details->set(kFrameDetailsLocalCountIndex,
8630 Smi::FromInt(info.NumberOfLocals()));
8631
8632 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00008633 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008634 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
8635 } else {
8636 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
8637 }
8638
8639 // Add the constructor information.
8640 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
8641
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008642 // Add the at return information.
8643 details->set(kFrameDetailsAtReturnIndex, Heap::ToBoolean(at_return));
8644
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008645 // Add information on whether this frame is invoked in the debugger context.
8646 details->set(kFrameDetailsDebuggerFrameIndex,
8647 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
8648
8649 // Fill the dynamic part.
8650 int details_index = kFrameDetailsFirstDynamicIndex;
8651
8652 // Add arguments name and value.
8653 for (int i = 0; i < argument_count; i++) {
8654 // Name of the argument.
8655 if (i < info.number_of_parameters()) {
8656 details->set(details_index++, *info.parameter_name(i));
8657 } else {
8658 details->set(details_index++, Heap::undefined_value());
8659 }
8660
8661 // Parameter value.
8662 if (i < it.frame()->GetProvidedParametersCount()) {
8663 details->set(details_index++, it.frame()->GetParameter(i));
8664 } else {
8665 details->set(details_index++, Heap::undefined_value());
8666 }
8667 }
8668
8669 // Add locals name and value from the temporary copy from the function frame.
8670 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
8671 details->set(details_index++, locals->get(i));
8672 }
8673
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008674 // Add the value being returned.
8675 if (at_return) {
8676 details->set(details_index++, *return_value);
8677 }
8678
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008679 // Add the receiver (same as in function frame).
8680 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
8681 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
8682 Handle<Object> receiver(it.frame()->receiver());
8683 if (!receiver->IsJSObject()) {
8684 // If the receiver is NOT a JSObject we have hit an optimization
8685 // where a value object is not converted into a wrapped JS objects.
8686 // To hide this optimization from the debugger, we wrap the receiver
8687 // by creating correct wrapper object based on the calling frame's
8688 // global context.
8689 it.Advance();
8690 Handle<Context> calling_frames_global_context(
8691 Context::cast(Context::cast(it.frame()->context())->global_context()));
8692 receiver = Factory::ToObject(receiver, calling_frames_global_context);
8693 }
8694 details->set(kFrameDetailsReceiverIndex, *receiver);
8695
8696 ASSERT_EQ(details_size, details_index);
8697 return *Factory::NewJSArrayWithElements(details);
8698}
8699
8700
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008701// Copy all the context locals into an object used to materialize a scope.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008702static void CopyContextLocalsToScopeObject(
8703 Handle<SerializedScopeInfo> serialized_scope_info,
8704 ScopeInfo<>& scope_info,
8705 Handle<Context> context,
8706 Handle<JSObject> scope_object) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008707 // Fill all context locals to the context extension.
8708 for (int i = Context::MIN_CONTEXT_SLOTS;
8709 i < scope_info.number_of_context_slots();
8710 i++) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00008711 int context_index = serialized_scope_info->ContextSlotIndex(
8712 *scope_info.context_slot_name(i), NULL);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008713
8714 // Don't include the arguments shadow (.arguments) context variable.
8715 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
8716 SetProperty(scope_object,
8717 scope_info.context_slot_name(i),
8718 Handle<Object>(context->get(context_index)), NONE);
8719 }
8720 }
8721}
8722
8723
8724// Create a plain JSObject which materializes the local scope for the specified
8725// frame.
8726static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
8727 Handle<JSFunction> function(JSFunction::cast(frame->function()));
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008728 Handle<SharedFunctionInfo> shared(function->shared());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008729 Handle<SerializedScopeInfo> serialized_scope_info(shared->scope_info());
8730 ScopeInfo<> scope_info(*serialized_scope_info);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008731
8732 // Allocate and initialize a JSObject with all the arguments, stack locals
8733 // heap locals and extension properties of the debugged function.
8734 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
8735
8736 // First fill all parameters.
8737 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8738 SetProperty(local_scope,
8739 scope_info.parameter_name(i),
8740 Handle<Object>(frame->GetParameter(i)), NONE);
8741 }
8742
8743 // Second fill all stack locals.
8744 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
8745 SetProperty(local_scope,
8746 scope_info.stack_slot_name(i),
8747 Handle<Object>(frame->GetExpression(i)), NONE);
8748 }
8749
8750 // Third fill all context locals.
8751 Handle<Context> frame_context(Context::cast(frame->context()));
8752 Handle<Context> function_context(frame_context->fcontext());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008753 CopyContextLocalsToScopeObject(serialized_scope_info, scope_info,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008754 function_context, local_scope);
8755
8756 // Finally copy any properties from the function context extension. This will
8757 // be variables introduced by eval.
8758 if (function_context->closure() == *function) {
8759 if (function_context->has_extension() &&
8760 !function_context->IsGlobalContext()) {
8761 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008762 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008763 for (int i = 0; i < keys->length(); i++) {
8764 // Names of variables introduced by eval are strings.
8765 ASSERT(keys->get(i)->IsString());
8766 Handle<String> key(String::cast(keys->get(i)));
8767 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
8768 }
8769 }
8770 }
8771 return local_scope;
8772}
8773
8774
8775// Create a plain JSObject which materializes the closure content for the
8776// context.
8777static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
8778 ASSERT(context->is_function_context());
8779
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008780 Handle<SharedFunctionInfo> shared(context->closure()->shared());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008781 Handle<SerializedScopeInfo> serialized_scope_info(shared->scope_info());
8782 ScopeInfo<> scope_info(*serialized_scope_info);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008783
8784 // Allocate and initialize a JSObject with all the content of theis function
8785 // closure.
8786 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
8787
8788 // Check whether the arguments shadow object exists.
8789 int arguments_shadow_index =
ager@chromium.orgb5737492010-07-15 09:29:43 +00008790 shared->scope_info()->ContextSlotIndex(Heap::arguments_shadow_symbol(),
8791 NULL);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008792 if (arguments_shadow_index >= 0) {
8793 // In this case all the arguments are available in the arguments shadow
8794 // object.
8795 Handle<JSObject> arguments_shadow(
8796 JSObject::cast(context->get(arguments_shadow_index)));
8797 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8798 SetProperty(closure_scope,
8799 scope_info.parameter_name(i),
8800 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
8801 }
8802 }
8803
8804 // Fill all context locals to the context extension.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008805 CopyContextLocalsToScopeObject(serialized_scope_info, scope_info,
8806 context, closure_scope);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008807
8808 // Finally copy any properties from the function context extension. This will
8809 // be variables introduced by eval.
8810 if (context->has_extension()) {
8811 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008812 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008813 for (int i = 0; i < keys->length(); i++) {
8814 // Names of variables introduced by eval are strings.
8815 ASSERT(keys->get(i)->IsString());
8816 Handle<String> key(String::cast(keys->get(i)));
8817 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
8818 }
8819 }
8820
8821 return closure_scope;
8822}
8823
8824
8825// Iterate over the actual scopes visible from a stack frame. All scopes are
8826// backed by an actual context except the local scope, which is inserted
8827// "artifically" in the context chain.
8828class ScopeIterator {
8829 public:
8830 enum ScopeType {
8831 ScopeTypeGlobal = 0,
8832 ScopeTypeLocal,
8833 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00008834 ScopeTypeClosure,
8835 // Every catch block contains an implicit with block (its parameter is
8836 // a JSContextExtensionObject) that extends current scope with a variable
8837 // holding exception object. Such with blocks are treated as scopes of their
8838 // own type.
8839 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008840 };
8841
8842 explicit ScopeIterator(JavaScriptFrame* frame)
8843 : frame_(frame),
8844 function_(JSFunction::cast(frame->function())),
8845 context_(Context::cast(frame->context())),
8846 local_done_(false),
8847 at_local_(false) {
8848
8849 // Check whether the first scope is actually a local scope.
8850 if (context_->IsGlobalContext()) {
8851 // If there is a stack slot for .result then this local scope has been
8852 // created for evaluating top level code and it is not a real local scope.
8853 // Checking for the existence of .result seems fragile, but the scope info
8854 // saved with the code object does not otherwise have that information.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008855 int index = function_->shared()->scope_info()->
8856 StackSlotIndex(Heap::result_symbol());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008857 at_local_ = index < 0;
8858 } else if (context_->is_function_context()) {
8859 at_local_ = true;
8860 }
8861 }
8862
8863 // More scopes?
8864 bool Done() { return context_.is_null(); }
8865
8866 // Move to the next scope.
8867 void Next() {
8868 // If at a local scope mark the local scope as passed.
8869 if (at_local_) {
8870 at_local_ = false;
8871 local_done_ = true;
8872
8873 // If the current context is not associated with the local scope the
8874 // current context is the next real scope, so don't move to the next
8875 // context in this case.
8876 if (context_->closure() != *function_) {
8877 return;
8878 }
8879 }
8880
8881 // The global scope is always the last in the chain.
8882 if (context_->IsGlobalContext()) {
8883 context_ = Handle<Context>();
8884 return;
8885 }
8886
8887 // Move to the next context.
8888 if (context_->is_function_context()) {
8889 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
8890 } else {
8891 context_ = Handle<Context>(context_->previous());
8892 }
8893
8894 // If passing the local scope indicate that the current scope is now the
8895 // local scope.
8896 if (!local_done_ &&
8897 (context_->IsGlobalContext() || (context_->is_function_context()))) {
8898 at_local_ = true;
8899 }
8900 }
8901
8902 // Return the type of the current scope.
8903 int Type() {
8904 if (at_local_) {
8905 return ScopeTypeLocal;
8906 }
8907 if (context_->IsGlobalContext()) {
8908 ASSERT(context_->global()->IsGlobalObject());
8909 return ScopeTypeGlobal;
8910 }
8911 if (context_->is_function_context()) {
8912 return ScopeTypeClosure;
8913 }
8914 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00008915 // Current scope is either an explicit with statement or a with statement
8916 // implicitely generated for a catch block.
8917 // If the extension object here is a JSContextExtensionObject then
8918 // current with statement is one frome a catch block otherwise it's a
8919 // regular with statement.
8920 if (context_->extension()->IsJSContextExtensionObject()) {
8921 return ScopeTypeCatch;
8922 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008923 return ScopeTypeWith;
8924 }
8925
8926 // Return the JavaScript object with the content of the current scope.
8927 Handle<JSObject> ScopeObject() {
8928 switch (Type()) {
8929 case ScopeIterator::ScopeTypeGlobal:
8930 return Handle<JSObject>(CurrentContext()->global());
8931 break;
8932 case ScopeIterator::ScopeTypeLocal:
8933 // Materialize the content of the local scope into a JSObject.
8934 return MaterializeLocalScope(frame_);
8935 break;
8936 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00008937 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008938 // Return the with object.
8939 return Handle<JSObject>(CurrentContext()->extension());
8940 break;
8941 case ScopeIterator::ScopeTypeClosure:
8942 // Materialize the content of the closure scope into a JSObject.
8943 return MaterializeClosure(CurrentContext());
8944 break;
8945 }
8946 UNREACHABLE();
8947 return Handle<JSObject>();
8948 }
8949
8950 // Return the context for this scope. For the local context there might not
8951 // be an actual context.
8952 Handle<Context> CurrentContext() {
8953 if (at_local_ && context_->closure() != *function_) {
8954 return Handle<Context>();
8955 }
8956 return context_;
8957 }
8958
8959#ifdef DEBUG
8960 // Debug print of the content of the current scope.
8961 void DebugPrint() {
8962 switch (Type()) {
8963 case ScopeIterator::ScopeTypeGlobal:
8964 PrintF("Global:\n");
8965 CurrentContext()->Print();
8966 break;
8967
8968 case ScopeIterator::ScopeTypeLocal: {
8969 PrintF("Local:\n");
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008970 ScopeInfo<> scope_info(function_->shared()->scope_info());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008971 scope_info.Print();
8972 if (!CurrentContext().is_null()) {
8973 CurrentContext()->Print();
8974 if (CurrentContext()->has_extension()) {
8975 Handle<JSObject> extension =
8976 Handle<JSObject>(CurrentContext()->extension());
8977 if (extension->IsJSContextExtensionObject()) {
8978 extension->Print();
8979 }
8980 }
8981 }
8982 break;
8983 }
8984
8985 case ScopeIterator::ScopeTypeWith: {
8986 PrintF("With:\n");
8987 Handle<JSObject> extension =
8988 Handle<JSObject>(CurrentContext()->extension());
8989 extension->Print();
8990 break;
8991 }
8992
ager@chromium.orga1645e22009-09-09 19:27:10 +00008993 case ScopeIterator::ScopeTypeCatch: {
8994 PrintF("Catch:\n");
8995 Handle<JSObject> extension =
8996 Handle<JSObject>(CurrentContext()->extension());
8997 extension->Print();
8998 break;
8999 }
9000
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009001 case ScopeIterator::ScopeTypeClosure: {
9002 PrintF("Closure:\n");
9003 CurrentContext()->Print();
9004 if (CurrentContext()->has_extension()) {
9005 Handle<JSObject> extension =
9006 Handle<JSObject>(CurrentContext()->extension());
9007 if (extension->IsJSContextExtensionObject()) {
9008 extension->Print();
9009 }
9010 }
9011 break;
9012 }
9013
9014 default:
9015 UNREACHABLE();
9016 }
9017 PrintF("\n");
9018 }
9019#endif
9020
9021 private:
9022 JavaScriptFrame* frame_;
9023 Handle<JSFunction> function_;
9024 Handle<Context> context_;
9025 bool local_done_;
9026 bool at_local_;
9027
9028 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
9029};
9030
9031
9032static Object* Runtime_GetScopeCount(Arguments args) {
9033 HandleScope scope;
9034 ASSERT(args.length() == 2);
9035
9036 // Check arguments.
9037 Object* check = Runtime_CheckExecutionState(args);
9038 if (check->IsFailure()) return check;
9039 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9040
9041 // Get the frame where the debugging is performed.
9042 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9043 JavaScriptFrameIterator it(id);
9044 JavaScriptFrame* frame = it.frame();
9045
9046 // Count the visible scopes.
9047 int n = 0;
9048 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
9049 n++;
9050 }
9051
9052 return Smi::FromInt(n);
9053}
9054
9055
9056static const int kScopeDetailsTypeIndex = 0;
9057static const int kScopeDetailsObjectIndex = 1;
9058static const int kScopeDetailsSize = 2;
9059
9060// Return an array with scope details
9061// args[0]: number: break id
9062// args[1]: number: frame index
9063// args[2]: number: scope index
9064//
9065// The array returned contains the following information:
9066// 0: Scope type
9067// 1: Scope object
9068static Object* Runtime_GetScopeDetails(Arguments args) {
9069 HandleScope scope;
9070 ASSERT(args.length() == 3);
9071
9072 // Check arguments.
9073 Object* check = Runtime_CheckExecutionState(args);
9074 if (check->IsFailure()) return check;
9075 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9076 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
9077
9078 // Get the frame where the debugging is performed.
9079 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9080 JavaScriptFrameIterator frame_it(id);
9081 JavaScriptFrame* frame = frame_it.frame();
9082
9083 // Find the requested scope.
9084 int n = 0;
9085 ScopeIterator it(frame);
9086 for (; !it.Done() && n < index; it.Next()) {
9087 n++;
9088 }
9089 if (it.Done()) {
9090 return Heap::undefined_value();
9091 }
9092
9093 // Calculate the size of the result.
9094 int details_size = kScopeDetailsSize;
9095 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
9096
9097 // Fill in scope details.
9098 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
9099 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
9100
9101 return *Factory::NewJSArrayWithElements(details);
9102}
9103
9104
9105static Object* Runtime_DebugPrintScopes(Arguments args) {
9106 HandleScope scope;
9107 ASSERT(args.length() == 0);
9108
9109#ifdef DEBUG
9110 // Print the scopes for the top frame.
9111 StackFrameLocator locator;
9112 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
9113 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
9114 it.DebugPrint();
9115 }
9116#endif
9117 return Heap::undefined_value();
9118}
9119
9120
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009121static Object* Runtime_GetCFrames(Arguments args) {
9122 HandleScope scope;
9123 ASSERT(args.length() == 1);
9124 Object* result = Runtime_CheckExecutionState(args);
9125 if (result->IsFailure()) return result;
9126
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00009127#if V8_HOST_ARCH_64_BIT
9128 UNIMPLEMENTED();
9129 return Heap::undefined_value();
9130#else
9131
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009132 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009133 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
9134 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009135 if (frames_count == OS::kStackWalkError) {
9136 return Heap::undefined_value();
9137 }
9138
9139 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
9140 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
9141 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
9142 for (int i = 0; i < frames_count; i++) {
9143 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
9144 frame_value->SetProperty(
9145 *address_str,
9146 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
9147 NONE);
9148
9149 // Get the stack walk text for this frame.
9150 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009151 int frame_text_length = StrLength(frames[i].text);
9152 if (frame_text_length > 0) {
9153 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009154 frame_text = Factory::NewStringFromAscii(str);
9155 }
9156
9157 if (!frame_text.is_null()) {
9158 frame_value->SetProperty(*text_str, *frame_text, NONE);
9159 }
9160
9161 frames_array->set(i, *frame_value);
9162 }
9163 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00009164#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009165}
9166
9167
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00009168static Object* Runtime_GetThreadCount(Arguments args) {
9169 HandleScope scope;
9170 ASSERT(args.length() == 1);
9171
9172 // Check arguments.
9173 Object* result = Runtime_CheckExecutionState(args);
9174 if (result->IsFailure()) return result;
9175
9176 // Count all archived V8 threads.
9177 int n = 0;
9178 for (ThreadState* thread = ThreadState::FirstInUse();
9179 thread != NULL;
9180 thread = thread->Next()) {
9181 n++;
9182 }
9183
9184 // Total number of threads is current thread and archived threads.
9185 return Smi::FromInt(n + 1);
9186}
9187
9188
9189static const int kThreadDetailsCurrentThreadIndex = 0;
9190static const int kThreadDetailsThreadIdIndex = 1;
9191static const int kThreadDetailsSize = 2;
9192
9193// Return an array with thread details
9194// args[0]: number: break id
9195// args[1]: number: thread index
9196//
9197// The array returned contains the following information:
9198// 0: Is current thread?
9199// 1: Thread id
9200static Object* Runtime_GetThreadDetails(Arguments args) {
9201 HandleScope scope;
9202 ASSERT(args.length() == 2);
9203
9204 // Check arguments.
9205 Object* check = Runtime_CheckExecutionState(args);
9206 if (check->IsFailure()) return check;
9207 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
9208
9209 // Allocate array for result.
9210 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
9211
9212 // Thread index 0 is current thread.
9213 if (index == 0) {
9214 // Fill the details.
9215 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
9216 details->set(kThreadDetailsThreadIdIndex,
9217 Smi::FromInt(ThreadManager::CurrentId()));
9218 } else {
9219 // Find the thread with the requested index.
9220 int n = 1;
9221 ThreadState* thread = ThreadState::FirstInUse();
9222 while (index != n && thread != NULL) {
9223 thread = thread->Next();
9224 n++;
9225 }
9226 if (thread == NULL) {
9227 return Heap::undefined_value();
9228 }
9229
9230 // Fill the details.
9231 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
9232 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
9233 }
9234
9235 // Convert to JS array and return.
9236 return *Factory::NewJSArrayWithElements(details);
9237}
9238
9239
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009240static Object* Runtime_GetBreakLocations(Arguments args) {
9241 HandleScope scope;
9242 ASSERT(args.length() == 1);
9243
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009244 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9245 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009246 // Find the number of break points
9247 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
9248 if (break_locations->IsUndefined()) return Heap::undefined_value();
9249 // Return array as JS array
9250 return *Factory::NewJSArrayWithElements(
9251 Handle<FixedArray>::cast(break_locations));
9252}
9253
9254
9255// Set a break point in a function
9256// args[0]: function
9257// args[1]: number: break source position (within the function source)
9258// args[2]: number: break point object
9259static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
9260 HandleScope scope;
9261 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009262 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9263 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009264 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9265 RUNTIME_ASSERT(source_position >= 0);
9266 Handle<Object> break_point_object_arg = args.at<Object>(2);
9267
9268 // Set break point.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009269 Debug::SetBreakPoint(shared, break_point_object_arg, &source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009270
lrn@chromium.org32d961d2010-06-30 09:09:34 +00009271 return Smi::FromInt(source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009272}
9273
9274
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009275Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
9276 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009277 // Iterate the heap looking for SharedFunctionInfo generated from the
9278 // script. The inner most SharedFunctionInfo containing the source position
9279 // for the requested break point is found.
9280 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
9281 // which is found is not compiled it is compiled and the heap is iterated
9282 // again as the compilation might create inner functions from the newly
9283 // compiled function and the actual requested break point might be in one of
9284 // these functions.
9285 bool done = false;
9286 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00009287 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009288 Handle<SharedFunctionInfo> target;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009289 while (!done) {
9290 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009291 for (HeapObject* obj = iterator.next();
9292 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009293 if (obj->IsSharedFunctionInfo()) {
9294 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
9295 if (shared->script() == *script) {
9296 // If the SharedFunctionInfo found has the requested script data and
9297 // contains the source position it is a candidate.
9298 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00009299 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009300 start_position = shared->start_position();
9301 }
9302 if (start_position <= position &&
9303 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00009304 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009305 // candidate this is the new candidate.
9306 if (target.is_null()) {
9307 target_start_position = start_position;
9308 target = shared;
9309 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00009310 if (target_start_position == start_position &&
9311 shared->end_position() == target->end_position()) {
9312 // If a top-level function contain only one function
9313 // declartion the source for the top-level and the function is
9314 // the same. In that case prefer the non top-level function.
9315 if (!shared->is_toplevel()) {
9316 target_start_position = start_position;
9317 target = shared;
9318 }
9319 } else if (target_start_position <= start_position &&
9320 shared->end_position() <= target->end_position()) {
9321 // This containment check includes equality as a function inside
9322 // a top-level function can share either start or end position
9323 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009324 target_start_position = start_position;
9325 target = shared;
9326 }
9327 }
9328 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009329 }
9330 }
9331 }
9332
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009333 if (target.is_null()) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009334 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009335 }
9336
9337 // If the candidate found is compiled we are done. NOTE: when lazy
9338 // compilation of inner functions is introduced some additional checking
9339 // needs to be done here to compile inner functions.
9340 done = target->is_compiled();
9341 if (!done) {
9342 // If the candidate is not compiled compile it to reveal any inner
9343 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009344 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009345 }
9346 }
9347
9348 return *target;
9349}
9350
9351
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009352// Changes the state of a break point in a script and returns source position
9353// where break point was set. NOTE: Regarding performance see the NOTE for
9354// GetScriptFromScriptData.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009355// args[0]: script to set break point in
9356// args[1]: number: break source position (within the script source)
9357// args[2]: number: break point object
9358static Object* Runtime_SetScriptBreakPoint(Arguments args) {
9359 HandleScope scope;
9360 ASSERT(args.length() == 3);
9361 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
9362 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9363 RUNTIME_ASSERT(source_position >= 0);
9364 Handle<Object> break_point_object_arg = args.at<Object>(2);
9365
9366 // Get the script from the script wrapper.
9367 RUNTIME_ASSERT(wrapper->value()->IsScript());
9368 Handle<Script> script(Script::cast(wrapper->value()));
9369
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009370 Object* result = Runtime::FindSharedFunctionInfoInScript(
9371 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009372 if (!result->IsUndefined()) {
9373 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
9374 // Find position within function. The script position might be before the
9375 // source position of the first function.
9376 int position;
9377 if (shared->start_position() > source_position) {
9378 position = 0;
9379 } else {
9380 position = source_position - shared->start_position();
9381 }
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009382 Debug::SetBreakPoint(shared, break_point_object_arg, &position);
9383 position += shared->start_position();
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00009384
9385 // The result position may become beyond script source end.
9386 // This is expected when the function is toplevel. This may become
9387 // a problem later when actual position gets converted into line/column.
9388 if (shared->is_toplevel() && position == shared->end_position()) {
9389 position = shared->end_position() - 1;
9390 }
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009391 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009392 }
9393 return Heap::undefined_value();
9394}
9395
9396
9397// Clear a break point
9398// args[0]: number: break point object
9399static Object* Runtime_ClearBreakPoint(Arguments args) {
9400 HandleScope scope;
9401 ASSERT(args.length() == 1);
9402 Handle<Object> break_point_object_arg = args.at<Object>(0);
9403
9404 // Clear break point.
9405 Debug::ClearBreakPoint(break_point_object_arg);
9406
9407 return Heap::undefined_value();
9408}
9409
9410
9411// Change the state of break on exceptions
9412// args[0]: boolean indicating uncaught exceptions
9413// args[1]: boolean indicating on/off
9414static Object* Runtime_ChangeBreakOnException(Arguments args) {
9415 HandleScope scope;
9416 ASSERT(args.length() == 2);
9417 ASSERT(args[0]->IsNumber());
9418 ASSERT(args[1]->IsBoolean());
9419
9420 // Update break point state
9421 ExceptionBreakType type =
9422 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
9423 bool enable = args[1]->ToBoolean()->IsTrue();
9424 Debug::ChangeBreakOnException(type, enable);
9425 return Heap::undefined_value();
9426}
9427
9428
9429// Prepare for stepping
9430// args[0]: break id for checking execution state
9431// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00009432// args[2]: number of times to perform the step, for step out it is the number
9433// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009434static Object* Runtime_PrepareStep(Arguments args) {
9435 HandleScope scope;
9436 ASSERT(args.length() == 3);
9437 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009438 Object* check = Runtime_CheckExecutionState(args);
9439 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009440 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
9441 return Top::Throw(Heap::illegal_argument_symbol());
9442 }
9443
9444 // Get the step action and check validity.
9445 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
9446 if (step_action != StepIn &&
9447 step_action != StepNext &&
9448 step_action != StepOut &&
9449 step_action != StepInMin &&
9450 step_action != StepMin) {
9451 return Top::Throw(Heap::illegal_argument_symbol());
9452 }
9453
9454 // Get the number of steps.
9455 int step_count = NumberToInt32(args[2]);
9456 if (step_count < 1) {
9457 return Top::Throw(Heap::illegal_argument_symbol());
9458 }
9459
ager@chromium.orga1645e22009-09-09 19:27:10 +00009460 // Clear all current stepping setup.
9461 Debug::ClearStepping();
9462
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009463 // Prepare step.
9464 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
9465 return Heap::undefined_value();
9466}
9467
9468
9469// Clear all stepping set by PrepareStep.
9470static Object* Runtime_ClearStepping(Arguments args) {
9471 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009472 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009473 Debug::ClearStepping();
9474 return Heap::undefined_value();
9475}
9476
9477
9478// Creates a copy of the with context chain. The copy of the context chain is
9479// is linked to the function context supplied.
9480static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
9481 Handle<Context> function_context) {
9482 // At the bottom of the chain. Return the function context to link to.
9483 if (context_chain->is_function_context()) {
9484 return function_context;
9485 }
9486
9487 // Recursively copy the with contexts.
9488 Handle<Context> previous(context_chain->previous());
9489 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
9490 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009491 CopyWithContextChain(function_context, previous),
9492 extension,
9493 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009494}
9495
9496
9497// Helper function to find or create the arguments object for
9498// Runtime_DebugEvaluate.
9499static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
9500 Handle<JSFunction> function,
ager@chromium.orgb5737492010-07-15 09:29:43 +00009501 Handle<SerializedScopeInfo> scope_info,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009502 const ScopeInfo<>* sinfo,
9503 Handle<Context> function_context) {
9504 // Try to find the value of 'arguments' to pass as parameter. If it is not
9505 // found (that is the debugged function does not reference 'arguments' and
9506 // does not support eval) then create an 'arguments' object.
9507 int index;
9508 if (sinfo->number_of_stack_slots() > 0) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00009509 index = scope_info->StackSlotIndex(Heap::arguments_symbol());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009510 if (index != -1) {
9511 return Handle<Object>(frame->GetExpression(index));
9512 }
9513 }
9514
9515 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00009516 index = scope_info->ContextSlotIndex(Heap::arguments_symbol(), NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009517 if (index != -1) {
9518 return Handle<Object>(function_context->get(index));
9519 }
9520 }
9521
9522 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009523 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
9524 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009525
9526 AssertNoAllocation no_gc;
9527 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009528 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009529 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009530 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009531 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009532 return arguments;
9533}
9534
9535
9536// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00009537// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009538// extension part has all the parameters and locals of the function on the
9539// stack frame. A function which calls eval with the code to evaluate is then
9540// compiled in this context and called in this context. As this context
9541// replaces the context of the function on the stack frame a new (empty)
9542// function is created as well to be used as the closure for the context.
9543// This function and the context acts as replacements for the function on the
9544// stack frame presenting the same view of the values of parameters and
9545// local variables as if the piece of JavaScript was evaluated at the point
9546// where the function on the stack frame is currently stopped.
9547static Object* Runtime_DebugEvaluate(Arguments args) {
9548 HandleScope scope;
9549
9550 // Check the execution state and decode arguments frame and source to be
9551 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009552 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009553 Object* check_result = Runtime_CheckExecutionState(args);
9554 if (check_result->IsFailure()) return check_result;
9555 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9556 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009557 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
9558
9559 // Handle the processing of break.
9560 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009561
9562 // Get the frame where the debugging is performed.
9563 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9564 JavaScriptFrameIterator it(id);
9565 JavaScriptFrame* frame = it.frame();
9566 Handle<JSFunction> function(JSFunction::cast(frame->function()));
ager@chromium.orgb5737492010-07-15 09:29:43 +00009567 Handle<SerializedScopeInfo> scope_info(function->shared()->scope_info());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009568 ScopeInfo<> sinfo(*scope_info);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009569
9570 // Traverse the saved contexts chain to find the active context for the
9571 // selected frame.
9572 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009573 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009574 save = save->prev();
9575 }
9576 ASSERT(save != NULL);
9577 SaveContext savex;
9578 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009579
9580 // Create the (empty) function replacing the function on the stack frame for
9581 // the purpose of evaluating in the context created below. It is important
9582 // that this function does not describe any parameters and local variables
9583 // in the context. If it does then this will cause problems with the lookup
9584 // in Context::Lookup, where context slots for parameters and local variables
9585 // are looked at before the extension object.
9586 Handle<JSFunction> go_between =
9587 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
9588 go_between->set_context(function->context());
9589#ifdef DEBUG
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009590 ScopeInfo<> go_between_sinfo(go_between->shared()->scope_info());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009591 ASSERT(go_between_sinfo.number_of_parameters() == 0);
9592 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
9593#endif
9594
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009595 // Materialize the content of the local scope into a JSObject.
9596 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009597
9598 // Allocate a new context for the debug evaluation and set the extension
9599 // object build.
9600 Handle<Context> context =
9601 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009602 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009603 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009604 Handle<Context> frame_context(Context::cast(frame->context()));
9605 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009606 context = CopyWithContextChain(frame_context, context);
9607
9608 // Wrap the evaluation statement in a new function compiled in the newly
9609 // created context. The function has one parameter which has to be called
9610 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00009611 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009612 // function(arguments,__source__) {return eval(__source__);}
9613 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00009614 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009615 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009616 Handle<String> function_source =
9617 Factory::NewStringFromAscii(Vector<const char>(source_str,
9618 source_str_length));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009619 Handle<SharedFunctionInfo> shared =
ager@chromium.org381abbb2009-02-25 13:23:22 +00009620 Compiler::CompileEval(function_source,
9621 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00009622 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00009623 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009624 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009625 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009626 Factory::NewFunctionFromSharedFunctionInfo(shared, context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009627
9628 // Invoke the result of the compilation to get the evaluation function.
9629 bool has_pending_exception;
9630 Handle<Object> receiver(frame->receiver());
9631 Handle<Object> evaluation_function =
9632 Execution::Call(compiled_function, receiver, 0, NULL,
9633 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009634 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009635
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009636 Handle<Object> arguments = GetArgumentsObject(frame, function, scope_info,
9637 &sinfo, function_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009638
9639 // Invoke the evaluation function and return the result.
9640 const int argc = 2;
9641 Object** argv[argc] = { arguments.location(),
9642 Handle<Object>::cast(source).location() };
9643 Handle<Object> result =
9644 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
9645 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009646 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009647
9648 // Skip the global proxy as it has no properties and always delegates to the
9649 // real global object.
9650 if (result->IsJSGlobalProxy()) {
9651 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
9652 }
9653
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009654 return *result;
9655}
9656
9657
9658static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
9659 HandleScope scope;
9660
9661 // Check the execution state and decode arguments frame and source to be
9662 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009663 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009664 Object* check_result = Runtime_CheckExecutionState(args);
9665 if (check_result->IsFailure()) return check_result;
9666 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009667 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
9668
9669 // Handle the processing of break.
9670 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009671
9672 // Enter the top context from before the debugger was invoked.
9673 SaveContext save;
9674 SaveContext* top = &save;
9675 while (top != NULL && *top->context() == *Debug::debug_context()) {
9676 top = top->prev();
9677 }
9678 if (top != NULL) {
9679 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009680 }
9681
9682 // Get the global context now set to the top context from before the
9683 // debugger was invoked.
9684 Handle<Context> context = Top::global_context();
9685
9686 // Compile the source to be evaluated.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009687 Handle<SharedFunctionInfo> shared =
9688 Compiler::CompileEval(source,
9689 context,
9690 true,
9691 Compiler::DONT_VALIDATE_JSON);
9692 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009693 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009694 Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
9695 context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009696
9697 // Invoke the result of the compilation to get the evaluation function.
9698 bool has_pending_exception;
9699 Handle<Object> receiver = Top::global();
9700 Handle<Object> result =
9701 Execution::Call(compiled_function, receiver, 0, NULL,
9702 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009703 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009704 return *result;
9705}
9706
9707
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009708static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
9709 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009710 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009711
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009712 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009713 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009714
9715 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009716 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00009717 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
9718 // Get the script wrapper in a local handle before calling GetScriptWrapper,
9719 // because using
9720 // instances->set(i, *GetScriptWrapper(script))
9721 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
9722 // already have deferenced the instances handle.
9723 Handle<JSValue> wrapper = GetScriptWrapper(script);
9724 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009725 }
9726
9727 // Return result as a JS array.
9728 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
9729 Handle<JSArray>::cast(result)->SetContent(*instances);
9730 return *result;
9731}
9732
9733
9734// Helper function used by Runtime_DebugReferencedBy below.
9735static int DebugReferencedBy(JSObject* target,
9736 Object* instance_filter, int max_references,
9737 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009738 JSFunction* arguments_function) {
9739 NoHandleAllocation ha;
9740 AssertNoAllocation no_alloc;
9741
9742 // Iterate the heap.
9743 int count = 0;
9744 JSObject* last = NULL;
9745 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009746 HeapObject* heap_obj = NULL;
9747 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009748 (max_references == 0 || count < max_references)) {
9749 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009750 if (heap_obj->IsJSObject()) {
9751 // Skip context extension objects and argument arrays as these are
9752 // checked in the context of functions using them.
9753 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00009754 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009755 obj->map()->constructor() == arguments_function) {
9756 continue;
9757 }
9758
9759 // Check if the JS object has a reference to the object looked for.
9760 if (obj->ReferencesObject(target)) {
9761 // Check instance filter if supplied. This is normally used to avoid
9762 // references from mirror objects (see Runtime_IsInPrototypeChain).
9763 if (!instance_filter->IsUndefined()) {
9764 Object* V = obj;
9765 while (true) {
9766 Object* prototype = V->GetPrototype();
9767 if (prototype->IsNull()) {
9768 break;
9769 }
9770 if (instance_filter == prototype) {
9771 obj = NULL; // Don't add this object.
9772 break;
9773 }
9774 V = prototype;
9775 }
9776 }
9777
9778 if (obj != NULL) {
9779 // Valid reference found add to instance array if supplied an update
9780 // count.
9781 if (instances != NULL && count < instances_size) {
9782 instances->set(count, obj);
9783 }
9784 last = obj;
9785 count++;
9786 }
9787 }
9788 }
9789 }
9790
9791 // Check for circular reference only. This can happen when the object is only
9792 // referenced from mirrors and has a circular reference in which case the
9793 // object is not really alive and would have been garbage collected if not
9794 // referenced from the mirror.
9795 if (count == 1 && last == target) {
9796 count = 0;
9797 }
9798
9799 // Return the number of referencing objects found.
9800 return count;
9801}
9802
9803
9804// Scan the heap for objects with direct references to an object
9805// args[0]: the object to find references to
9806// args[1]: constructor function for instances to exclude (Mirror)
9807// args[2]: the the maximum number of objects to return
9808static Object* Runtime_DebugReferencedBy(Arguments args) {
9809 ASSERT(args.length() == 3);
9810
9811 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009812 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009813
9814 // Check parameters.
9815 CONVERT_CHECKED(JSObject, target, args[0]);
9816 Object* instance_filter = args[1];
9817 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
9818 instance_filter->IsJSObject());
9819 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
9820 RUNTIME_ASSERT(max_references >= 0);
9821
9822 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009823 JSObject* arguments_boilerplate =
9824 Top::context()->global_context()->arguments_boilerplate();
9825 JSFunction* arguments_function =
9826 JSFunction::cast(arguments_boilerplate->map()->constructor());
9827
9828 // Get the number of referencing objects.
9829 int count;
9830 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009831 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009832
9833 // Allocate an array to hold the result.
9834 Object* object = Heap::AllocateFixedArray(count);
9835 if (object->IsFailure()) return object;
9836 FixedArray* instances = FixedArray::cast(object);
9837
9838 // Fill the referencing objects.
9839 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009840 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009841
9842 // Return result as JS array.
9843 Object* result =
9844 Heap::AllocateJSObject(
9845 Top::context()->global_context()->array_function());
9846 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9847 return result;
9848}
9849
9850
9851// Helper function used by Runtime_DebugConstructedBy below.
9852static int DebugConstructedBy(JSFunction* constructor, int max_references,
9853 FixedArray* instances, int instances_size) {
9854 AssertNoAllocation no_alloc;
9855
9856 // Iterate the heap.
9857 int count = 0;
9858 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009859 HeapObject* heap_obj = NULL;
9860 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009861 (max_references == 0 || count < max_references)) {
9862 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009863 if (heap_obj->IsJSObject()) {
9864 JSObject* obj = JSObject::cast(heap_obj);
9865 if (obj->map()->constructor() == constructor) {
9866 // Valid reference found add to instance array if supplied an update
9867 // count.
9868 if (instances != NULL && count < instances_size) {
9869 instances->set(count, obj);
9870 }
9871 count++;
9872 }
9873 }
9874 }
9875
9876 // Return the number of referencing objects found.
9877 return count;
9878}
9879
9880
9881// Scan the heap for objects constructed by a specific function.
9882// args[0]: the constructor to find instances of
9883// args[1]: the the maximum number of objects to return
9884static Object* Runtime_DebugConstructedBy(Arguments args) {
9885 ASSERT(args.length() == 2);
9886
9887 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009888 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009889
9890 // Check parameters.
9891 CONVERT_CHECKED(JSFunction, constructor, args[0]);
9892 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
9893 RUNTIME_ASSERT(max_references >= 0);
9894
9895 // Get the number of referencing objects.
9896 int count;
9897 count = DebugConstructedBy(constructor, max_references, NULL, 0);
9898
9899 // Allocate an array to hold the result.
9900 Object* object = Heap::AllocateFixedArray(count);
9901 if (object->IsFailure()) return object;
9902 FixedArray* instances = FixedArray::cast(object);
9903
9904 // Fill the referencing objects.
9905 count = DebugConstructedBy(constructor, max_references, instances, count);
9906
9907 // Return result as JS array.
9908 Object* result =
9909 Heap::AllocateJSObject(
9910 Top::context()->global_context()->array_function());
9911 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9912 return result;
9913}
9914
9915
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009916// Find the effective prototype object as returned by __proto__.
9917// args[0]: the object to find the prototype for.
9918static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009919 ASSERT(args.length() == 1);
9920
9921 CONVERT_CHECKED(JSObject, obj, args[0]);
9922
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009923 // Use the __proto__ accessor.
9924 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009925}
9926
9927
9928static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009929 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009930 CPU::DebugBreak();
9931 return Heap::undefined_value();
9932}
9933
9934
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009935static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009936#ifdef DEBUG
9937 HandleScope scope;
9938 ASSERT(args.length() == 1);
9939 // Get the function and make sure it is compiled.
9940 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009941 Handle<SharedFunctionInfo> shared(func->shared());
9942 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009943 return Failure::Exception();
9944 }
9945 func->code()->PrintLn();
9946#endif // DEBUG
9947 return Heap::undefined_value();
9948}
ager@chromium.org9085a012009-05-11 19:22:57 +00009949
9950
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009951static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
9952#ifdef DEBUG
9953 HandleScope scope;
9954 ASSERT(args.length() == 1);
9955 // Get the function and make sure it is compiled.
9956 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009957 Handle<SharedFunctionInfo> shared(func->shared());
9958 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009959 return Failure::Exception();
9960 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009961 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009962#endif // DEBUG
9963 return Heap::undefined_value();
9964}
9965
9966
ager@chromium.org9085a012009-05-11 19:22:57 +00009967static Object* Runtime_FunctionGetInferredName(Arguments args) {
9968 NoHandleAllocation ha;
9969 ASSERT(args.length() == 1);
9970
9971 CONVERT_CHECKED(JSFunction, f, args[0]);
9972 return f->shared()->inferred_name();
9973}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009974
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009975
9976static int FindSharedFunctionInfosForScript(Script* script,
9977 FixedArray* buffer) {
9978 AssertNoAllocation no_allocations;
9979
9980 int counter = 0;
9981 int buffer_size = buffer->length();
9982 HeapIterator iterator;
9983 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
9984 ASSERT(obj != NULL);
9985 if (!obj->IsSharedFunctionInfo()) {
9986 continue;
9987 }
9988 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
9989 if (shared->script() != script) {
9990 continue;
9991 }
9992 if (counter < buffer_size) {
9993 buffer->set(counter, shared);
9994 }
9995 counter++;
9996 }
9997 return counter;
9998}
9999
10000// For a script finds all SharedFunctionInfo's in the heap that points
10001// to this script. Returns JSArray of SharedFunctionInfo wrapped
10002// in OpaqueReferences.
10003static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
10004 Arguments args) {
10005 ASSERT(args.length() == 1);
10006 HandleScope scope;
10007 CONVERT_CHECKED(JSValue, script_value, args[0]);
10008
10009 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
10010
10011 const int kBufferSize = 32;
10012
10013 Handle<FixedArray> array;
10014 array = Factory::NewFixedArray(kBufferSize);
10015 int number = FindSharedFunctionInfosForScript(*script, *array);
10016 if (number > kBufferSize) {
10017 array = Factory::NewFixedArray(number);
10018 FindSharedFunctionInfosForScript(*script, *array);
10019 }
10020
10021 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
10022 result->set_length(Smi::FromInt(number));
10023
10024 LiveEdit::WrapSharedFunctionInfos(result);
10025
10026 return *result;
10027}
10028
10029// For a script calculates compilation information about all its functions.
10030// The script source is explicitly specified by the second argument.
10031// The source of the actual script is not used, however it is important that
10032// all generated code keeps references to this particular instance of script.
10033// Returns a JSArray of compilation infos. The array is ordered so that
10034// each function with all its descendant is always stored in a continues range
10035// with the function itself going first. The root function is a script function.
10036static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
10037 ASSERT(args.length() == 2);
10038 HandleScope scope;
10039 CONVERT_CHECKED(JSValue, script, args[0]);
10040 CONVERT_ARG_CHECKED(String, source, 1);
10041 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
10042
10043 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
10044
10045 if (Top::has_pending_exception()) {
10046 return Failure::Exception();
10047 }
10048
10049 return result;
10050}
10051
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010052// Changes the source of the script to a new_source.
10053// If old_script_name is provided (i.e. is a String), also creates a copy of
10054// the script with its original source and sends notification to debugger.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010055static Object* Runtime_LiveEditReplaceScript(Arguments args) {
10056 ASSERT(args.length() == 3);
10057 HandleScope scope;
10058 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
10059 CONVERT_ARG_CHECKED(String, new_source, 1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010060 Handle<Object> old_script_name(args[2]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010061
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010062 CONVERT_CHECKED(Script, original_script_pointer,
10063 original_script_value->value());
10064 Handle<Script> original_script(original_script_pointer);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010065
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010066 Object* old_script = LiveEdit::ChangeScriptSource(original_script,
10067 new_source,
10068 old_script_name);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010069
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010070 if (old_script->IsScript()) {
10071 Handle<Script> script_handle(Script::cast(old_script));
10072 return *(GetScriptWrapper(script_handle));
10073 } else {
10074 return Heap::null_value();
10075 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010076}
10077
10078// Replaces code of SharedFunctionInfo with a new one.
10079static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
10080 ASSERT(args.length() == 2);
10081 HandleScope scope;
10082 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
10083 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
10084
ager@chromium.orgac091b72010-05-05 07:34:42 +000010085 return LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010086}
10087
10088// Connects SharedFunctionInfo to another script.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010089static Object* Runtime_LiveEditFunctionSetScript(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010090 ASSERT(args.length() == 2);
10091 HandleScope scope;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010092 Handle<Object> function_object(args[0]);
10093 Handle<Object> script_object(args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010094
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010095 if (function_object->IsJSValue()) {
10096 Handle<JSValue> function_wrapper = Handle<JSValue>::cast(function_object);
10097 if (script_object->IsJSValue()) {
10098 CONVERT_CHECKED(Script, script, JSValue::cast(*script_object)->value());
10099 script_object = Handle<Object>(script);
10100 }
10101
10102 LiveEdit::SetFunctionScript(function_wrapper, script_object);
10103 } else {
10104 // Just ignore this. We may not have a SharedFunctionInfo for some functions
10105 // and we check it in this function.
10106 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010107
10108 return Heap::undefined_value();
10109}
10110
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010111
10112// In a code of a parent function replaces original function as embedded object
10113// with a substitution one.
10114static Object* Runtime_LiveEditReplaceRefToNestedFunction(Arguments args) {
10115 ASSERT(args.length() == 3);
10116 HandleScope scope;
10117
10118 CONVERT_ARG_CHECKED(JSValue, parent_wrapper, 0);
10119 CONVERT_ARG_CHECKED(JSValue, orig_wrapper, 1);
10120 CONVERT_ARG_CHECKED(JSValue, subst_wrapper, 2);
10121
10122 LiveEdit::ReplaceRefToNestedFunction(parent_wrapper, orig_wrapper,
10123 subst_wrapper);
10124
10125 return Heap::undefined_value();
10126}
10127
10128
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010129// Updates positions of a shared function info (first parameter) according
10130// to script source change. Text change is described in second parameter as
10131// array of groups of 3 numbers:
10132// (change_begin, change_end, change_end_new_position).
10133// Each group describes a change in text; groups are sorted by change_begin.
10134static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
10135 ASSERT(args.length() == 2);
10136 HandleScope scope;
10137 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
10138 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
10139
ager@chromium.orgac091b72010-05-05 07:34:42 +000010140 return LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010141}
10142
10143
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010144// For array of SharedFunctionInfo's (each wrapped in JSValue)
10145// checks that none of them have activations on stacks (of any thread).
10146// Returns array of the same length with corresponding results of
10147// LiveEdit::FunctionPatchabilityStatus type.
ager@chromium.org357bf652010-04-12 11:30:10 +000010148static Object* Runtime_LiveEditCheckAndDropActivations(Arguments args) {
10149 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010150 HandleScope scope;
10151 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +000010152 CONVERT_BOOLEAN_CHECKED(do_drop, args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010153
ager@chromium.org357bf652010-04-12 11:30:10 +000010154 return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010155}
10156
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010157// Compares 2 strings line-by-line and returns diff in form of JSArray of
fschneider@chromium.org013f3e12010-04-26 13:27:52 +000010158// triplets (pos1, pos1_end, pos2_end) describing list of diff chunks.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010159static Object* Runtime_LiveEditCompareStringsLinewise(Arguments args) {
10160 ASSERT(args.length() == 2);
10161 HandleScope scope;
10162 CONVERT_ARG_CHECKED(String, s1, 0);
10163 CONVERT_ARG_CHECKED(String, s2, 1);
10164
10165 return *LiveEdit::CompareStringsLinewise(s1, s2);
10166}
10167
10168
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010169
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010170// A testing entry. Returns statement position which is the closest to
10171// source_position.
10172static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
10173 ASSERT(args.length() == 2);
10174 HandleScope scope;
10175 CONVERT_ARG_CHECKED(JSFunction, function, 0);
10176 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
10177
10178 Handle<Code> code(function->code());
10179
10180 RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
10181 int closest_pc = 0;
10182 int distance = kMaxInt;
10183 while (!it.done()) {
10184 int statement_position = static_cast<int>(it.rinfo()->data());
10185 // Check if this break point is closer that what was previously found.
10186 if (source_position <= statement_position &&
10187 statement_position - source_position < distance) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +000010188 closest_pc =
10189 static_cast<int>(it.rinfo()->pc() - code->instruction_start());
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010190 distance = statement_position - source_position;
10191 // Check whether we can't get any closer.
10192 if (distance == 0) break;
10193 }
10194 it.next();
10195 }
10196
10197 return Smi::FromInt(closest_pc);
10198}
10199
10200
ager@chromium.org357bf652010-04-12 11:30:10 +000010201// Calls specified function with or without entering the debugger.
10202// This is used in unit tests to run code as if debugger is entered or simply
10203// to have a stack with C++ frame in the middle.
10204static Object* Runtime_ExecuteInDebugContext(Arguments args) {
10205 ASSERT(args.length() == 2);
10206 HandleScope scope;
10207 CONVERT_ARG_CHECKED(JSFunction, function, 0);
10208 CONVERT_BOOLEAN_CHECKED(without_debugger, args[1]);
10209
10210 Handle<Object> result;
10211 bool pending_exception;
10212 {
10213 if (without_debugger) {
10214 result = Execution::Call(function, Top::global(), 0, NULL,
10215 &pending_exception);
10216 } else {
10217 EnterDebugger enter_debugger;
10218 result = Execution::Call(function, Top::global(), 0, NULL,
10219 &pending_exception);
10220 }
10221 }
10222 if (!pending_exception) {
10223 return *result;
10224 } else {
10225 return Failure::Exception();
10226 }
10227}
10228
10229
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010230#endif // ENABLE_DEBUGGER_SUPPORT
10231
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010232#ifdef ENABLE_LOGGING_AND_PROFILING
10233
10234static Object* Runtime_ProfilerResume(Arguments args) {
10235 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010236 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010237
10238 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010239 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10240 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010241 return Heap::undefined_value();
10242}
10243
10244
10245static Object* Runtime_ProfilerPause(Arguments args) {
10246 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010247 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010248
10249 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010250 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10251 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010252 return Heap::undefined_value();
10253}
10254
10255#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010256
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010257// Finds the script object from the script data. NOTE: This operation uses
10258// heap traversal to find the function generated for the source position
10259// for the requested break point. For lazily compiled functions several heap
10260// traversals might be required rendering this operation as a rather slow
10261// operation. However for setting break points which is normally done through
10262// some kind of user interaction the performance is not crucial.
10263static Handle<Object> Runtime_GetScriptFromScriptName(
10264 Handle<String> script_name) {
10265 // Scan the heap for Script objects to find the script with the requested
10266 // script data.
10267 Handle<Script> script;
10268 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010269 HeapObject* obj = NULL;
10270 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010271 // If a script is found check if it has the script data requested.
10272 if (obj->IsScript()) {
10273 if (Script::cast(obj)->name()->IsString()) {
10274 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
10275 script = Handle<Script>(Script::cast(obj));
10276 }
10277 }
10278 }
10279 }
10280
10281 // If no script with the requested script data is found return undefined.
10282 if (script.is_null()) return Factory::undefined_value();
10283
10284 // Return the script found.
10285 return GetScriptWrapper(script);
10286}
10287
10288
10289// Get the script object from script data. NOTE: Regarding performance
10290// see the NOTE for GetScriptFromScriptData.
10291// args[0]: script data for the script to find the source for
10292static Object* Runtime_GetScript(Arguments args) {
10293 HandleScope scope;
10294
10295 ASSERT(args.length() == 1);
10296
10297 CONVERT_CHECKED(String, script_name, args[0]);
10298
10299 // Find the requested script.
10300 Handle<Object> result =
10301 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
10302 return *result;
10303}
10304
10305
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010306// Determines whether the given stack frame should be displayed in
10307// a stack trace. The caller is the error constructor that asked
10308// for the stack trace to be collected. The first time a construct
10309// call to this function is encountered it is skipped. The seen_caller
10310// in/out parameter is used to remember if the caller has been seen
10311// yet.
10312static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
10313 bool* seen_caller) {
10314 // Only display JS frames.
10315 if (!raw_frame->is_java_script())
10316 return false;
10317 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
10318 Object* raw_fun = frame->function();
10319 // Not sure when this can happen but skip it just in case.
10320 if (!raw_fun->IsJSFunction())
10321 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010322 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010323 *seen_caller = true;
10324 return false;
10325 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010326 // Skip all frames until we've seen the caller. Also, skip the most
10327 // obvious builtin calls. Some builtin calls (such as Number.ADD
10328 // which is invoked using 'call') are very difficult to recognize
10329 // so we're leaving them in for now.
10330 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010331}
10332
10333
10334// Collect the raw data for a stack trace. Returns an array of three
10335// element segments each containing a receiver, function and native
10336// code offset.
10337static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010338 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010339 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010340 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
10341
10342 HandleScope scope;
10343
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000010344 limit = Max(limit, 0); // Ensure that limit is not negative.
10345 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010346 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010347
10348 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010349 // If the caller parameter is a function we skip frames until we're
10350 // under it before starting to collect.
10351 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010352 int cursor = 0;
10353 int frames_seen = 0;
10354 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010355 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010356 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010357 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010358 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010359 Object* recv = frame->receiver();
10360 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010361 Address pc = frame->pc();
10362 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010363 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010364 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010365 if (cursor + 2 < elements->length()) {
10366 elements->set(cursor++, recv);
10367 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010368 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010369 } else {
10370 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010371 Handle<Object> recv_handle(recv);
10372 Handle<Object> fun_handle(fun);
10373 SetElement(result, cursor++, recv_handle);
10374 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010375 SetElement(result, cursor++, Handle<Smi>(offset));
10376 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010377 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010378 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010379 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010380
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010381 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010382 return *result;
10383}
10384
10385
ager@chromium.org3811b432009-10-28 14:53:37 +000010386// Returns V8 version as a string.
10387static Object* Runtime_GetV8Version(Arguments args) {
10388 ASSERT_EQ(args.length(), 0);
10389
10390 NoHandleAllocation ha;
10391
10392 const char* version_string = v8::V8::GetVersion();
10393
10394 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
10395}
10396
10397
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010398static Object* Runtime_Abort(Arguments args) {
10399 ASSERT(args.length() == 2);
10400 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
10401 Smi::cast(args[1])->value());
10402 Top::PrintStack();
10403 OS::Abort();
10404 UNREACHABLE();
10405 return NULL;
10406}
10407
10408
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010409static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
10410 ASSERT(args.length() == 0);
10411 HandleScope::DeleteExtensions();
10412 return Heap::undefined_value();
10413}
10414
10415
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010416static Object* CacheMiss(FixedArray* cache_obj, int index, Object* key_obj) {
10417 ASSERT(index % 2 == 0); // index of the key
10418 ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
10419 ASSERT(index < cache_obj->length());
10420
10421 HandleScope scope;
10422
10423 Handle<FixedArray> cache(cache_obj);
10424 Handle<Object> key(key_obj);
10425 Handle<JSFunction> factory(JSFunction::cast(
10426 cache->get(JSFunctionResultCache::kFactoryIndex)));
10427 // TODO(antonm): consider passing a receiver when constructing a cache.
10428 Handle<Object> receiver(Top::global_context()->global());
10429
10430 Handle<Object> value;
10431 {
10432 // This handle is nor shared, nor used later, so it's safe.
10433 Object** argv[] = { key.location() };
10434 bool pending_exception = false;
10435 value = Execution::Call(factory,
10436 receiver,
10437 1,
10438 argv,
10439 &pending_exception);
10440 if (pending_exception) return Failure::Exception();
10441 }
10442
10443 cache->set(index, *key);
10444 cache->set(index + 1, *value);
10445 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(index));
10446
10447 return *value;
10448}
10449
10450
10451static Object* Runtime_GetFromCache(Arguments args) {
10452 // This is only called from codegen, so checks might be more lax.
10453 CONVERT_CHECKED(FixedArray, cache, args[0]);
10454 Object* key = args[1];
10455
10456 const int finger_index =
10457 Smi::cast(cache->get(JSFunctionResultCache::kFingerIndex))->value();
10458
10459 Object* o = cache->get(finger_index);
10460 if (o == key) {
10461 // The fastest case: hit the same place again.
10462 return cache->get(finger_index + 1);
10463 }
10464
10465 for (int i = finger_index - 2;
10466 i >= JSFunctionResultCache::kEntriesIndex;
10467 i -= 2) {
10468 o = cache->get(i);
10469 if (o == key) {
10470 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10471 return cache->get(i + 1);
10472 }
10473 }
10474
10475 const int size =
10476 Smi::cast(cache->get(JSFunctionResultCache::kCacheSizeIndex))->value();
10477 ASSERT(size <= cache->length());
10478
10479 for (int i = size - 2; i > finger_index; i -= 2) {
10480 o = cache->get(i);
10481 if (o == key) {
10482 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10483 return cache->get(i + 1);
10484 }
10485 }
10486
10487 // Cache miss. If we have spare room, put new data into it, otherwise
10488 // evict post finger entry which must be least recently used.
10489 if (size < cache->length()) {
10490 cache->set(JSFunctionResultCache::kCacheSizeIndex, Smi::FromInt(size + 2));
10491 return CacheMiss(cache, size, key);
10492 } else {
antonm@chromium.org397e23c2010-04-21 12:00:05 +000010493 int target_index = finger_index + JSFunctionResultCache::kEntrySize;
10494 if (target_index == cache->length()) {
10495 target_index = JSFunctionResultCache::kEntriesIndex;
10496 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010497 return CacheMiss(cache, target_index, key);
10498 }
10499}
10500
kasper.lund44510672008-07-25 07:37:58 +000010501#ifdef DEBUG
10502// ListNatives is ONLY used by the fuzz-natives.js in debug mode
10503// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010504static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +000010505 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010506 HandleScope scope;
10507 Handle<JSArray> result = Factory::NewJSArray(0);
10508 int index = 0;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010509 bool inline_runtime_functions = false;
ager@chromium.orga1645e22009-09-09 19:27:10 +000010510#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010511 { \
10512 HandleScope inner; \
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010513 Handle<String> name; \
10514 /* Inline runtime functions have an underscore in front of the name. */ \
10515 if (inline_runtime_functions) { \
10516 name = Factory::NewStringFromAscii( \
10517 Vector<const char>("_" #Name, StrLength("_" #Name))); \
10518 } else { \
10519 name = Factory::NewStringFromAscii( \
10520 Vector<const char>(#Name, StrLength(#Name))); \
10521 } \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010522 Handle<JSArray> pair = Factory::NewJSArray(0); \
10523 SetElement(pair, 0, name); \
10524 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
10525 SetElement(result, index++, pair); \
10526 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010527 inline_runtime_functions = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010528 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010529 inline_runtime_functions = true;
10530 INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010531#undef ADD_ENTRY
10532 return *result;
10533}
kasper.lund44510672008-07-25 07:37:58 +000010534#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010535
10536
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010537static Object* Runtime_Log(Arguments args) {
10538 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +000010539 CONVERT_CHECKED(String, format, args[0]);
10540 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010541 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010542 Logger::LogRuntime(chars, elms);
10543 return Heap::undefined_value();
10544}
10545
10546
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010547static Object* Runtime_IS_VAR(Arguments args) {
10548 UNREACHABLE(); // implemented as macro in the parser
10549 return NULL;
10550}
10551
10552
10553// ----------------------------------------------------------------------------
10554// Implementation of Runtime
10555
ager@chromium.orga1645e22009-09-09 19:27:10 +000010556#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010557 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +000010558 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010559
10560static Runtime::Function Runtime_functions[] = {
10561 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010562 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010563};
10564
10565#undef F
10566
10567
10568Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
10569 ASSERT(0 <= fid && fid < kNofFunctions);
10570 return &Runtime_functions[fid];
10571}
10572
10573
10574Runtime::Function* Runtime::FunctionForName(const char* name) {
10575 for (Function* f = Runtime_functions; f->name != NULL; f++) {
10576 if (strcmp(f->name, name) == 0) {
10577 return f;
10578 }
10579 }
10580 return NULL;
10581}
10582
10583
10584void Runtime::PerformGC(Object* result) {
10585 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010586 if (failure->IsRetryAfterGC()) {
10587 // Try to do a garbage collection; ignore it if it fails. The C
10588 // entry stub will throw an out-of-memory exception in that case.
10589 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
10590 } else {
10591 // Handle last resort GC and make sure to allow future allocations
10592 // to grow the heap without causing GCs (if possible).
10593 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +000010594 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010595 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010596}
10597
10598
10599} } // namespace v8::internal