blob: fc6ca762f1495c98d7c29a6e24b6bb03ea995407 [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;
vegorov@chromium.org26c16f82010-08-11 13:41:03 +0000308 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));
312 result = SetProperty(boilerplate, name, value, NONE);
313 } 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.
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001630 target->shared()->set_code(shared->code());
1631 target->set_code(shared->code());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00001632 target->shared()->set_scope_info(shared->scope_info());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001633 target->shared()->set_length(shared->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001634 target->shared()->set_formal_parameter_count(
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001635 shared->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001636 // Set the source code of the target function to undefined.
1637 // SetCode is only used for built-in constructors like String,
1638 // Array, and Object, and some web code
1639 // doesn't like seeing source code for constructors.
1640 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001641 // Clear the optimization hints related to the compiled code as these are no
1642 // longer valid when the code is overwritten.
1643 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001644 context = Handle<Context>(fun->context());
1645
1646 // Make sure we get a fresh copy of the literal vector to avoid
1647 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001648 int number_of_literals = fun->NumberOfLiterals();
1649 Handle<FixedArray> literals =
1650 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001651 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001652 // Insert the object, regexp and array functions in the literals
1653 // array prefix. These are the functions that will be used when
1654 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001655 literals->set(JSFunction::kLiteralGlobalContextIndex,
1656 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001657 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001658 // It's okay to skip the write barrier here because the literals
1659 // are guaranteed to be in old space.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001660 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001661 }
1662
1663 target->set_context(*context);
1664 return *target;
1665}
1666
1667
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001668static Object* CharFromCode(Object* char_code) {
1669 uint32_t code;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001670 if (char_code->ToArrayIndex(&code)) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001671 if (code <= 0xffff) {
1672 return Heap::LookupSingleCharacterStringFromCode(code);
1673 }
1674 }
1675 return Heap::empty_string();
1676}
1677
1678
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001679static Object* Runtime_StringCharCodeAt(Arguments args) {
1680 NoHandleAllocation ha;
1681 ASSERT(args.length() == 2);
1682
1683 CONVERT_CHECKED(String, subject, args[0]);
1684 Object* index = args[1];
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001685 RUNTIME_ASSERT(index->IsNumber());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001686
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001687 uint32_t i = 0;
1688 if (index->IsSmi()) {
1689 int value = Smi::cast(index)->value();
1690 if (value < 0) return Heap::nan_value();
1691 i = value;
1692 } else {
1693 ASSERT(index->IsHeapNumber());
1694 double value = HeapNumber::cast(index)->value();
1695 i = static_cast<uint32_t>(DoubleToInteger(value));
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001696 }
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001697
1698 // Flatten the string. If someone wants to get a char at an index
1699 // in a cons string, it is likely that more indices will be
1700 // accessed.
1701 Object* flat = subject->TryFlatten();
1702 if (flat->IsFailure()) return flat;
1703 subject = String::cast(flat);
1704
1705 if (i >= static_cast<uint32_t>(subject->length())) {
1706 return Heap::nan_value();
1707 }
1708
1709 return Smi::FromInt(subject->Get(i));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001710}
1711
1712
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001713static Object* Runtime_CharFromCode(Arguments args) {
1714 NoHandleAllocation ha;
1715 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001716 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001717}
1718
lrn@chromium.org25156de2010-04-06 13:10:27 +00001719
1720class FixedArrayBuilder {
1721 public:
1722 explicit FixedArrayBuilder(int initial_capacity)
1723 : array_(Factory::NewFixedArrayWithHoles(initial_capacity)),
1724 length_(0) {
1725 // Require a non-zero initial size. Ensures that doubling the size to
1726 // extend the array will work.
1727 ASSERT(initial_capacity > 0);
1728 }
1729
1730 explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
1731 : array_(backing_store),
1732 length_(0) {
1733 // Require a non-zero initial size. Ensures that doubling the size to
1734 // extend the array will work.
1735 ASSERT(backing_store->length() > 0);
1736 }
1737
1738 bool HasCapacity(int elements) {
1739 int length = array_->length();
1740 int required_length = length_ + elements;
1741 return (length >= required_length);
1742 }
1743
1744 void EnsureCapacity(int elements) {
1745 int length = array_->length();
1746 int required_length = length_ + elements;
1747 if (length < required_length) {
1748 int new_length = length;
1749 do {
1750 new_length *= 2;
1751 } while (new_length < required_length);
1752 Handle<FixedArray> extended_array =
1753 Factory::NewFixedArrayWithHoles(new_length);
1754 array_->CopyTo(0, *extended_array, 0, length_);
1755 array_ = extended_array;
1756 }
1757 }
1758
1759 void Add(Object* value) {
1760 ASSERT(length_ < capacity());
1761 array_->set(length_, value);
1762 length_++;
1763 }
1764
1765 void Add(Smi* value) {
1766 ASSERT(length_ < capacity());
1767 array_->set(length_, value);
1768 length_++;
1769 }
1770
1771 Handle<FixedArray> array() {
1772 return array_;
1773 }
1774
1775 int length() {
1776 return length_;
1777 }
1778
1779 int capacity() {
1780 return array_->length();
1781 }
1782
1783 Handle<JSArray> ToJSArray() {
1784 Handle<JSArray> result_array = Factory::NewJSArrayWithElements(array_);
1785 result_array->set_length(Smi::FromInt(length_));
1786 return result_array;
1787 }
1788
1789 Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
1790 target_array->set_elements(*array_);
1791 target_array->set_length(Smi::FromInt(length_));
1792 return target_array;
1793 }
1794
1795 private:
1796 Handle<FixedArray> array_;
1797 int length_;
1798};
1799
1800
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001801// Forward declarations.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001802const int kStringBuilderConcatHelperLengthBits = 11;
1803const int kStringBuilderConcatHelperPositionBits = 19;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001804
1805template <typename schar>
1806static inline void StringBuilderConcatHelper(String*,
1807 schar*,
1808 FixedArray*,
1809 int);
1810
lrn@chromium.org25156de2010-04-06 13:10:27 +00001811typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
1812 StringBuilderSubstringLength;
1813typedef BitField<int,
1814 kStringBuilderConcatHelperLengthBits,
1815 kStringBuilderConcatHelperPositionBits>
1816 StringBuilderSubstringPosition;
1817
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001818
1819class ReplacementStringBuilder {
1820 public:
1821 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
lrn@chromium.org25156de2010-04-06 13:10:27 +00001822 : array_builder_(estimated_part_count),
1823 subject_(subject),
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001824 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001825 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001826 // Require a non-zero initial size. Ensures that doubling the size to
1827 // extend the array will work.
1828 ASSERT(estimated_part_count > 0);
1829 }
1830
lrn@chromium.org25156de2010-04-06 13:10:27 +00001831 static inline void AddSubjectSlice(FixedArrayBuilder* builder,
1832 int from,
1833 int to) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001834 ASSERT(from >= 0);
1835 int length = to - from;
1836 ASSERT(length > 0);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001837 if (StringBuilderSubstringLength::is_valid(length) &&
1838 StringBuilderSubstringPosition::is_valid(from)) {
1839 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1840 StringBuilderSubstringPosition::encode(from);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001841 builder->Add(Smi::FromInt(encoded_slice));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001842 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001843 // Otherwise encode as two smis.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001844 builder->Add(Smi::FromInt(-length));
1845 builder->Add(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001846 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00001847 }
1848
1849
1850 void EnsureCapacity(int elements) {
1851 array_builder_.EnsureCapacity(elements);
1852 }
1853
1854
1855 void AddSubjectSlice(int from, int to) {
1856 AddSubjectSlice(&array_builder_, from, to);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001857 IncrementCharacterCount(to - from);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001858 }
1859
1860
1861 void AddString(Handle<String> string) {
1862 int length = string->length();
1863 ASSERT(length > 0);
1864 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001865 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001866 is_ascii_ = false;
1867 }
1868 IncrementCharacterCount(length);
1869 }
1870
1871
1872 Handle<String> ToString() {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001873 if (array_builder_.length() == 0) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001874 return Factory::empty_string();
1875 }
1876
1877 Handle<String> joined_string;
1878 if (is_ascii_) {
1879 joined_string = NewRawAsciiString(character_count_);
1880 AssertNoAllocation no_alloc;
1881 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1882 char* char_buffer = seq->GetChars();
1883 StringBuilderConcatHelper(*subject_,
1884 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001885 *array_builder_.array(),
1886 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001887 } else {
1888 // Non-ASCII.
1889 joined_string = NewRawTwoByteString(character_count_);
1890 AssertNoAllocation no_alloc;
1891 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1892 uc16* char_buffer = seq->GetChars();
1893 StringBuilderConcatHelper(*subject_,
1894 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001895 *array_builder_.array(),
1896 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001897 }
1898 return joined_string;
1899 }
1900
1901
1902 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001903 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001904 V8::FatalProcessOutOfMemory("String.replace result too large.");
1905 }
1906 character_count_ += by;
1907 }
1908
lrn@chromium.org25156de2010-04-06 13:10:27 +00001909 Handle<JSArray> GetParts() {
1910 Handle<JSArray> result =
1911 Factory::NewJSArrayWithElements(array_builder_.array());
1912 result->set_length(Smi::FromInt(array_builder_.length()));
1913 return result;
1914 }
kmillikin@chromium.orgd9825192010-03-30 08:36:16 +00001915
lrn@chromium.org25156de2010-04-06 13:10:27 +00001916 private:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001917 Handle<String> NewRawAsciiString(int size) {
1918 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1919 }
1920
1921
1922 Handle<String> NewRawTwoByteString(int size) {
1923 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1924 }
1925
1926
1927 void AddElement(Object* element) {
1928 ASSERT(element->IsSmi() || element->IsString());
lrn@chromium.org25156de2010-04-06 13:10:27 +00001929 ASSERT(array_builder_.capacity() > array_builder_.length());
1930 array_builder_.Add(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001931 }
1932
lrn@chromium.org25156de2010-04-06 13:10:27 +00001933 FixedArrayBuilder array_builder_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001934 Handle<String> subject_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001935 int character_count_;
1936 bool is_ascii_;
1937};
1938
1939
1940class CompiledReplacement {
1941 public:
1942 CompiledReplacement()
1943 : parts_(1), replacement_substrings_(0) {}
1944
1945 void Compile(Handle<String> replacement,
1946 int capture_count,
1947 int subject_length);
1948
1949 void Apply(ReplacementStringBuilder* builder,
1950 int match_from,
1951 int match_to,
1952 Handle<JSArray> last_match_info);
1953
1954 // Number of distinct parts of the replacement pattern.
1955 int parts() {
1956 return parts_.length();
1957 }
1958 private:
1959 enum PartType {
1960 SUBJECT_PREFIX = 1,
1961 SUBJECT_SUFFIX,
1962 SUBJECT_CAPTURE,
1963 REPLACEMENT_SUBSTRING,
1964 REPLACEMENT_STRING,
1965
1966 NUMBER_OF_PART_TYPES
1967 };
1968
1969 struct ReplacementPart {
1970 static inline ReplacementPart SubjectMatch() {
1971 return ReplacementPart(SUBJECT_CAPTURE, 0);
1972 }
1973 static inline ReplacementPart SubjectCapture(int capture_index) {
1974 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1975 }
1976 static inline ReplacementPart SubjectPrefix() {
1977 return ReplacementPart(SUBJECT_PREFIX, 0);
1978 }
1979 static inline ReplacementPart SubjectSuffix(int subject_length) {
1980 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1981 }
1982 static inline ReplacementPart ReplacementString() {
1983 return ReplacementPart(REPLACEMENT_STRING, 0);
1984 }
1985 static inline ReplacementPart ReplacementSubString(int from, int to) {
1986 ASSERT(from >= 0);
1987 ASSERT(to > from);
1988 return ReplacementPart(-from, to);
1989 }
1990
1991 // If tag <= 0 then it is the negation of a start index of a substring of
1992 // the replacement pattern, otherwise it's a value from PartType.
1993 ReplacementPart(int tag, int data)
1994 : tag(tag), data(data) {
1995 // Must be non-positive or a PartType value.
1996 ASSERT(tag < NUMBER_OF_PART_TYPES);
1997 }
1998 // Either a value of PartType or a non-positive number that is
1999 // the negation of an index into the replacement string.
2000 int tag;
2001 // The data value's interpretation depends on the value of tag:
2002 // tag == SUBJECT_PREFIX ||
2003 // tag == SUBJECT_SUFFIX: data is unused.
2004 // tag == SUBJECT_CAPTURE: data is the number of the capture.
2005 // tag == REPLACEMENT_SUBSTRING ||
2006 // tag == REPLACEMENT_STRING: data is index into array of substrings
2007 // of the replacement string.
2008 // tag <= 0: Temporary representation of the substring of the replacement
2009 // string ranging over -tag .. data.
2010 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
2011 // substring objects.
2012 int data;
2013 };
2014
2015 template<typename Char>
2016 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
2017 Vector<Char> characters,
2018 int capture_count,
2019 int subject_length) {
2020 int length = characters.length();
2021 int last = 0;
2022 for (int i = 0; i < length; i++) {
2023 Char c = characters[i];
2024 if (c == '$') {
2025 int next_index = i + 1;
2026 if (next_index == length) { // No next character!
2027 break;
2028 }
2029 Char c2 = characters[next_index];
2030 switch (c2) {
2031 case '$':
2032 if (i > last) {
2033 // There is a substring before. Include the first "$".
2034 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
2035 last = next_index + 1; // Continue after the second "$".
2036 } else {
2037 // Let the next substring start with the second "$".
2038 last = next_index;
2039 }
2040 i = next_index;
2041 break;
2042 case '`':
2043 if (i > last) {
2044 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2045 }
2046 parts->Add(ReplacementPart::SubjectPrefix());
2047 i = next_index;
2048 last = i + 1;
2049 break;
2050 case '\'':
2051 if (i > last) {
2052 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2053 }
2054 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
2055 i = next_index;
2056 last = i + 1;
2057 break;
2058 case '&':
2059 if (i > last) {
2060 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2061 }
2062 parts->Add(ReplacementPart::SubjectMatch());
2063 i = next_index;
2064 last = i + 1;
2065 break;
2066 case '0':
2067 case '1':
2068 case '2':
2069 case '3':
2070 case '4':
2071 case '5':
2072 case '6':
2073 case '7':
2074 case '8':
2075 case '9': {
2076 int capture_ref = c2 - '0';
2077 if (capture_ref > capture_count) {
2078 i = next_index;
2079 continue;
2080 }
2081 int second_digit_index = next_index + 1;
2082 if (second_digit_index < length) {
2083 // Peek ahead to see if we have two digits.
2084 Char c3 = characters[second_digit_index];
2085 if ('0' <= c3 && c3 <= '9') { // Double digits.
2086 int double_digit_ref = capture_ref * 10 + c3 - '0';
2087 if (double_digit_ref <= capture_count) {
2088 next_index = second_digit_index;
2089 capture_ref = double_digit_ref;
2090 }
2091 }
2092 }
2093 if (capture_ref > 0) {
2094 if (i > last) {
2095 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2096 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002097 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002098 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
2099 last = next_index + 1;
2100 }
2101 i = next_index;
2102 break;
2103 }
2104 default:
2105 i = next_index;
2106 break;
2107 }
2108 }
2109 }
2110 if (length > last) {
2111 if (last == 0) {
2112 parts->Add(ReplacementPart::ReplacementString());
2113 } else {
2114 parts->Add(ReplacementPart::ReplacementSubString(last, length));
2115 }
2116 }
2117 }
2118
2119 ZoneList<ReplacementPart> parts_;
2120 ZoneList<Handle<String> > replacement_substrings_;
2121};
2122
2123
2124void CompiledReplacement::Compile(Handle<String> replacement,
2125 int capture_count,
2126 int subject_length) {
2127 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00002128 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002129 AssertNoAllocation no_alloc;
2130 ParseReplacementPattern(&parts_,
2131 replacement->ToAsciiVector(),
2132 capture_count,
2133 subject_length);
2134 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00002135 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002136 AssertNoAllocation no_alloc;
2137
2138 ParseReplacementPattern(&parts_,
2139 replacement->ToUC16Vector(),
2140 capture_count,
2141 subject_length);
2142 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002143 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002144 int substring_index = 0;
2145 for (int i = 0, n = parts_.length(); i < n; i++) {
2146 int tag = parts_[i].tag;
2147 if (tag <= 0) { // A replacement string slice.
2148 int from = -tag;
2149 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002150 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002151 parts_[i].tag = REPLACEMENT_SUBSTRING;
2152 parts_[i].data = substring_index;
2153 substring_index++;
2154 } else if (tag == REPLACEMENT_STRING) {
2155 replacement_substrings_.Add(replacement);
2156 parts_[i].data = substring_index;
2157 substring_index++;
2158 }
2159 }
2160}
2161
2162
2163void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
2164 int match_from,
2165 int match_to,
2166 Handle<JSArray> last_match_info) {
2167 for (int i = 0, n = parts_.length(); i < n; i++) {
2168 ReplacementPart part = parts_[i];
2169 switch (part.tag) {
2170 case SUBJECT_PREFIX:
2171 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
2172 break;
2173 case SUBJECT_SUFFIX: {
2174 int subject_length = part.data;
2175 if (match_to < subject_length) {
2176 builder->AddSubjectSlice(match_to, subject_length);
2177 }
2178 break;
2179 }
2180 case SUBJECT_CAPTURE: {
2181 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002182 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002183 int from = RegExpImpl::GetCapture(match_info, capture * 2);
2184 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
2185 if (from >= 0 && to > from) {
2186 builder->AddSubjectSlice(from, to);
2187 }
2188 break;
2189 }
2190 case REPLACEMENT_SUBSTRING:
2191 case REPLACEMENT_STRING:
2192 builder->AddString(replacement_substrings_[part.data]);
2193 break;
2194 default:
2195 UNREACHABLE();
2196 }
2197 }
2198}
2199
2200
2201
2202static Object* StringReplaceRegExpWithString(String* subject,
2203 JSRegExp* regexp,
2204 String* replacement,
2205 JSArray* last_match_info) {
2206 ASSERT(subject->IsFlat());
2207 ASSERT(replacement->IsFlat());
2208
2209 HandleScope handles;
2210
2211 int length = subject->length();
2212 Handle<String> subject_handle(subject);
2213 Handle<JSRegExp> regexp_handle(regexp);
2214 Handle<String> replacement_handle(replacement);
2215 Handle<JSArray> last_match_info_handle(last_match_info);
2216 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2217 subject_handle,
2218 0,
2219 last_match_info_handle);
2220 if (match.is_null()) {
2221 return Failure::Exception();
2222 }
2223 if (match->IsNull()) {
2224 return *subject_handle;
2225 }
2226
2227 int capture_count = regexp_handle->CaptureCount();
2228
2229 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002230 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002231 CompiledReplacement compiled_replacement;
2232 compiled_replacement.Compile(replacement_handle,
2233 capture_count,
2234 length);
2235
2236 bool is_global = regexp_handle->GetFlags().is_global();
2237
2238 // Guessing the number of parts that the final result string is built
2239 // from. Global regexps can match any number of times, so we guess
2240 // conservatively.
2241 int expected_parts =
2242 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
2243 ReplacementStringBuilder builder(subject_handle, expected_parts);
2244
2245 // Index of end of last match.
2246 int prev = 0;
2247
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002248 // Number of parts added by compiled replacement plus preceeding
2249 // string and possibly suffix after last match. It is possible for
2250 // all components to use two elements when encoded as two smis.
2251 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002252 bool matched = true;
2253 do {
2254 ASSERT(last_match_info_handle->HasFastElements());
2255 // Increase the capacity of the builder before entering local handle-scope,
2256 // so its internal buffer can safely allocate a new handle if it grows.
2257 builder.EnsureCapacity(parts_added_per_loop);
2258
2259 HandleScope loop_scope;
2260 int start, end;
2261 {
2262 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002263 FixedArray* match_info_array =
2264 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002265
2266 ASSERT_EQ(capture_count * 2 + 2,
2267 RegExpImpl::GetLastCaptureCount(match_info_array));
2268 start = RegExpImpl::GetCapture(match_info_array, 0);
2269 end = RegExpImpl::GetCapture(match_info_array, 1);
2270 }
2271
2272 if (prev < start) {
2273 builder.AddSubjectSlice(prev, start);
2274 }
2275 compiled_replacement.Apply(&builder,
2276 start,
2277 end,
2278 last_match_info_handle);
2279 prev = end;
2280
2281 // Only continue checking for global regexps.
2282 if (!is_global) break;
2283
2284 // Continue from where the match ended, unless it was an empty match.
2285 int next = end;
2286 if (start == end) {
2287 next = end + 1;
2288 if (next > length) break;
2289 }
2290
2291 match = RegExpImpl::Exec(regexp_handle,
2292 subject_handle,
2293 next,
2294 last_match_info_handle);
2295 if (match.is_null()) {
2296 return Failure::Exception();
2297 }
2298 matched = !match->IsNull();
2299 } while (matched);
2300
2301 if (prev < length) {
2302 builder.AddSubjectSlice(prev, length);
2303 }
2304
2305 return *(builder.ToString());
2306}
2307
2308
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00002309template <typename ResultSeqString>
2310static Object* StringReplaceRegExpWithEmptyString(String* subject,
2311 JSRegExp* regexp,
2312 JSArray* last_match_info) {
2313 ASSERT(subject->IsFlat());
2314
2315 HandleScope handles;
2316
2317 Handle<String> subject_handle(subject);
2318 Handle<JSRegExp> regexp_handle(regexp);
2319 Handle<JSArray> last_match_info_handle(last_match_info);
2320 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2321 subject_handle,
2322 0,
2323 last_match_info_handle);
2324 if (match.is_null()) return Failure::Exception();
2325 if (match->IsNull()) return *subject_handle;
2326
2327 ASSERT(last_match_info_handle->HasFastElements());
2328
2329 HandleScope loop_scope;
2330 int start, end;
2331 {
2332 AssertNoAllocation match_info_array_is_not_in_a_handle;
2333 FixedArray* match_info_array =
2334 FixedArray::cast(last_match_info_handle->elements());
2335
2336 start = RegExpImpl::GetCapture(match_info_array, 0);
2337 end = RegExpImpl::GetCapture(match_info_array, 1);
2338 }
2339
2340 int length = subject->length();
2341 int new_length = length - (end - start);
2342 if (new_length == 0) {
2343 return Heap::empty_string();
2344 }
2345 Handle<ResultSeqString> answer;
2346 if (ResultSeqString::kHasAsciiEncoding) {
2347 answer =
2348 Handle<ResultSeqString>::cast(Factory::NewRawAsciiString(new_length));
2349 } else {
2350 answer =
2351 Handle<ResultSeqString>::cast(Factory::NewRawTwoByteString(new_length));
2352 }
2353
2354 // If the regexp isn't global, only match once.
2355 if (!regexp_handle->GetFlags().is_global()) {
2356 if (start > 0) {
2357 String::WriteToFlat(*subject_handle,
2358 answer->GetChars(),
2359 0,
2360 start);
2361 }
2362 if (end < length) {
2363 String::WriteToFlat(*subject_handle,
2364 answer->GetChars() + start,
2365 end,
2366 length);
2367 }
2368 return *answer;
2369 }
2370
2371 int prev = 0; // Index of end of last match.
2372 int next = 0; // Start of next search (prev unless last match was empty).
2373 int position = 0;
2374
2375 do {
2376 if (prev < start) {
2377 // Add substring subject[prev;start] to answer string.
2378 String::WriteToFlat(*subject_handle,
2379 answer->GetChars() + position,
2380 prev,
2381 start);
2382 position += start - prev;
2383 }
2384 prev = end;
2385 next = end;
2386 // Continue from where the match ended, unless it was an empty match.
2387 if (start == end) {
2388 next++;
2389 if (next > length) break;
2390 }
2391 match = RegExpImpl::Exec(regexp_handle,
2392 subject_handle,
2393 next,
2394 last_match_info_handle);
2395 if (match.is_null()) return Failure::Exception();
2396 if (match->IsNull()) break;
2397
2398 ASSERT(last_match_info_handle->HasFastElements());
2399 HandleScope loop_scope;
2400 {
2401 AssertNoAllocation match_info_array_is_not_in_a_handle;
2402 FixedArray* match_info_array =
2403 FixedArray::cast(last_match_info_handle->elements());
2404 start = RegExpImpl::GetCapture(match_info_array, 0);
2405 end = RegExpImpl::GetCapture(match_info_array, 1);
2406 }
2407 } while (true);
2408
2409 if (prev < length) {
2410 // Add substring subject[prev;length] to answer string.
2411 String::WriteToFlat(*subject_handle,
2412 answer->GetChars() + position,
2413 prev,
2414 length);
2415 position += length - prev;
2416 }
2417
2418 if (position == 0) {
2419 return Heap::empty_string();
2420 }
2421
2422 // Shorten string and fill
2423 int string_size = ResultSeqString::SizeFor(position);
2424 int allocated_string_size = ResultSeqString::SizeFor(new_length);
2425 int delta = allocated_string_size - string_size;
2426
2427 answer->set_length(position);
2428 if (delta == 0) return *answer;
2429
2430 Address end_of_string = answer->address() + string_size;
2431 Heap::CreateFillerObjectAt(end_of_string, delta);
2432
2433 return *answer;
2434}
2435
2436
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002437static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2438 ASSERT(args.length() == 4);
2439
2440 CONVERT_CHECKED(String, subject, args[0]);
2441 if (!subject->IsFlat()) {
2442 Object* flat_subject = subject->TryFlatten();
2443 if (flat_subject->IsFailure()) {
2444 return flat_subject;
2445 }
2446 subject = String::cast(flat_subject);
2447 }
2448
2449 CONVERT_CHECKED(String, replacement, args[2]);
2450 if (!replacement->IsFlat()) {
2451 Object* flat_replacement = replacement->TryFlatten();
2452 if (flat_replacement->IsFailure()) {
2453 return flat_replacement;
2454 }
2455 replacement = String::cast(flat_replacement);
2456 }
2457
2458 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2459 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2460
2461 ASSERT(last_match_info->HasFastElements());
2462
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00002463 if (replacement->length() == 0) {
2464 if (subject->HasOnlyAsciiChars()) {
2465 return StringReplaceRegExpWithEmptyString<SeqAsciiString>(
2466 subject, regexp, last_match_info);
2467 } else {
2468 return StringReplaceRegExpWithEmptyString<SeqTwoByteString>(
2469 subject, regexp, last_match_info);
2470 }
2471 }
2472
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002473 return StringReplaceRegExpWithString(subject,
2474 regexp,
2475 replacement,
2476 last_match_info);
2477}
2478
2479
ager@chromium.org7c537e22008-10-16 08:43:32 +00002480// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2481// limit, we can fix the size of tables.
2482static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002483// Reduce alphabet to this size.
2484static const int kBMAlphabetSize = 0x100;
2485// For patterns below this length, the skip length of Boyer-Moore is too short
2486// to compensate for the algorithmic overhead compared to simple brute force.
2487static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002488
ager@chromium.org7c537e22008-10-16 08:43:32 +00002489// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2490// shift. Only allows the last kBMMaxShift characters of the needle
2491// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002492class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002493 public:
2494 BMGoodSuffixBuffers() {}
2495 inline void init(int needle_length) {
2496 ASSERT(needle_length > 1);
2497 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2498 int len = needle_length - start;
2499 biased_suffixes_ = suffixes_ - start;
2500 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2501 for (int i = 0; i <= len; i++) {
2502 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002503 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002504 }
2505 inline int& suffix(int index) {
2506 ASSERT(biased_suffixes_ + index >= suffixes_);
2507 return biased_suffixes_[index];
2508 }
2509 inline int& shift(int index) {
2510 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2511 return biased_good_suffix_shift_[index];
2512 }
2513 private:
2514 int suffixes_[kBMMaxShift + 1];
2515 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002516 int* biased_suffixes_;
2517 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002518 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2519};
2520
2521// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002522static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002523static BMGoodSuffixBuffers bmgs_buffers;
2524
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002525// State of the string match tables.
2526// SIMPLE: No usable content in the buffers.
2527// BOYER_MOORE_HORSPOOL: The bad_char_occurences table has been populated.
2528// BOYER_MOORE: The bmgs_buffers tables have also been populated.
2529// Whenever starting with a new needle, one should call InitializeStringSearch
2530// to determine which search strategy to use, and in the case of a long-needle
2531// strategy, the call also initializes the algorithm to SIMPLE.
2532enum StringSearchAlgorithm { SIMPLE_SEARCH, BOYER_MOORE_HORSPOOL, BOYER_MOORE };
2533static StringSearchAlgorithm algorithm;
2534
2535
ager@chromium.org7c537e22008-10-16 08:43:32 +00002536// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002537template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002538static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern) {
2539 // Only preprocess at most kBMMaxShift last characters of pattern.
2540 int start = pattern.length() < kBMMaxShift ? 0
2541 : pattern.length() - kBMMaxShift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002542 // Run forwards to populate bad_char_table, so that *last* instance
2543 // of character equivalence class is the one registered.
2544 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002545 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2546 : kBMAlphabetSize;
2547 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002548 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002549 } else {
2550 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002551 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002552 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002553 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002554 for (int i = start; i < pattern.length() - 1; i++) {
2555 pchar c = pattern[i];
2556 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002557 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002558 }
2559}
2560
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002561
ager@chromium.org7c537e22008-10-16 08:43:32 +00002562template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002563static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002564 int m = pattern.length();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002565 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002566 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002567 // Compute Good Suffix tables.
2568 bmgs_buffers.init(m);
2569
2570 bmgs_buffers.shift(m-1) = 1;
2571 bmgs_buffers.suffix(m) = m + 1;
2572 pchar last_char = pattern[m - 1];
2573 int suffix = m + 1;
2574 for (int i = m; i > start;) {
2575 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2576 if (bmgs_buffers.shift(suffix) == len) {
2577 bmgs_buffers.shift(suffix) = suffix - i;
2578 }
2579 suffix = bmgs_buffers.suffix(suffix);
2580 }
2581 i--;
2582 suffix--;
2583 bmgs_buffers.suffix(i) = suffix;
2584 if (suffix == m) {
2585 // No suffix to extend, so we check against last_char only.
2586 while (i > start && pattern[i - 1] != last_char) {
2587 if (bmgs_buffers.shift(m) == len) {
2588 bmgs_buffers.shift(m) = m - i;
2589 }
2590 i--;
2591 bmgs_buffers.suffix(i) = m;
2592 }
2593 if (i > start) {
2594 i--;
2595 suffix--;
2596 bmgs_buffers.suffix(i) = suffix;
2597 }
2598 }
2599 }
2600 if (suffix < m) {
2601 for (int i = start; i <= m; i++) {
2602 if (bmgs_buffers.shift(i) == len) {
2603 bmgs_buffers.shift(i) = suffix - start;
2604 }
2605 if (i == suffix) {
2606 suffix = bmgs_buffers.suffix(suffix);
2607 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002608 }
2609 }
2610}
2611
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002612
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002613template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002614static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002615 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002616 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002617 }
2618 if (sizeof(pchar) == 1) {
2619 if (char_code > String::kMaxAsciiCharCode) {
2620 return -1;
2621 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002622 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002623 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002624 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002625}
2626
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002627
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002628// Restricted simplified Boyer-Moore string matching.
2629// Uses only the bad-shift table of Boyer-Moore and only uses it
2630// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002631template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002632static int BoyerMooreHorspool(Vector<const schar> subject,
2633 Vector<const pchar> pattern,
2634 int start_index,
2635 bool* complete) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002636 ASSERT(algorithm <= BOYER_MOORE_HORSPOOL);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002637 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002638 int m = pattern.length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002639
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002640 int badness = -m;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002641
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002642 // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002643 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002644 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002645 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002646 // Perform search
2647 for (idx = start_index; idx <= n - m;) {
2648 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002649 int c;
2650 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002651 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002652 int shift = j - bc_occ;
2653 idx += shift;
2654 badness += 1 - shift; // at most zero, so badness cannot increase.
2655 if (idx > n - m) {
2656 *complete = true;
2657 return -1;
2658 }
2659 }
2660 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002661 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002662 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002663 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002664 return idx;
2665 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002666 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002667 // Badness increases by the number of characters we have
2668 // checked, and decreases by the number of characters we
2669 // can skip by shifting. It's a measure of how we are doing
2670 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002671 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002672 if (badness > 0) {
2673 *complete = false;
2674 return idx;
2675 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002676 }
2677 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002678 *complete = true;
2679 return -1;
2680}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002681
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002682
2683template <typename schar, typename pchar>
2684static int BoyerMooreIndexOf(Vector<const schar> subject,
2685 Vector<const pchar> pattern,
2686 int idx) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002687 ASSERT(algorithm <= BOYER_MOORE);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002688 int n = subject.length();
2689 int m = pattern.length();
2690 // Only preprocess at most kBMMaxShift last characters of pattern.
2691 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2692
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002693 pchar last_char = pattern[m - 1];
2694 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002695 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002696 int j = m - 1;
2697 schar c;
2698 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002699 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002700 idx += shift;
2701 if (idx > n - m) {
2702 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002703 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002704 }
2705 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2706 if (j < 0) {
2707 return idx;
2708 } else if (j < start) {
2709 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002710 // Fall back on BMH shift.
2711 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002712 } else {
2713 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002714 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002715 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002716 if (gs_shift > shift) {
2717 shift = gs_shift;
2718 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002719 idx += shift;
2720 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002721 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002722
2723 return -1;
2724}
2725
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002726
2727template <typename schar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002728static inline int SingleCharIndexOf(Vector<const schar> string,
2729 schar pattern_char,
2730 int start_index) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002731 if (sizeof(schar) == 1) {
2732 const schar* pos = reinterpret_cast<const schar*>(
2733 memchr(string.start() + start_index,
2734 pattern_char,
2735 string.length() - start_index));
2736 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002737 return static_cast<int>(pos - string.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002738 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002739 for (int i = start_index, n = string.length(); i < n; i++) {
2740 if (pattern_char == string[i]) {
2741 return i;
2742 }
2743 }
2744 return -1;
2745}
2746
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002747
2748template <typename schar>
2749static int SingleCharLastIndexOf(Vector<const schar> string,
2750 schar pattern_char,
2751 int start_index) {
2752 for (int i = start_index; i >= 0; i--) {
2753 if (pattern_char == string[i]) {
2754 return i;
2755 }
2756 }
2757 return -1;
2758}
2759
2760
ager@chromium.org7c537e22008-10-16 08:43:32 +00002761// Trivial string search for shorter strings.
2762// On return, if "complete" is set to true, the return value is the
2763// final result of searching for the patter in the subject.
2764// If "complete" is set to false, the return value is the index where
2765// further checking should start, i.e., it's guaranteed that the pattern
2766// does not occur at a position prior to the returned index.
2767template <typename pchar, typename schar>
2768static int SimpleIndexOf(Vector<const schar> subject,
2769 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002770 int idx,
2771 bool* complete) {
2772 // Badness is a count of how much work we have done. When we have
2773 // done enough work we decide it's probably worth switching to a better
2774 // algorithm.
2775 int badness = -10 - (pattern.length() << 2);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002776
ager@chromium.org7c537e22008-10-16 08:43:32 +00002777 // We know our pattern is at least 2 characters, we cache the first so
2778 // the common case of the first character not matching is faster.
2779 pchar pattern_first_char = pattern[0];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002780 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2781 badness++;
2782 if (badness > 0) {
2783 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002784 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002785 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002786 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2787 const schar* pos = reinterpret_cast<const schar*>(
2788 memchr(subject.start() + i,
2789 pattern_first_char,
2790 n - i + 1));
2791 if (pos == NULL) {
2792 *complete = true;
2793 return -1;
2794 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002795 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002796 } else {
2797 if (subject[i] != pattern_first_char) continue;
2798 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002799 int j = 1;
2800 do {
2801 if (pattern[j] != subject[i+j]) {
2802 break;
2803 }
2804 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002805 } while (j < pattern.length());
2806 if (j == pattern.length()) {
2807 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002808 return i;
2809 }
2810 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002811 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002812 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002813 return -1;
2814}
2815
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002816// Simple indexOf that never bails out. For short patterns only.
2817template <typename pchar, typename schar>
2818static int SimpleIndexOf(Vector<const schar> subject,
2819 Vector<const pchar> pattern,
2820 int idx) {
2821 pchar pattern_first_char = pattern[0];
2822 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002823 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2824 const schar* pos = reinterpret_cast<const schar*>(
2825 memchr(subject.start() + i,
2826 pattern_first_char,
2827 n - i + 1));
2828 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002829 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002830 } else {
2831 if (subject[i] != pattern_first_char) continue;
2832 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002833 int j = 1;
2834 do {
2835 if (pattern[j] != subject[i+j]) {
2836 break;
2837 }
2838 j++;
2839 } while (j < pattern.length());
2840 if (j == pattern.length()) {
2841 return i;
2842 }
2843 }
2844 return -1;
2845}
2846
2847
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002848// Strategy for searching for a string in another string.
2849enum StringSearchStrategy { SEARCH_FAIL, SEARCH_SHORT, SEARCH_LONG };
ager@chromium.org7c537e22008-10-16 08:43:32 +00002850
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002851
2852template <typename pchar>
2853static inline StringSearchStrategy InitializeStringSearch(
2854 Vector<const pchar> pat, bool ascii_subject) {
2855 ASSERT(pat.length() > 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002856 // We have an ASCII haystack and a non-ASCII needle. Check if there
2857 // really is a non-ASCII character in the needle and bail out if there
2858 // is.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002859 if (ascii_subject && sizeof(pchar) > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002860 for (int i = 0; i < pat.length(); i++) {
2861 uc16 c = pat[i];
2862 if (c > String::kMaxAsciiCharCode) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002863 return SEARCH_FAIL;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002864 }
2865 }
2866 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002867 if (pat.length() < kBMMinPatternLength) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002868 return SEARCH_SHORT;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002869 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002870 algorithm = SIMPLE_SEARCH;
2871 return SEARCH_LONG;
2872}
2873
2874
2875// Dispatch long needle searches to different algorithms.
2876template <typename schar, typename pchar>
2877static int ComplexIndexOf(Vector<const schar> sub,
2878 Vector<const pchar> pat,
2879 int start_index) {
2880 ASSERT(pat.length() >= kBMMinPatternLength);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002881 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002882 bool complete;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002883 int idx = start_index;
2884 switch (algorithm) {
2885 case SIMPLE_SEARCH:
2886 idx = SimpleIndexOf(sub, pat, idx, &complete);
2887 if (complete) return idx;
2888 BoyerMoorePopulateBadCharTable(pat);
2889 algorithm = BOYER_MOORE_HORSPOOL;
2890 // FALLTHROUGH.
2891 case BOYER_MOORE_HORSPOOL:
2892 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
2893 if (complete) return idx;
2894 // Build the Good Suffix table and continue searching.
2895 BoyerMoorePopulateGoodSuffixTable(pat);
2896 algorithm = BOYER_MOORE;
2897 // FALLTHROUGH.
2898 case BOYER_MOORE:
2899 return BoyerMooreIndexOf(sub, pat, idx);
2900 }
2901 UNREACHABLE();
2902 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002903}
2904
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002905
2906// Dispatch to different search strategies for a single search.
2907// If searching multiple times on the same needle, the search
2908// strategy should only be computed once and then dispatch to different
2909// loops.
2910template <typename schar, typename pchar>
2911static int StringSearch(Vector<const schar> sub,
2912 Vector<const pchar> pat,
2913 int start_index) {
2914 bool ascii_subject = (sizeof(schar) == 1);
2915 StringSearchStrategy strategy = InitializeStringSearch(pat, ascii_subject);
2916 switch (strategy) {
2917 case SEARCH_FAIL: return -1;
2918 case SEARCH_SHORT: return SimpleIndexOf(sub, pat, start_index);
2919 case SEARCH_LONG: return ComplexIndexOf(sub, pat, start_index);
2920 }
2921 UNREACHABLE();
2922 return -1;
2923}
2924
2925
ager@chromium.org7c537e22008-10-16 08:43:32 +00002926// Perform string match of pattern on subject, starting at start index.
2927// Caller must ensure that 0 <= start_index <= sub->length(),
2928// and should check that pat->length() + start_index <= sub->length()
2929int Runtime::StringMatch(Handle<String> sub,
2930 Handle<String> pat,
2931 int start_index) {
2932 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002933 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002934
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002935 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002936 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002937
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002938 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002939 if (start_index + pattern_length > subject_length) return -1;
2940
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002941 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002942 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002943 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002944
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002945 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002946 // character patterns linear search is necessary, so any smart
2947 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002948 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002949 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002950 String* seq_sub = *sub;
2951 if (seq_sub->IsConsString()) {
2952 seq_sub = ConsString::cast(seq_sub)->first();
2953 }
2954 if (seq_sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002955 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002956 if (pchar > String::kMaxAsciiCharCode) {
2957 return -1;
2958 }
2959 Vector<const char> ascii_vector =
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002960 seq_sub->ToAsciiVector().SubVector(start_index, subject_length);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002961 const void* pos = memchr(ascii_vector.start(),
2962 static_cast<const char>(pchar),
2963 static_cast<size_t>(ascii_vector.length()));
2964 if (pos == NULL) {
2965 return -1;
2966 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002967 return static_cast<int>(reinterpret_cast<const char*>(pos)
2968 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002969 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002970 return SingleCharIndexOf(seq_sub->ToUC16Vector(),
2971 pat->Get(0),
2972 start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002973 }
2974
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002975 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002976 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002977 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002978
ager@chromium.org7c537e22008-10-16 08:43:32 +00002979 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002980 // Extract flattened substrings of cons strings before determining asciiness.
2981 String* seq_sub = *sub;
2982 if (seq_sub->IsConsString()) {
2983 seq_sub = ConsString::cast(seq_sub)->first();
2984 }
2985 String* seq_pat = *pat;
2986 if (seq_pat->IsConsString()) {
2987 seq_pat = ConsString::cast(seq_pat)->first();
2988 }
2989
ager@chromium.org7c537e22008-10-16 08:43:32 +00002990 // dispatch on type of strings
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002991 if (seq_pat->IsAsciiRepresentation()) {
2992 Vector<const char> pat_vector = seq_pat->ToAsciiVector();
2993 if (seq_sub->IsAsciiRepresentation()) {
2994 return StringSearch(seq_sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002995 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002996 return StringSearch(seq_sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002997 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002998 Vector<const uc16> pat_vector = seq_pat->ToUC16Vector();
2999 if (seq_sub->IsAsciiRepresentation()) {
3000 return StringSearch(seq_sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003001 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00003002 return StringSearch(seq_sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003003}
3004
3005
3006static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003007 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003008 ASSERT(args.length() == 3);
3009
ager@chromium.org7c537e22008-10-16 08:43:32 +00003010 CONVERT_ARG_CHECKED(String, sub, 0);
3011 CONVERT_ARG_CHECKED(String, pat, 1);
3012
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003013 Object* index = args[2];
3014 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003015 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003016
ager@chromium.org870a0b62008-11-04 11:43:05 +00003017 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00003018 int position = Runtime::StringMatch(sub, pat, start_index);
3019 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003020}
3021
3022
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003023template <typename schar, typename pchar>
3024static int StringMatchBackwards(Vector<const schar> sub,
3025 Vector<const pchar> pat,
3026 int idx) {
3027 ASSERT(pat.length() >= 1);
3028 ASSERT(idx + pat.length() <= sub.length());
3029
3030 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
3031 for (int i = 0; i < pat.length(); i++) {
3032 uc16 c = pat[i];
3033 if (c > String::kMaxAsciiCharCode) {
3034 return -1;
3035 }
3036 }
3037 }
3038
3039 pchar pattern_first_char = pat[0];
3040 for (int i = idx; i >= 0; i--) {
3041 if (sub[i] != pattern_first_char) continue;
3042 int j = 1;
3043 while (j < pat.length()) {
3044 if (pat[j] != sub[i+j]) {
3045 break;
3046 }
3047 j++;
3048 }
3049 if (j == pat.length()) {
3050 return i;
3051 }
3052 }
3053 return -1;
3054}
3055
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003056static Object* Runtime_StringLastIndexOf(Arguments args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003057 HandleScope scope; // create a new handle scope
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003058 ASSERT(args.length() == 3);
3059
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003060 CONVERT_ARG_CHECKED(String, sub, 0);
3061 CONVERT_ARG_CHECKED(String, pat, 1);
3062
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003063 Object* index = args[2];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003064 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003065 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003066
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003067 uint32_t pat_length = pat->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003068 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003069
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003070 if (start_index + pat_length > sub_length) {
3071 start_index = sub_length - pat_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00003072 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003073
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003074 if (pat_length == 0) {
3075 return Smi::FromInt(start_index);
3076 }
3077
3078 if (!sub->IsFlat()) {
3079 FlattenString(sub);
3080 }
3081
3082 if (pat_length == 1) {
3083 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
3084 if (sub->IsAsciiRepresentation()) {
3085 uc16 pchar = pat->Get(0);
3086 if (pchar > String::kMaxAsciiCharCode) {
3087 return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003088 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003089 return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
3090 static_cast<char>(pat->Get(0)),
3091 start_index));
3092 } else {
3093 return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
3094 pat->Get(0),
3095 start_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003096 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003097 }
3098
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003099 if (!pat->IsFlat()) {
3100 FlattenString(pat);
3101 }
3102
3103 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
3104
3105 int position = -1;
3106
3107 if (pat->IsAsciiRepresentation()) {
3108 Vector<const char> pat_vector = pat->ToAsciiVector();
3109 if (sub->IsAsciiRepresentation()) {
3110 position = StringMatchBackwards(sub->ToAsciiVector(),
3111 pat_vector,
3112 start_index);
3113 } else {
3114 position = StringMatchBackwards(sub->ToUC16Vector(),
3115 pat_vector,
3116 start_index);
3117 }
3118 } else {
3119 Vector<const uc16> pat_vector = pat->ToUC16Vector();
3120 if (sub->IsAsciiRepresentation()) {
3121 position = StringMatchBackwards(sub->ToAsciiVector(),
3122 pat_vector,
3123 start_index);
3124 } else {
3125 position = StringMatchBackwards(sub->ToUC16Vector(),
3126 pat_vector,
3127 start_index);
3128 }
3129 }
3130
3131 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003132}
3133
3134
3135static Object* Runtime_StringLocaleCompare(Arguments args) {
3136 NoHandleAllocation ha;
3137 ASSERT(args.length() == 2);
3138
3139 CONVERT_CHECKED(String, str1, args[0]);
3140 CONVERT_CHECKED(String, str2, args[1]);
3141
3142 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003143 int str1_length = str1->length();
3144 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003145
3146 // Decide trivial cases without flattening.
3147 if (str1_length == 0) {
3148 if (str2_length == 0) return Smi::FromInt(0); // Equal.
3149 return Smi::FromInt(-str2_length);
3150 } else {
3151 if (str2_length == 0) return Smi::FromInt(str1_length);
3152 }
3153
3154 int end = str1_length < str2_length ? str1_length : str2_length;
3155
3156 // No need to flatten if we are going to find the answer on the first
3157 // character. At this point we know there is at least one character
3158 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003159 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003160 if (d != 0) return Smi::FromInt(d);
3161
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003162 str1->TryFlatten();
3163 str2->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003164
3165 static StringInputBuffer buf1;
3166 static StringInputBuffer buf2;
3167
3168 buf1.Reset(str1);
3169 buf2.Reset(str2);
3170
3171 for (int i = 0; i < end; i++) {
3172 uint16_t char1 = buf1.GetNext();
3173 uint16_t char2 = buf2.GetNext();
3174 if (char1 != char2) return Smi::FromInt(char1 - char2);
3175 }
3176
3177 return Smi::FromInt(str1_length - str2_length);
3178}
3179
3180
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003181static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003182 NoHandleAllocation ha;
3183 ASSERT(args.length() == 3);
3184
3185 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00003186 Object* from = args[1];
3187 Object* to = args[2];
3188 int start, end;
3189 // We have a fast integer-only case here to avoid a conversion to double in
3190 // the common case where from and to are Smis.
3191 if (from->IsSmi() && to->IsSmi()) {
3192 start = Smi::cast(from)->value();
3193 end = Smi::cast(to)->value();
3194 } else {
3195 CONVERT_DOUBLE_CHECKED(from_number, from);
3196 CONVERT_DOUBLE_CHECKED(to_number, to);
3197 start = FastD2I(from_number);
3198 end = FastD2I(to_number);
3199 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003200 RUNTIME_ASSERT(end >= start);
3201 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003202 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003203 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003204 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003205}
3206
3207
ager@chromium.org41826e72009-03-30 13:30:57 +00003208static Object* Runtime_StringMatch(Arguments args) {
3209 ASSERT_EQ(3, args.length());
3210
3211 CONVERT_ARG_CHECKED(String, subject, 0);
3212 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
3213 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
3214 HandleScope handles;
3215
3216 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
3217
3218 if (match.is_null()) {
3219 return Failure::Exception();
3220 }
3221 if (match->IsNull()) {
3222 return Heap::null_value();
3223 }
3224 int length = subject->length();
3225
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00003226 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00003227 ZoneList<int> offsets(8);
3228 do {
3229 int start;
3230 int end;
3231 {
3232 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003233 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00003234 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
3235 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
3236 }
3237 offsets.Add(start);
3238 offsets.Add(end);
3239 int index = start < end ? end : end + 1;
3240 if (index > length) break;
3241 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
3242 if (match.is_null()) {
3243 return Failure::Exception();
3244 }
3245 } while (!match->IsNull());
3246 int matches = offsets.length() / 2;
3247 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
3248 for (int i = 0; i < matches ; i++) {
3249 int from = offsets.at(i * 2);
3250 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003251 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00003252 }
3253 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
3254 result->set_length(Smi::FromInt(matches));
3255 return *result;
3256}
3257
3258
lrn@chromium.org25156de2010-04-06 13:10:27 +00003259// Two smis before and after the match, for very long strings.
3260const int kMaxBuilderEntriesPerRegExpMatch = 5;
3261
3262
3263static void SetLastMatchInfoNoCaptures(Handle<String> subject,
3264 Handle<JSArray> last_match_info,
3265 int match_start,
3266 int match_end) {
3267 // Fill last_match_info with a single capture.
3268 last_match_info->EnsureSize(2 + RegExpImpl::kLastMatchOverhead);
3269 AssertNoAllocation no_gc;
3270 FixedArray* elements = FixedArray::cast(last_match_info->elements());
3271 RegExpImpl::SetLastCaptureCount(elements, 2);
3272 RegExpImpl::SetLastInput(elements, *subject);
3273 RegExpImpl::SetLastSubject(elements, *subject);
3274 RegExpImpl::SetCapture(elements, 0, match_start);
3275 RegExpImpl::SetCapture(elements, 1, match_end);
3276}
3277
3278
3279template <typename schar>
3280static bool SearchCharMultiple(Vector<schar> subject,
3281 String* pattern,
3282 schar pattern_char,
3283 FixedArrayBuilder* builder,
3284 int* match_pos) {
3285 // Position of last match.
3286 int pos = *match_pos;
3287 int subject_length = subject.length();
3288 while (pos < subject_length) {
3289 int match_end = pos + 1;
3290 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3291 *match_pos = pos;
3292 return false;
3293 }
3294 int new_pos = SingleCharIndexOf(subject, pattern_char, match_end);
3295 if (new_pos >= 0) {
3296 // Match has been found.
3297 if (new_pos > match_end) {
3298 ReplacementStringBuilder::AddSubjectSlice(builder, match_end, new_pos);
3299 }
3300 pos = new_pos;
3301 builder->Add(pattern);
3302 } else {
3303 break;
3304 }
3305 }
3306 if (pos + 1 < subject_length) {
3307 ReplacementStringBuilder::AddSubjectSlice(builder, pos + 1, subject_length);
3308 }
3309 *match_pos = pos;
3310 return true;
3311}
3312
3313
3314static bool SearchCharMultiple(Handle<String> subject,
3315 Handle<String> pattern,
3316 Handle<JSArray> last_match_info,
3317 FixedArrayBuilder* builder) {
3318 ASSERT(subject->IsFlat());
3319 ASSERT_EQ(1, pattern->length());
3320 uc16 pattern_char = pattern->Get(0);
3321 // Treating position before first as initial "previous match position".
3322 int match_pos = -1;
3323
3324 for (;;) { // Break when search complete.
3325 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3326 AssertNoAllocation no_gc;
3327 if (subject->IsAsciiRepresentation()) {
3328 if (pattern_char > String::kMaxAsciiCharCode) {
3329 break;
3330 }
3331 Vector<const char> subject_vector = subject->ToAsciiVector();
3332 char pattern_ascii_char = static_cast<char>(pattern_char);
3333 bool complete = SearchCharMultiple<const char>(subject_vector,
3334 *pattern,
3335 pattern_ascii_char,
3336 builder,
3337 &match_pos);
3338 if (complete) break;
3339 } else {
3340 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3341 bool complete = SearchCharMultiple<const uc16>(subject_vector,
3342 *pattern,
3343 pattern_char,
3344 builder,
3345 &match_pos);
3346 if (complete) break;
3347 }
3348 }
3349
3350 if (match_pos >= 0) {
3351 SetLastMatchInfoNoCaptures(subject,
3352 last_match_info,
3353 match_pos,
3354 match_pos + 1);
3355 return true;
3356 }
3357 return false; // No matches at all.
3358}
3359
3360
3361template <typename schar, typename pchar>
3362static bool SearchStringMultiple(Vector<schar> subject,
3363 String* pattern,
3364 Vector<pchar> pattern_string,
3365 FixedArrayBuilder* builder,
3366 int* match_pos) {
3367 int pos = *match_pos;
3368 int subject_length = subject.length();
3369 int pattern_length = pattern_string.length();
3370 int max_search_start = subject_length - pattern_length;
3371 bool is_ascii = (sizeof(schar) == 1);
3372 StringSearchStrategy strategy =
3373 InitializeStringSearch(pattern_string, is_ascii);
3374 switch (strategy) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003375 case SEARCH_FAIL: break;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003376 case SEARCH_SHORT:
3377 while (pos <= max_search_start) {
3378 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3379 *match_pos = pos;
3380 return false;
3381 }
3382 // Position of end of previous match.
3383 int match_end = pos + pattern_length;
3384 int new_pos = SimpleIndexOf(subject, pattern_string, match_end);
3385 if (new_pos >= 0) {
3386 // A match.
3387 if (new_pos > match_end) {
3388 ReplacementStringBuilder::AddSubjectSlice(builder,
3389 match_end,
3390 new_pos);
3391 }
3392 pos = new_pos;
3393 builder->Add(pattern);
3394 } else {
3395 break;
3396 }
3397 }
3398 break;
3399 case SEARCH_LONG:
3400 while (pos <= max_search_start) {
3401 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003402 *match_pos = pos;
3403 return false;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003404 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003405 int match_end = pos + pattern_length;
3406 int new_pos = ComplexIndexOf(subject, pattern_string, match_end);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003407 if (new_pos >= 0) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003408 // A match has been found.
3409 if (new_pos > match_end) {
3410 ReplacementStringBuilder::AddSubjectSlice(builder,
3411 match_end,
3412 new_pos);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003413 }
3414 pos = new_pos;
3415 builder->Add(pattern);
3416 } else {
3417 break;
3418 }
3419 }
3420 break;
3421 }
3422 if (pos < max_search_start) {
3423 ReplacementStringBuilder::AddSubjectSlice(builder,
3424 pos + pattern_length,
3425 subject_length);
3426 }
3427 *match_pos = pos;
3428 return true;
3429}
3430
3431
3432static bool SearchStringMultiple(Handle<String> subject,
3433 Handle<String> pattern,
3434 Handle<JSArray> last_match_info,
3435 FixedArrayBuilder* builder) {
3436 ASSERT(subject->IsFlat());
3437 ASSERT(pattern->IsFlat());
3438 ASSERT(pattern->length() > 1);
3439
3440 // Treating as if a previous match was before first character.
3441 int match_pos = -pattern->length();
3442
3443 for (;;) { // Break when search complete.
3444 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3445 AssertNoAllocation no_gc;
3446 if (subject->IsAsciiRepresentation()) {
3447 Vector<const char> subject_vector = subject->ToAsciiVector();
3448 if (pattern->IsAsciiRepresentation()) {
3449 if (SearchStringMultiple(subject_vector,
3450 *pattern,
3451 pattern->ToAsciiVector(),
3452 builder,
3453 &match_pos)) break;
3454 } else {
3455 if (SearchStringMultiple(subject_vector,
3456 *pattern,
3457 pattern->ToUC16Vector(),
3458 builder,
3459 &match_pos)) break;
3460 }
3461 } else {
3462 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3463 if (pattern->IsAsciiRepresentation()) {
3464 if (SearchStringMultiple(subject_vector,
3465 *pattern,
3466 pattern->ToAsciiVector(),
3467 builder,
3468 &match_pos)) break;
3469 } else {
3470 if (SearchStringMultiple(subject_vector,
3471 *pattern,
3472 pattern->ToUC16Vector(),
3473 builder,
3474 &match_pos)) break;
3475 }
3476 }
3477 }
3478
3479 if (match_pos >= 0) {
3480 SetLastMatchInfoNoCaptures(subject,
3481 last_match_info,
3482 match_pos,
3483 match_pos + pattern->length());
3484 return true;
3485 }
3486 return false; // No matches at all.
3487}
3488
3489
3490static RegExpImpl::IrregexpResult SearchRegExpNoCaptureMultiple(
3491 Handle<String> subject,
3492 Handle<JSRegExp> regexp,
3493 Handle<JSArray> last_match_array,
3494 FixedArrayBuilder* builder) {
3495 ASSERT(subject->IsFlat());
3496 int match_start = -1;
3497 int match_end = 0;
3498 int pos = 0;
3499 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3500 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3501
3502 OffsetsVector registers(required_registers);
3503 Vector<int> register_vector(registers.vector(), registers.length());
3504 int subject_length = subject->length();
3505
3506 for (;;) { // Break on failure, return on exception.
3507 RegExpImpl::IrregexpResult result =
3508 RegExpImpl::IrregexpExecOnce(regexp,
3509 subject,
3510 pos,
3511 register_vector);
3512 if (result == RegExpImpl::RE_SUCCESS) {
3513 match_start = register_vector[0];
3514 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3515 if (match_end < match_start) {
3516 ReplacementStringBuilder::AddSubjectSlice(builder,
3517 match_end,
3518 match_start);
3519 }
3520 match_end = register_vector[1];
3521 HandleScope loop_scope;
3522 builder->Add(*Factory::NewSubString(subject, match_start, match_end));
3523 if (match_start != match_end) {
3524 pos = match_end;
3525 } else {
3526 pos = match_end + 1;
3527 if (pos > subject_length) break;
3528 }
3529 } else if (result == RegExpImpl::RE_FAILURE) {
3530 break;
3531 } else {
3532 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3533 return result;
3534 }
3535 }
3536
3537 if (match_start >= 0) {
3538 if (match_end < subject_length) {
3539 ReplacementStringBuilder::AddSubjectSlice(builder,
3540 match_end,
3541 subject_length);
3542 }
3543 SetLastMatchInfoNoCaptures(subject,
3544 last_match_array,
3545 match_start,
3546 match_end);
3547 return RegExpImpl::RE_SUCCESS;
3548 } else {
3549 return RegExpImpl::RE_FAILURE; // No matches at all.
3550 }
3551}
3552
3553
3554static RegExpImpl::IrregexpResult SearchRegExpMultiple(
3555 Handle<String> subject,
3556 Handle<JSRegExp> regexp,
3557 Handle<JSArray> last_match_array,
3558 FixedArrayBuilder* builder) {
3559
3560 ASSERT(subject->IsFlat());
3561 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3562 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3563
3564 OffsetsVector registers(required_registers);
3565 Vector<int> register_vector(registers.vector(), registers.length());
3566
3567 RegExpImpl::IrregexpResult result =
3568 RegExpImpl::IrregexpExecOnce(regexp,
3569 subject,
3570 0,
3571 register_vector);
3572
3573 int capture_count = regexp->CaptureCount();
3574 int subject_length = subject->length();
3575
3576 // Position to search from.
3577 int pos = 0;
3578 // End of previous match. Differs from pos if match was empty.
3579 int match_end = 0;
3580 if (result == RegExpImpl::RE_SUCCESS) {
3581 // Need to keep a copy of the previous match for creating last_match_info
3582 // at the end, so we have two vectors that we swap between.
3583 OffsetsVector registers2(required_registers);
3584 Vector<int> prev_register_vector(registers2.vector(), registers2.length());
3585
3586 do {
3587 int match_start = register_vector[0];
3588 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3589 if (match_end < match_start) {
3590 ReplacementStringBuilder::AddSubjectSlice(builder,
3591 match_end,
3592 match_start);
3593 }
3594 match_end = register_vector[1];
3595
3596 {
3597 // Avoid accumulating new handles inside loop.
3598 HandleScope temp_scope;
3599 // Arguments array to replace function is match, captures, index and
3600 // subject, i.e., 3 + capture count in total.
3601 Handle<FixedArray> elements = Factory::NewFixedArray(3 + capture_count);
3602 elements->set(0, *Factory::NewSubString(subject,
3603 match_start,
3604 match_end));
3605 for (int i = 1; i <= capture_count; i++) {
3606 int start = register_vector[i * 2];
3607 if (start >= 0) {
3608 int end = register_vector[i * 2 + 1];
3609 ASSERT(start <= end);
3610 Handle<String> substring = Factory::NewSubString(subject,
3611 start,
3612 end);
3613 elements->set(i, *substring);
3614 } else {
3615 ASSERT(register_vector[i * 2 + 1] < 0);
3616 elements->set(i, Heap::undefined_value());
3617 }
3618 }
3619 elements->set(capture_count + 1, Smi::FromInt(match_start));
3620 elements->set(capture_count + 2, *subject);
3621 builder->Add(*Factory::NewJSArrayWithElements(elements));
3622 }
3623 // Swap register vectors, so the last successful match is in
3624 // prev_register_vector.
3625 Vector<int> tmp = prev_register_vector;
3626 prev_register_vector = register_vector;
3627 register_vector = tmp;
3628
3629 if (match_end > match_start) {
3630 pos = match_end;
3631 } else {
3632 pos = match_end + 1;
3633 if (pos > subject_length) {
3634 break;
3635 }
3636 }
3637
3638 result = RegExpImpl::IrregexpExecOnce(regexp,
3639 subject,
3640 pos,
3641 register_vector);
3642 } while (result == RegExpImpl::RE_SUCCESS);
3643
3644 if (result != RegExpImpl::RE_EXCEPTION) {
3645 // Finished matching, with at least one match.
3646 if (match_end < subject_length) {
3647 ReplacementStringBuilder::AddSubjectSlice(builder,
3648 match_end,
3649 subject_length);
3650 }
3651
3652 int last_match_capture_count = (capture_count + 1) * 2;
3653 int last_match_array_size =
3654 last_match_capture_count + RegExpImpl::kLastMatchOverhead;
3655 last_match_array->EnsureSize(last_match_array_size);
3656 AssertNoAllocation no_gc;
3657 FixedArray* elements = FixedArray::cast(last_match_array->elements());
3658 RegExpImpl::SetLastCaptureCount(elements, last_match_capture_count);
3659 RegExpImpl::SetLastSubject(elements, *subject);
3660 RegExpImpl::SetLastInput(elements, *subject);
3661 for (int i = 0; i < last_match_capture_count; i++) {
3662 RegExpImpl::SetCapture(elements, i, prev_register_vector[i]);
3663 }
3664 return RegExpImpl::RE_SUCCESS;
3665 }
3666 }
3667 // No matches at all, return failure or exception result directly.
3668 return result;
3669}
3670
3671
3672static Object* Runtime_RegExpExecMultiple(Arguments args) {
3673 ASSERT(args.length() == 4);
3674 HandleScope handles;
3675
3676 CONVERT_ARG_CHECKED(String, subject, 1);
3677 if (!subject->IsFlat()) { FlattenString(subject); }
3678 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
3679 CONVERT_ARG_CHECKED(JSArray, last_match_info, 2);
3680 CONVERT_ARG_CHECKED(JSArray, result_array, 3);
3681
3682 ASSERT(last_match_info->HasFastElements());
3683 ASSERT(regexp->GetFlags().is_global());
3684 Handle<FixedArray> result_elements;
3685 if (result_array->HasFastElements()) {
3686 result_elements =
3687 Handle<FixedArray>(FixedArray::cast(result_array->elements()));
3688 } else {
3689 result_elements = Factory::NewFixedArrayWithHoles(16);
3690 }
3691 FixedArrayBuilder builder(result_elements);
3692
3693 if (regexp->TypeTag() == JSRegExp::ATOM) {
3694 Handle<String> pattern(
3695 String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex)));
3696 int pattern_length = pattern->length();
3697 if (pattern_length == 1) {
3698 if (SearchCharMultiple(subject, pattern, last_match_info, &builder)) {
3699 return *builder.ToJSArray(result_array);
3700 }
3701 return Heap::null_value();
3702 }
3703
3704 if (!pattern->IsFlat()) FlattenString(pattern);
3705 if (SearchStringMultiple(subject, pattern, last_match_info, &builder)) {
3706 return *builder.ToJSArray(result_array);
3707 }
3708 return Heap::null_value();
3709 }
3710
3711 ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
3712
3713 RegExpImpl::IrregexpResult result;
3714 if (regexp->CaptureCount() == 0) {
3715 result = SearchRegExpNoCaptureMultiple(subject,
3716 regexp,
3717 last_match_info,
3718 &builder);
3719 } else {
3720 result = SearchRegExpMultiple(subject, regexp, last_match_info, &builder);
3721 }
3722 if (result == RegExpImpl::RE_SUCCESS) return *builder.ToJSArray(result_array);
3723 if (result == RegExpImpl::RE_FAILURE) return Heap::null_value();
3724 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3725 return Failure::Exception();
3726}
3727
3728
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003729static Object* Runtime_NumberToRadixString(Arguments args) {
3730 NoHandleAllocation ha;
3731 ASSERT(args.length() == 2);
3732
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003733 // Fast case where the result is a one character string.
3734 if (args[0]->IsSmi() && args[1]->IsSmi()) {
3735 int value = Smi::cast(args[0])->value();
3736 int radix = Smi::cast(args[1])->value();
3737 if (value >= 0 && value < radix) {
3738 RUNTIME_ASSERT(radix <= 36);
3739 // Character array used for conversion.
3740 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
3741 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
3742 }
3743 }
3744
3745 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003746 CONVERT_DOUBLE_CHECKED(value, args[0]);
3747 if (isnan(value)) {
3748 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3749 }
3750 if (isinf(value)) {
3751 if (value < 0) {
3752 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3753 }
3754 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3755 }
3756 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
3757 int radix = FastD2I(radix_number);
3758 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3759 char* str = DoubleToRadixCString(value, radix);
3760 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3761 DeleteArray(str);
3762 return result;
3763}
3764
3765
3766static Object* Runtime_NumberToFixed(Arguments args) {
3767 NoHandleAllocation ha;
3768 ASSERT(args.length() == 2);
3769
3770 CONVERT_DOUBLE_CHECKED(value, args[0]);
3771 if (isnan(value)) {
3772 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3773 }
3774 if (isinf(value)) {
3775 if (value < 0) {
3776 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3777 }
3778 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3779 }
3780 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3781 int f = FastD2I(f_number);
3782 RUNTIME_ASSERT(f >= 0);
3783 char* str = DoubleToFixedCString(value, f);
3784 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3785 DeleteArray(str);
3786 return res;
3787}
3788
3789
3790static Object* Runtime_NumberToExponential(Arguments args) {
3791 NoHandleAllocation ha;
3792 ASSERT(args.length() == 2);
3793
3794 CONVERT_DOUBLE_CHECKED(value, args[0]);
3795 if (isnan(value)) {
3796 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3797 }
3798 if (isinf(value)) {
3799 if (value < 0) {
3800 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3801 }
3802 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3803 }
3804 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3805 int f = FastD2I(f_number);
3806 RUNTIME_ASSERT(f >= -1 && f <= 20);
3807 char* str = DoubleToExponentialCString(value, f);
3808 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3809 DeleteArray(str);
3810 return res;
3811}
3812
3813
3814static Object* Runtime_NumberToPrecision(Arguments args) {
3815 NoHandleAllocation ha;
3816 ASSERT(args.length() == 2);
3817
3818 CONVERT_DOUBLE_CHECKED(value, args[0]);
3819 if (isnan(value)) {
3820 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3821 }
3822 if (isinf(value)) {
3823 if (value < 0) {
3824 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3825 }
3826 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3827 }
3828 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3829 int f = FastD2I(f_number);
3830 RUNTIME_ASSERT(f >= 1 && f <= 21);
3831 char* str = DoubleToPrecisionCString(value, f);
3832 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3833 DeleteArray(str);
3834 return res;
3835}
3836
3837
3838// Returns a single character string where first character equals
3839// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003840static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003841 if (index < static_cast<uint32_t>(string->length())) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003842 string->TryFlatten();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003843 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003844 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003845 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003846 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003847}
3848
3849
3850Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
3851 // Handle [] indexing on Strings
3852 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003853 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
3854 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003855 }
3856
3857 // Handle [] indexing on String objects
3858 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003859 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
3860 Handle<Object> result =
3861 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
3862 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003863 }
3864
3865 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003866 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003867 return prototype->GetElement(index);
3868 }
3869
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003870 return GetElement(object, index);
3871}
3872
3873
3874Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003875 return object->GetElement(index);
3876}
3877
3878
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003879Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
3880 HandleScope scope;
3881
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003882 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003883 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003884 Handle<Object> error =
3885 Factory::NewTypeError("non_object_property_load",
3886 HandleVector(args, 2));
3887 return Top::Throw(*error);
3888 }
3889
3890 // Check if the given key is an array index.
3891 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003892 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003893 return GetElementOrCharAt(object, index);
3894 }
3895
3896 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003897 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003898 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003899 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003900 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003901 bool has_pending_exception = false;
3902 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003903 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003904 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003905 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003906 }
3907
ager@chromium.org32912102009-01-16 10:38:43 +00003908 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003909 // the element if so.
3910 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003911 return GetElementOrCharAt(object, index);
3912 } else {
3913 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003914 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003915 }
3916}
3917
3918
3919static Object* Runtime_GetProperty(Arguments args) {
3920 NoHandleAllocation ha;
3921 ASSERT(args.length() == 2);
3922
3923 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003924 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003925
3926 return Runtime::GetObjectProperty(object, key);
3927}
3928
3929
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003930// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003931static Object* Runtime_KeyedGetProperty(Arguments args) {
3932 NoHandleAllocation ha;
3933 ASSERT(args.length() == 2);
3934
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003935 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00003936 // itself.
3937 //
3938 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00003939 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00003940 // global proxy object never has properties. This is the case
3941 // because the global proxy object forwards everything to its hidden
3942 // prototype including local lookups.
3943 //
3944 // Additionally, we need to make sure that we do not cache results
3945 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003946 if (args[0]->IsJSObject() &&
3947 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00003948 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003949 args[1]->IsString()) {
3950 JSObject* receiver = JSObject::cast(args[0]);
3951 String* key = String::cast(args[1]);
3952 if (receiver->HasFastProperties()) {
3953 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003954 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003955 int offset = KeyedLookupCache::Lookup(receiver_map, key);
3956 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003957 Object* value = receiver->FastPropertyAt(offset);
3958 return value->IsTheHole() ? Heap::undefined_value() : value;
3959 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003960 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003961 LookupResult result;
3962 receiver->LocalLookup(key, &result);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00003963 if (result.IsProperty() && result.type() == FIELD) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003964 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003965 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003966 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003967 }
3968 } else {
3969 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003970 StringDictionary* dictionary = receiver->property_dictionary();
3971 int entry = dictionary->FindEntry(key);
3972 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003973 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003974 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003975 if (!receiver->IsGlobalObject()) return value;
3976 value = JSGlobalPropertyCell::cast(value)->value();
3977 if (!value->IsTheHole()) return value;
3978 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003979 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003980 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003981 } else if (args[0]->IsString() && args[1]->IsSmi()) {
3982 // Fast case for string indexing using [] with a smi index.
3983 HandleScope scope;
3984 Handle<String> str = args.at<String>(0);
3985 int index = Smi::cast(args[1])->value();
3986 Handle<Object> result = GetCharAt(str, index);
3987 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003988 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003989
3990 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003991 return Runtime::GetObjectProperty(args.at<Object>(0),
3992 args.at<Object>(1));
3993}
3994
3995
ager@chromium.org5c838252010-02-19 08:53:10 +00003996static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
3997 ASSERT(args.length() == 5);
3998 HandleScope scope;
3999 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4000 CONVERT_CHECKED(String, name, args[1]);
4001 CONVERT_CHECKED(Smi, flag_setter, args[2]);
4002 CONVERT_CHECKED(JSFunction, fun, args[3]);
4003 CONVERT_CHECKED(Smi, flag_attr, args[4]);
4004 int unchecked = flag_attr->value();
4005 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4006 RUNTIME_ASSERT(!obj->IsNull());
4007 LookupResult result;
4008 obj->LocalLookupRealNamedProperty(name, &result);
4009
4010 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
4011 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
4012 // delete it to avoid running into trouble in DefineAccessor, which
4013 // handles this incorrectly if the property is readonly (does nothing)
4014 if (result.IsProperty() &&
4015 (result.type() == FIELD || result.type() == NORMAL
4016 || result.type() == CONSTANT_FUNCTION)) {
4017 obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
4018 }
4019 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
4020}
4021
4022static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
4023 ASSERT(args.length() == 4);
4024 HandleScope scope;
4025 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
4026 CONVERT_ARG_CHECKED(String, name, 1);
4027 Handle<Object> obj_value = args.at<Object>(2);
4028
4029 CONVERT_CHECKED(Smi, flag, args[3]);
4030 int unchecked = flag->value();
4031 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4032
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004033 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
4034
4035 // Check if this is an element.
4036 uint32_t index;
4037 bool is_element = name->AsArrayIndex(&index);
4038
4039 // Special case for elements if any of the flags are true.
4040 // If elements are in fast case we always implicitly assume that:
4041 // DONT_DELETE: false, DONT_ENUM: false, READ_ONLY: false.
4042 if (((unchecked & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) &&
4043 is_element) {
4044 // Normalize the elements to enable attributes on the property.
4045 js_object->NormalizeElements();
4046 NumberDictionary* dictionary = js_object->element_dictionary();
4047 // Make sure that we never go back to fast case.
4048 dictionary->set_requires_slow_elements();
4049 PropertyDetails details = PropertyDetails(attr, NORMAL);
4050 dictionary->Set(index, *obj_value, details);
4051 }
4052
ager@chromium.org5c838252010-02-19 08:53:10 +00004053 LookupResult result;
4054 js_object->LocalLookupRealNamedProperty(*name, &result);
4055
ager@chromium.org5c838252010-02-19 08:53:10 +00004056 // Take special care when attributes are different and there is already
4057 // a property. For simplicity we normalize the property which enables us
4058 // to not worry about changing the instance_descriptor and creating a new
4059 // map. The current version of SetObjectProperty does not handle attributes
4060 // correctly in the case where a property is a field and is reset with
4061 // new attributes.
4062 if (result.IsProperty() && attr != result.GetAttributes()) {
4063 // New attributes - normalize to avoid writing to instance descriptor
4064 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
4065 // Use IgnoreAttributes version since a readonly property may be
4066 // overridden and SetProperty does not allow this.
4067 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
4068 *obj_value,
4069 attr);
4070 }
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004071
ager@chromium.org5c838252010-02-19 08:53:10 +00004072 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
4073}
4074
4075
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004076Object* Runtime::SetObjectProperty(Handle<Object> object,
4077 Handle<Object> key,
4078 Handle<Object> value,
4079 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004080 HandleScope scope;
4081
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004082 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004083 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004084 Handle<Object> error =
4085 Factory::NewTypeError("non_object_property_store",
4086 HandleVector(args, 2));
4087 return Top::Throw(*error);
4088 }
4089
4090 // If the object isn't a JavaScript object, we ignore the store.
4091 if (!object->IsJSObject()) return *value;
4092
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004093 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
4094
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004095 // Check if the given key is an array index.
4096 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004097 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004098 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
4099 // of a string using [] notation. We need to support this too in
4100 // JavaScript.
4101 // In the case of a String object we just need to redirect the assignment to
4102 // the underlying string if the index is in range. Since the underlying
4103 // string does nothing with the assignment then we can ignore such
4104 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004105 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004106 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004107 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004108
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004109 Handle<Object> result = SetElement(js_object, index, value);
4110 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004111 return *value;
4112 }
4113
4114 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004115 Handle<Object> result;
4116 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004117 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004118 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004119 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004120 key_string->TryFlatten();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004121 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004122 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004123 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004124 return *value;
4125 }
4126
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004127 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004128 bool has_pending_exception = false;
4129 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4130 if (has_pending_exception) return Failure::Exception();
4131 Handle<String> name = Handle<String>::cast(converted);
4132
4133 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004134 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004135 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004136 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004137 }
4138}
4139
4140
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004141Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
4142 Handle<Object> key,
4143 Handle<Object> value,
4144 PropertyAttributes attr) {
4145 HandleScope scope;
4146
4147 // Check if the given key is an array index.
4148 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004149 if (key->ToArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004150 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
4151 // of a string using [] notation. We need to support this too in
4152 // JavaScript.
4153 // In the case of a String object we just need to redirect the assignment to
4154 // the underlying string if the index is in range. Since the underlying
4155 // string does nothing with the assignment then we can ignore such
4156 // assignments.
4157 if (js_object->IsStringObjectWithCharacterAt(index)) {
4158 return *value;
4159 }
4160
4161 return js_object->SetElement(index, *value);
4162 }
4163
4164 if (key->IsString()) {
4165 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004166 return js_object->SetElement(index, *value);
4167 } else {
4168 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004169 key_string->TryFlatten();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004170 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
4171 *value,
4172 attr);
4173 }
4174 }
4175
4176 // Call-back into JavaScript to convert the key to a string.
4177 bool has_pending_exception = false;
4178 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4179 if (has_pending_exception) return Failure::Exception();
4180 Handle<String> name = Handle<String>::cast(converted);
4181
4182 if (name->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004183 return js_object->SetElement(index, *value);
4184 } else {
4185 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
4186 }
4187}
4188
4189
ager@chromium.orge2902be2009-06-08 12:21:35 +00004190Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
4191 Handle<Object> key) {
4192 HandleScope scope;
4193
4194 // Check if the given key is an array index.
4195 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004196 if (key->ToArrayIndex(&index)) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00004197 // In Firefox/SpiderMonkey, Safari and Opera you can access the
4198 // characters of a string using [] notation. In the case of a
4199 // String object we just need to redirect the deletion to the
4200 // underlying string if the index is in range. Since the
4201 // underlying string does nothing with the deletion, we can ignore
4202 // such deletions.
4203 if (js_object->IsStringObjectWithCharacterAt(index)) {
4204 return Heap::true_value();
4205 }
4206
4207 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
4208 }
4209
4210 Handle<String> key_string;
4211 if (key->IsString()) {
4212 key_string = Handle<String>::cast(key);
4213 } else {
4214 // Call-back into JavaScript to convert the key to a string.
4215 bool has_pending_exception = false;
4216 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4217 if (has_pending_exception) return Failure::Exception();
4218 key_string = Handle<String>::cast(converted);
4219 }
4220
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004221 key_string->TryFlatten();
ager@chromium.orge2902be2009-06-08 12:21:35 +00004222 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
4223}
4224
4225
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004226static Object* Runtime_SetProperty(Arguments args) {
4227 NoHandleAllocation ha;
4228 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
4229
4230 Handle<Object> object = args.at<Object>(0);
4231 Handle<Object> key = args.at<Object>(1);
4232 Handle<Object> value = args.at<Object>(2);
4233
4234 // Compute attributes.
4235 PropertyAttributes attributes = NONE;
4236 if (args.length() == 4) {
4237 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004238 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004239 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004240 RUNTIME_ASSERT(
4241 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4242 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004243 }
4244 return Runtime::SetObjectProperty(object, key, value, attributes);
4245}
4246
4247
4248// Set a local property, even if it is READ_ONLY. If the property does not
4249// exist, it will be added with attributes NONE.
4250static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
4251 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004252 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004253 CONVERT_CHECKED(JSObject, object, args[0]);
4254 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004255 // Compute attributes.
4256 PropertyAttributes attributes = NONE;
4257 if (args.length() == 4) {
4258 CONVERT_CHECKED(Smi, value_obj, args[3]);
4259 int unchecked_value = value_obj->value();
4260 // Only attribute bits should be set.
4261 RUNTIME_ASSERT(
4262 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4263 attributes = static_cast<PropertyAttributes>(unchecked_value);
4264 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004265
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004266 return object->
4267 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004268}
4269
4270
4271static Object* Runtime_DeleteProperty(Arguments args) {
4272 NoHandleAllocation ha;
4273 ASSERT(args.length() == 2);
4274
4275 CONVERT_CHECKED(JSObject, object, args[0]);
4276 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00004277 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004278}
4279
4280
ager@chromium.org9085a012009-05-11 19:22:57 +00004281static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
4282 Handle<String> key) {
4283 if (object->HasLocalProperty(*key)) return Heap::true_value();
4284 // Handle hidden prototypes. If there's a hidden prototype above this thing
4285 // then we have to check it for properties, because they are supposed to
4286 // look like they are on this object.
4287 Handle<Object> proto(object->GetPrototype());
4288 if (proto->IsJSObject() &&
4289 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
4290 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
4291 }
4292 return Heap::false_value();
4293}
4294
4295
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004296static Object* Runtime_HasLocalProperty(Arguments args) {
4297 NoHandleAllocation ha;
4298 ASSERT(args.length() == 2);
4299 CONVERT_CHECKED(String, key, args[1]);
4300
ager@chromium.org9085a012009-05-11 19:22:57 +00004301 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004302 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00004303 if (obj->IsJSObject()) {
4304 JSObject* object = JSObject::cast(obj);
4305 // Fast case - no interceptors.
4306 if (object->HasRealNamedProperty(key)) return Heap::true_value();
4307 // Slow case. Either it's not there or we have an interceptor. We should
4308 // have handles for this kind of deal.
4309 HandleScope scope;
4310 return HasLocalPropertyImplementation(Handle<JSObject>(object),
4311 Handle<String>(key));
4312 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004313 // Well, there is one exception: Handle [] on strings.
4314 uint32_t index;
4315 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00004316 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004317 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004318 return Heap::true_value();
4319 }
4320 }
4321 return Heap::false_value();
4322}
4323
4324
4325static Object* Runtime_HasProperty(Arguments args) {
4326 NoHandleAllocation na;
4327 ASSERT(args.length() == 2);
4328
4329 // Only JS objects can have properties.
4330 if (args[0]->IsJSObject()) {
4331 JSObject* object = JSObject::cast(args[0]);
4332 CONVERT_CHECKED(String, key, args[1]);
4333 if (object->HasProperty(key)) return Heap::true_value();
4334 }
4335 return Heap::false_value();
4336}
4337
4338
4339static Object* Runtime_HasElement(Arguments args) {
4340 NoHandleAllocation na;
4341 ASSERT(args.length() == 2);
4342
4343 // Only JS objects can have elements.
4344 if (args[0]->IsJSObject()) {
4345 JSObject* object = JSObject::cast(args[0]);
4346 CONVERT_CHECKED(Smi, index_obj, args[1]);
4347 uint32_t index = index_obj->value();
4348 if (object->HasElement(index)) return Heap::true_value();
4349 }
4350 return Heap::false_value();
4351}
4352
4353
4354static Object* Runtime_IsPropertyEnumerable(Arguments args) {
4355 NoHandleAllocation ha;
4356 ASSERT(args.length() == 2);
4357
4358 CONVERT_CHECKED(JSObject, object, args[0]);
4359 CONVERT_CHECKED(String, key, args[1]);
4360
4361 uint32_t index;
4362 if (key->AsArrayIndex(&index)) {
4363 return Heap::ToBoolean(object->HasElement(index));
4364 }
4365
ager@chromium.org870a0b62008-11-04 11:43:05 +00004366 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
4367 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004368}
4369
4370
4371static Object* Runtime_GetPropertyNames(Arguments args) {
4372 HandleScope scope;
4373 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004374 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004375 return *GetKeysFor(object);
4376}
4377
4378
4379// Returns either a FixedArray as Runtime_GetPropertyNames,
4380// or, if the given object has an enum cache that contains
4381// all enumerable properties of the object and its prototypes
4382// have none, the map of the object. This is used to speed up
4383// the check for deletions during a for-in.
4384static Object* Runtime_GetPropertyNamesFast(Arguments args) {
4385 ASSERT(args.length() == 1);
4386
4387 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4388
4389 if (raw_object->IsSimpleEnum()) return raw_object->map();
4390
4391 HandleScope scope;
4392 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004393 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
4394 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004395
4396 // Test again, since cache may have been built by preceding call.
4397 if (object->IsSimpleEnum()) return object->map();
4398
4399 return *content;
4400}
4401
4402
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004403// Find the length of the prototype chain that is to to handled as one. If a
4404// prototype object is hidden it is to be viewed as part of the the object it
4405// is prototype for.
4406static int LocalPrototypeChainLength(JSObject* obj) {
4407 int count = 1;
4408 Object* proto = obj->GetPrototype();
4409 while (proto->IsJSObject() &&
4410 JSObject::cast(proto)->map()->is_hidden_prototype()) {
4411 count++;
4412 proto = JSObject::cast(proto)->GetPrototype();
4413 }
4414 return count;
4415}
4416
4417
4418// Return the names of the local named properties.
4419// args[0]: object
4420static Object* Runtime_GetLocalPropertyNames(Arguments args) {
4421 HandleScope scope;
4422 ASSERT(args.length() == 1);
4423 if (!args[0]->IsJSObject()) {
4424 return Heap::undefined_value();
4425 }
4426 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4427
4428 // Skip the global proxy as it has no properties and always delegates to the
4429 // real global object.
4430 if (obj->IsJSGlobalProxy()) {
4431 // Only collect names if access is permitted.
4432 if (obj->IsAccessCheckNeeded() &&
4433 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
4434 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
4435 return *Factory::NewJSArray(0);
4436 }
4437 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
4438 }
4439
4440 // Find the number of objects making up this.
4441 int length = LocalPrototypeChainLength(*obj);
4442
4443 // Find the number of local properties for each of the objects.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004444 ScopedVector<int> local_property_count(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004445 int total_property_count = 0;
4446 Handle<JSObject> jsproto = obj;
4447 for (int i = 0; i < length; i++) {
4448 // Only collect names if access is permitted.
4449 if (jsproto->IsAccessCheckNeeded() &&
4450 !Top::MayNamedAccess(*jsproto,
4451 Heap::undefined_value(),
4452 v8::ACCESS_KEYS)) {
4453 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
4454 return *Factory::NewJSArray(0);
4455 }
4456 int n;
4457 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4458 local_property_count[i] = n;
4459 total_property_count += n;
4460 if (i < length - 1) {
4461 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4462 }
4463 }
4464
4465 // Allocate an array with storage for all the property names.
4466 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
4467
4468 // Get the property names.
4469 jsproto = obj;
4470 int proto_with_hidden_properties = 0;
4471 for (int i = 0; i < length; i++) {
4472 jsproto->GetLocalPropertyNames(*names,
4473 i == 0 ? 0 : local_property_count[i - 1]);
4474 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
4475 proto_with_hidden_properties++;
4476 }
4477 if (i < length - 1) {
4478 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4479 }
4480 }
4481
4482 // Filter out name of hidden propeties object.
4483 if (proto_with_hidden_properties > 0) {
4484 Handle<FixedArray> old_names = names;
4485 names = Factory::NewFixedArray(
4486 names->length() - proto_with_hidden_properties);
4487 int dest_pos = 0;
4488 for (int i = 0; i < total_property_count; i++) {
4489 Object* name = old_names->get(i);
4490 if (name == Heap::hidden_symbol()) {
4491 continue;
4492 }
4493 names->set(dest_pos++, name);
4494 }
4495 }
4496
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004497 return *Factory::NewJSArrayWithElements(names);
4498}
4499
4500
4501// Return the names of the local indexed properties.
4502// args[0]: object
4503static Object* Runtime_GetLocalElementNames(Arguments args) {
4504 HandleScope scope;
4505 ASSERT(args.length() == 1);
4506 if (!args[0]->IsJSObject()) {
4507 return Heap::undefined_value();
4508 }
4509 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4510
4511 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4512 Handle<FixedArray> names = Factory::NewFixedArray(n);
4513 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4514 return *Factory::NewJSArrayWithElements(names);
4515}
4516
4517
4518// Return information on whether an object has a named or indexed interceptor.
4519// args[0]: object
4520static Object* Runtime_GetInterceptorInfo(Arguments args) {
4521 HandleScope scope;
4522 ASSERT(args.length() == 1);
4523 if (!args[0]->IsJSObject()) {
4524 return Smi::FromInt(0);
4525 }
4526 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4527
4528 int result = 0;
4529 if (obj->HasNamedInterceptor()) result |= 2;
4530 if (obj->HasIndexedInterceptor()) result |= 1;
4531
4532 return Smi::FromInt(result);
4533}
4534
4535
4536// Return property names from named interceptor.
4537// args[0]: object
4538static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
4539 HandleScope scope;
4540 ASSERT(args.length() == 1);
4541 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4542
4543 if (obj->HasNamedInterceptor()) {
4544 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4545 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4546 }
4547 return Heap::undefined_value();
4548}
4549
4550
4551// Return element names from indexed interceptor.
4552// args[0]: object
4553static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
4554 HandleScope scope;
4555 ASSERT(args.length() == 1);
4556 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4557
4558 if (obj->HasIndexedInterceptor()) {
4559 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4560 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4561 }
4562 return Heap::undefined_value();
4563}
4564
4565
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004566static Object* Runtime_LocalKeys(Arguments args) {
4567 ASSERT_EQ(args.length(), 1);
4568 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4569 HandleScope scope;
4570 Handle<JSObject> object(raw_object);
4571 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
4572 LOCAL_ONLY);
4573 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
4574 // property array and since the result is mutable we have to create
4575 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004576 int length = contents->length();
4577 Handle<FixedArray> copy = Factory::NewFixedArray(length);
4578 for (int i = 0; i < length; i++) {
4579 Object* entry = contents->get(i);
4580 if (entry->IsString()) {
4581 copy->set(i, entry);
4582 } else {
4583 ASSERT(entry->IsNumber());
4584 HandleScope scope;
4585 Handle<Object> entry_handle(entry);
4586 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
4587 copy->set(i, *entry_str);
4588 }
4589 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004590 return *Factory::NewJSArrayWithElements(copy);
4591}
4592
4593
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004594static Object* Runtime_GetArgumentsProperty(Arguments args) {
4595 NoHandleAllocation ha;
4596 ASSERT(args.length() == 1);
4597
4598 // Compute the frame holding the arguments.
4599 JavaScriptFrameIterator it;
4600 it.AdvanceToArgumentsFrame();
4601 JavaScriptFrame* frame = it.frame();
4602
4603 // Get the actual number of provided arguments.
4604 const uint32_t n = frame->GetProvidedParametersCount();
4605
4606 // Try to convert the key to an index. If successful and within
4607 // index return the the argument from the frame.
4608 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004609 if (args[0]->ToArrayIndex(&index) && index < n) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004610 return frame->GetParameter(index);
4611 }
4612
4613 // Convert the key to a string.
4614 HandleScope scope;
4615 bool exception = false;
4616 Handle<Object> converted =
4617 Execution::ToString(args.at<Object>(0), &exception);
4618 if (exception) return Failure::Exception();
4619 Handle<String> key = Handle<String>::cast(converted);
4620
4621 // Try to convert the string key into an array index.
4622 if (key->AsArrayIndex(&index)) {
4623 if (index < n) {
4624 return frame->GetParameter(index);
4625 } else {
4626 return Top::initial_object_prototype()->GetElement(index);
4627 }
4628 }
4629
4630 // Handle special arguments properties.
4631 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
4632 if (key->Equals(Heap::callee_symbol())) return frame->function();
4633
4634 // Lookup in the initial Object.prototype object.
4635 return Top::initial_object_prototype()->GetProperty(*key);
4636}
4637
4638
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004639static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004640 HandleScope scope;
4641
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004642 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004643 Handle<Object> object = args.at<Object>(0);
4644 if (object->IsJSObject()) {
4645 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00004646 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
4647 js_object->TransformToFastProperties(0);
4648 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004649 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004650 return *object;
4651}
4652
4653
4654static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004655 HandleScope scope;
4656
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004657 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004658 Handle<Object> object = args.at<Object>(0);
4659 if (object->IsJSObject()) {
4660 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004661 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004662 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004663 return *object;
4664}
4665
4666
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004667static Object* Runtime_ToBool(Arguments args) {
4668 NoHandleAllocation ha;
4669 ASSERT(args.length() == 1);
4670
4671 return args[0]->ToBoolean();
4672}
4673
4674
4675// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
4676// Possible optimizations: put the type string into the oddballs.
4677static Object* Runtime_Typeof(Arguments args) {
4678 NoHandleAllocation ha;
4679
4680 Object* obj = args[0];
4681 if (obj->IsNumber()) return Heap::number_symbol();
4682 HeapObject* heap_obj = HeapObject::cast(obj);
4683
4684 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004685 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004686
4687 InstanceType instance_type = heap_obj->map()->instance_type();
4688 if (instance_type < FIRST_NONSTRING_TYPE) {
4689 return Heap::string_symbol();
4690 }
4691
4692 switch (instance_type) {
4693 case ODDBALL_TYPE:
4694 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
4695 return Heap::boolean_symbol();
4696 }
4697 if (heap_obj->IsNull()) {
4698 return Heap::object_symbol();
4699 }
4700 ASSERT(heap_obj->IsUndefined());
4701 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004702 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004703 return Heap::function_symbol();
4704 default:
4705 // For any kind of object not handled above, the spec rule for
4706 // host objects gives that it is okay to return "object"
4707 return Heap::object_symbol();
4708 }
4709}
4710
4711
lrn@chromium.org25156de2010-04-06 13:10:27 +00004712static bool AreDigits(const char*s, int from, int to) {
4713 for (int i = from; i < to; i++) {
4714 if (s[i] < '0' || s[i] > '9') return false;
4715 }
4716
4717 return true;
4718}
4719
4720
4721static int ParseDecimalInteger(const char*s, int from, int to) {
4722 ASSERT(to - from < 10); // Overflow is not possible.
4723 ASSERT(from < to);
4724 int d = s[from] - '0';
4725
4726 for (int i = from + 1; i < to; i++) {
4727 d = 10 * d + (s[i] - '0');
4728 }
4729
4730 return d;
4731}
4732
4733
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004734static Object* Runtime_StringToNumber(Arguments args) {
4735 NoHandleAllocation ha;
4736 ASSERT(args.length() == 1);
4737 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004738 subject->TryFlatten();
lrn@chromium.org25156de2010-04-06 13:10:27 +00004739
4740 // Fast case: short integer or some sorts of junk values.
4741 int len = subject->length();
4742 if (subject->IsSeqAsciiString()) {
4743 if (len == 0) return Smi::FromInt(0);
4744
4745 char const* data = SeqAsciiString::cast(subject)->GetChars();
4746 bool minus = (data[0] == '-');
4747 int start_pos = (minus ? 1 : 0);
4748
4749 if (start_pos == len) {
4750 return Heap::nan_value();
4751 } else if (data[start_pos] > '9') {
4752 // Fast check for a junk value. A valid string may start from a
4753 // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit or
4754 // the 'I' character ('Infinity'). All of that have codes not greater than
4755 // '9' except 'I'.
4756 if (data[start_pos] != 'I') {
4757 return Heap::nan_value();
4758 }
4759 } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
4760 // The maximal/minimal smi has 10 digits. If the string has less digits we
4761 // know it will fit into the smi-data type.
4762 int d = ParseDecimalInteger(data, start_pos, len);
4763 if (minus) {
4764 if (d == 0) return Heap::minus_zero_value();
4765 d = -d;
4766 }
4767 return Smi::FromInt(d);
4768 }
4769 }
4770
4771 // Slower case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004772 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
4773}
4774
4775
4776static Object* Runtime_StringFromCharCodeArray(Arguments args) {
4777 NoHandleAllocation ha;
4778 ASSERT(args.length() == 1);
4779
4780 CONVERT_CHECKED(JSArray, codes, args[0]);
4781 int length = Smi::cast(codes->length())->value();
4782
4783 // Check if the string can be ASCII.
4784 int i;
4785 for (i = 0; i < length; i++) {
4786 Object* element = codes->GetElement(i);
4787 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
4788 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
4789 break;
4790 }
4791
4792 Object* object = NULL;
4793 if (i == length) { // The string is ASCII.
4794 object = Heap::AllocateRawAsciiString(length);
4795 } else { // The string is not ASCII.
4796 object = Heap::AllocateRawTwoByteString(length);
4797 }
4798
4799 if (object->IsFailure()) return object;
4800 String* result = String::cast(object);
4801 for (int i = 0; i < length; i++) {
4802 Object* element = codes->GetElement(i);
4803 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004804 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004805 }
4806 return result;
4807}
4808
4809
4810// kNotEscaped is generated by the following:
4811//
4812// #!/bin/perl
4813// for (my $i = 0; $i < 256; $i++) {
4814// print "\n" if $i % 16 == 0;
4815// my $c = chr($i);
4816// my $escaped = 1;
4817// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
4818// print $escaped ? "0, " : "1, ";
4819// }
4820
4821
4822static bool IsNotEscaped(uint16_t character) {
4823 // Only for 8 bit characters, the rest are always escaped (in a different way)
4824 ASSERT(character < 256);
4825 static const char kNotEscaped[256] = {
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, 0, 0, 0, 0, 0, 0,
4828 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
4829 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
4830 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4831 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
4832 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4833 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4842 };
4843 return kNotEscaped[character] != 0;
4844}
4845
4846
4847static Object* Runtime_URIEscape(Arguments args) {
4848 const char hex_chars[] = "0123456789ABCDEF";
4849 NoHandleAllocation ha;
4850 ASSERT(args.length() == 1);
4851 CONVERT_CHECKED(String, source, args[0]);
4852
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004853 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004854
4855 int escaped_length = 0;
4856 int length = source->length();
4857 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004858 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004859 buffer->Reset(source);
4860 while (buffer->has_more()) {
4861 uint16_t character = buffer->GetNext();
4862 if (character >= 256) {
4863 escaped_length += 6;
4864 } else if (IsNotEscaped(character)) {
4865 escaped_length++;
4866 } else {
4867 escaped_length += 3;
4868 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004869 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004870 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004871 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004872 Top::context()->mark_out_of_memory();
4873 return Failure::OutOfMemoryException();
4874 }
4875 }
4876 }
4877 // No length change implies no change. Return original string if no change.
4878 if (escaped_length == length) {
4879 return source;
4880 }
4881 Object* o = Heap::AllocateRawAsciiString(escaped_length);
4882 if (o->IsFailure()) return o;
4883 String* destination = String::cast(o);
4884 int dest_position = 0;
4885
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004886 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004887 buffer->Rewind();
4888 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00004889 uint16_t chr = buffer->GetNext();
4890 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004891 destination->Set(dest_position, '%');
4892 destination->Set(dest_position+1, 'u');
4893 destination->Set(dest_position+2, hex_chars[chr >> 12]);
4894 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
4895 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
4896 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004897 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00004898 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004899 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004900 dest_position++;
4901 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004902 destination->Set(dest_position, '%');
4903 destination->Set(dest_position+1, hex_chars[chr >> 4]);
4904 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004905 dest_position += 3;
4906 }
4907 }
4908 return destination;
4909}
4910
4911
4912static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
4913 static const signed char kHexValue['g'] = {
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 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4917 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
4918 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4919 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4920 -1, 10, 11, 12, 13, 14, 15 };
4921
4922 if (character1 > 'f') return -1;
4923 int hi = kHexValue[character1];
4924 if (hi == -1) return -1;
4925 if (character2 > 'f') return -1;
4926 int lo = kHexValue[character2];
4927 if (lo == -1) return -1;
4928 return (hi << 4) + lo;
4929}
4930
4931
ager@chromium.org870a0b62008-11-04 11:43:05 +00004932static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004933 int i,
4934 int length,
4935 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004936 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00004937 int32_t hi = 0;
4938 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004939 if (character == '%' &&
4940 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004941 source->Get(i + 1) == 'u' &&
4942 (hi = TwoDigitHex(source->Get(i + 2),
4943 source->Get(i + 3))) != -1 &&
4944 (lo = TwoDigitHex(source->Get(i + 4),
4945 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004946 *step = 6;
4947 return (hi << 8) + lo;
4948 } else if (character == '%' &&
4949 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004950 (lo = TwoDigitHex(source->Get(i + 1),
4951 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004952 *step = 3;
4953 return lo;
4954 } else {
4955 *step = 1;
4956 return character;
4957 }
4958}
4959
4960
4961static Object* Runtime_URIUnescape(Arguments args) {
4962 NoHandleAllocation ha;
4963 ASSERT(args.length() == 1);
4964 CONVERT_CHECKED(String, source, args[0]);
4965
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004966 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004967
4968 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004969 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004970
4971 int unescaped_length = 0;
4972 for (int i = 0; i < length; unescaped_length++) {
4973 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004974 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004975 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004976 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004977 i += step;
4978 }
4979
4980 // No length change implies no change. Return original string if no change.
4981 if (unescaped_length == length)
4982 return source;
4983
4984 Object* o = ascii ?
4985 Heap::AllocateRawAsciiString(unescaped_length) :
4986 Heap::AllocateRawTwoByteString(unescaped_length);
4987 if (o->IsFailure()) return o;
4988 String* destination = String::cast(o);
4989
4990 int dest_position = 0;
4991 for (int i = 0; i < length; dest_position++) {
4992 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004993 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004994 i += step;
4995 }
4996 return destination;
4997}
4998
4999
5000static Object* Runtime_StringParseInt(Arguments args) {
5001 NoHandleAllocation ha;
5002
5003 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005004 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005005
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005006 s->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005007
lrn@chromium.org25156de2010-04-06 13:10:27 +00005008 RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
5009 double value = StringToInt(s, radix);
5010 return Heap::NumberFromDouble(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005011 return Heap::nan_value();
5012}
5013
5014
5015static Object* Runtime_StringParseFloat(Arguments args) {
5016 NoHandleAllocation ha;
5017 CONVERT_CHECKED(String, str, args[0]);
5018
5019 // ECMA-262 section 15.1.2.3, empty string is NaN
5020 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
5021
5022 // Create a number object from the value.
5023 return Heap::NumberFromDouble(value);
5024}
5025
5026
5027static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
5028static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
5029
5030
5031template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005032static Object* ConvertCaseHelper(String* s,
5033 int length,
5034 int input_string_length,
5035 unibrow::Mapping<Converter, 128>* mapping) {
5036 // We try this twice, once with the assumption that the result is no longer
5037 // than the input and, if that assumption breaks, again with the exact
5038 // length. This may not be pretty, but it is nicer than what was here before
5039 // and I hereby claim my vaffel-is.
5040 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005041 // Allocate the resulting string.
5042 //
5043 // NOTE: This assumes that the upper/lower case of an ascii
5044 // character is also ascii. This is currently the case, but it
5045 // might break in the future if we implement more context and locale
5046 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00005047 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005048 ? Heap::AllocateRawAsciiString(length)
5049 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005050 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005051 String* result = String::cast(o);
5052 bool has_changed_character = false;
5053
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005054 // Convert all characters to upper case, assuming that they will fit
5055 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005056 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005057 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005058 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005059 // We can assume that the string is not empty
5060 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005061 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00005062 bool has_next = buffer->has_more();
5063 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005064 int char_length = mapping->get(current, next, chars);
5065 if (char_length == 0) {
5066 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005067 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005068 i++;
5069 } else if (char_length == 1) {
5070 // Common case: converting the letter resulted in one character.
5071 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005072 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005073 has_changed_character = true;
5074 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005075 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005076 // We've assumed that the result would be as long as the
5077 // input but here is a character that converts to several
5078 // characters. No matter, we calculate the exact length
5079 // of the result and try the whole thing again.
5080 //
5081 // Note that this leaves room for optimization. We could just
5082 // memcpy what we already have to the result string. Also,
5083 // the result string is the last object allocated we could
5084 // "realloc" it and probably, in the vast majority of cases,
5085 // extend the existing string to be able to hold the full
5086 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00005087 int next_length = 0;
5088 if (has_next) {
5089 next_length = mapping->get(next, 0, chars);
5090 if (next_length == 0) next_length = 1;
5091 }
5092 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005093 while (buffer->has_more()) {
5094 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00005095 // NOTE: we use 0 as the next character here because, while
5096 // the next character may affect what a character converts to,
5097 // it does not in any case affect the length of what it convert
5098 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005099 int char_length = mapping->get(current, 0, chars);
5100 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00005101 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005102 if (current_length > Smi::kMaxValue) {
5103 Top::context()->mark_out_of_memory();
5104 return Failure::OutOfMemoryException();
5105 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005106 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005107 // Try again with the real length.
5108 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005109 } else {
5110 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005111 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005112 i++;
5113 }
5114 has_changed_character = true;
5115 }
5116 current = next;
5117 }
5118 if (has_changed_character) {
5119 return result;
5120 } else {
5121 // If we didn't actually change anything in doing the conversion
5122 // we simple return the result and let the converted string
5123 // become garbage; there is no reason to keep two identical strings
5124 // alive.
5125 return s;
5126 }
5127}
5128
5129
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005130namespace {
5131
5132struct ToLowerTraits {
5133 typedef unibrow::ToLowercase UnibrowConverter;
5134
5135 static bool ConvertAscii(char* dst, char* src, int length) {
5136 bool changed = false;
5137 for (int i = 0; i < length; ++i) {
5138 char c = src[i];
5139 if ('A' <= c && c <= 'Z') {
5140 c += ('a' - 'A');
5141 changed = true;
5142 }
5143 dst[i] = c;
5144 }
5145 return changed;
5146 }
5147};
5148
5149
5150struct ToUpperTraits {
5151 typedef unibrow::ToUppercase UnibrowConverter;
5152
5153 static bool ConvertAscii(char* dst, char* src, int length) {
5154 bool changed = false;
5155 for (int i = 0; i < length; ++i) {
5156 char c = src[i];
5157 if ('a' <= c && c <= 'z') {
5158 c -= ('a' - 'A');
5159 changed = true;
5160 }
5161 dst[i] = c;
5162 }
5163 return changed;
5164 }
5165};
5166
5167} // namespace
5168
5169
5170template <typename ConvertTraits>
5171static Object* ConvertCase(
5172 Arguments args,
5173 unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005174 NoHandleAllocation ha;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005175 CONVERT_CHECKED(String, s, args[0]);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005176 s = s->TryFlattenGetString();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005177
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005178 const int length = s->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005179 // Assume that the string is not empty; we need this assumption later
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005180 if (length == 0) return s;
5181
5182 // Simpler handling of ascii strings.
5183 //
5184 // NOTE: This assumes that the upper/lower case of an ascii
5185 // character is also ascii. This is currently the case, but it
5186 // might break in the future if we implement more context and locale
5187 // dependent upper/lower conversions.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005188 if (s->IsSeqAsciiString()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005189 Object* o = Heap::AllocateRawAsciiString(length);
5190 if (o->IsFailure()) return o;
5191 SeqAsciiString* result = SeqAsciiString::cast(o);
5192 bool has_changed_character = ConvertTraits::ConvertAscii(
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005193 result->GetChars(), SeqAsciiString::cast(s)->GetChars(), length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005194 return has_changed_character ? result : s;
5195 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005196
5197 Object* answer = ConvertCaseHelper(s, length, length, mapping);
5198 if (answer->IsSmi()) {
5199 // Retry with correct length.
5200 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
5201 }
5202 return answer; // This may be a failure.
5203}
5204
5205
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005206static Object* Runtime_StringToLowerCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005207 return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005208}
5209
5210
5211static Object* Runtime_StringToUpperCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005212 return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005213}
5214
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005215
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005216static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
5217 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
5218}
5219
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005220
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005221static Object* Runtime_StringTrim(Arguments args) {
5222 NoHandleAllocation ha;
5223 ASSERT(args.length() == 3);
5224
5225 CONVERT_CHECKED(String, s, args[0]);
5226 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
5227 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
5228
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005229 s->TryFlatten();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005230 int length = s->length();
5231
5232 int left = 0;
5233 if (trimLeft) {
5234 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
5235 left++;
5236 }
5237 }
5238
5239 int right = length;
5240 if (trimRight) {
5241 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
5242 right--;
5243 }
5244 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005245 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005246}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005247
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005248
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005249template <typename schar, typename pchar>
5250void FindStringIndices(Vector<const schar> subject,
5251 Vector<const pchar> pattern,
5252 ZoneList<int>* indices,
5253 unsigned int limit) {
5254 ASSERT(limit > 0);
5255 // Collect indices of pattern in subject, and the end-of-string index.
5256 // Stop after finding at most limit values.
5257 StringSearchStrategy strategy =
5258 InitializeStringSearch(pattern, sizeof(schar) == 1);
5259 switch (strategy) {
5260 case SEARCH_FAIL: return;
5261 case SEARCH_SHORT: {
5262 int pattern_length = pattern.length();
5263 int index = 0;
5264 while (limit > 0) {
5265 index = SimpleIndexOf(subject, pattern, index);
5266 if (index < 0) return;
5267 indices->Add(index);
5268 index += pattern_length;
5269 limit--;
5270 }
5271 return;
5272 }
5273 case SEARCH_LONG: {
5274 int pattern_length = pattern.length();
5275 int index = 0;
5276 while (limit > 0) {
5277 index = ComplexIndexOf(subject, pattern, index);
5278 if (index < 0) return;
5279 indices->Add(index);
5280 index += pattern_length;
5281 limit--;
5282 }
5283 return;
5284 }
5285 default:
5286 UNREACHABLE();
5287 return;
5288 }
5289}
5290
5291template <typename schar>
5292inline void FindCharIndices(Vector<const schar> subject,
5293 const schar pattern_char,
5294 ZoneList<int>* indices,
5295 unsigned int limit) {
5296 // Collect indices of pattern_char in subject, and the end-of-string index.
5297 // Stop after finding at most limit values.
5298 int index = 0;
5299 while (limit > 0) {
5300 index = SingleCharIndexOf(subject, pattern_char, index);
5301 if (index < 0) return;
5302 indices->Add(index);
5303 index++;
5304 limit--;
5305 }
5306}
5307
5308
5309static Object* Runtime_StringSplit(Arguments args) {
5310 ASSERT(args.length() == 3);
5311 HandleScope handle_scope;
5312 CONVERT_ARG_CHECKED(String, subject, 0);
5313 CONVERT_ARG_CHECKED(String, pattern, 1);
5314 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
5315
5316 int subject_length = subject->length();
5317 int pattern_length = pattern->length();
5318 RUNTIME_ASSERT(pattern_length > 0);
5319
5320 // The limit can be very large (0xffffffffu), but since the pattern
5321 // isn't empty, we can never create more parts than ~half the length
5322 // of the subject.
5323
5324 if (!subject->IsFlat()) FlattenString(subject);
5325
5326 static const int kMaxInitialListCapacity = 16;
5327
5328 ZoneScope scope(DELETE_ON_EXIT);
5329
5330 // Find (up to limit) indices of separator and end-of-string in subject
5331 int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
5332 ZoneList<int> indices(initial_capacity);
5333 if (pattern_length == 1) {
5334 // Special case, go directly to fast single-character split.
5335 AssertNoAllocation nogc;
5336 uc16 pattern_char = pattern->Get(0);
5337 if (subject->IsTwoByteRepresentation()) {
5338 FindCharIndices(subject->ToUC16Vector(), pattern_char,
5339 &indices,
5340 limit);
5341 } else if (pattern_char <= String::kMaxAsciiCharCode) {
5342 FindCharIndices(subject->ToAsciiVector(),
5343 static_cast<char>(pattern_char),
5344 &indices,
5345 limit);
5346 }
5347 } else {
5348 if (!pattern->IsFlat()) FlattenString(pattern);
5349 AssertNoAllocation nogc;
5350 if (subject->IsAsciiRepresentation()) {
5351 Vector<const char> subject_vector = subject->ToAsciiVector();
5352 if (pattern->IsAsciiRepresentation()) {
5353 FindStringIndices(subject_vector,
5354 pattern->ToAsciiVector(),
5355 &indices,
5356 limit);
5357 } else {
5358 FindStringIndices(subject_vector,
5359 pattern->ToUC16Vector(),
5360 &indices,
5361 limit);
5362 }
5363 } else {
5364 Vector<const uc16> subject_vector = subject->ToUC16Vector();
5365 if (pattern->IsAsciiRepresentation()) {
5366 FindStringIndices(subject_vector,
5367 pattern->ToAsciiVector(),
5368 &indices,
5369 limit);
5370 } else {
5371 FindStringIndices(subject_vector,
5372 pattern->ToUC16Vector(),
5373 &indices,
5374 limit);
5375 }
5376 }
5377 }
5378 if (static_cast<uint32_t>(indices.length()) < limit) {
5379 indices.Add(subject_length);
5380 }
5381 // The list indices now contains the end of each part to create.
5382
5383
5384 // Create JSArray of substrings separated by separator.
5385 int part_count = indices.length();
5386
5387 Handle<JSArray> result = Factory::NewJSArray(part_count);
5388 result->set_length(Smi::FromInt(part_count));
5389
5390 ASSERT(result->HasFastElements());
5391
5392 if (part_count == 1 && indices.at(0) == subject_length) {
5393 FixedArray::cast(result->elements())->set(0, *subject);
5394 return *result;
5395 }
5396
5397 Handle<FixedArray> elements(FixedArray::cast(result->elements()));
5398 int part_start = 0;
5399 for (int i = 0; i < part_count; i++) {
5400 HandleScope local_loop_handle;
5401 int part_end = indices.at(i);
5402 Handle<String> substring =
5403 Factory::NewSubString(subject, part_start, part_end);
5404 elements->set(i, *substring);
5405 part_start = part_end + pattern_length;
5406 }
5407
5408 return *result;
5409}
5410
5411
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005412// Copies ascii characters to the given fixed array looking up
5413// one-char strings in the cache. Gives up on the first char that is
5414// not in the cache and fills the remainder with smi zeros. Returns
5415// the length of the successfully copied prefix.
5416static int CopyCachedAsciiCharsToArray(const char* chars,
5417 FixedArray* elements,
5418 int length) {
5419 AssertNoAllocation nogc;
5420 FixedArray* ascii_cache = Heap::single_character_string_cache();
5421 Object* undefined = Heap::undefined_value();
5422 int i;
5423 for (i = 0; i < length; ++i) {
5424 Object* value = ascii_cache->get(chars[i]);
5425 if (value == undefined) break;
5426 ASSERT(!Heap::InNewSpace(value));
5427 elements->set(i, value, SKIP_WRITE_BARRIER);
5428 }
5429 if (i < length) {
5430 ASSERT(Smi::FromInt(0) == 0);
5431 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
5432 }
5433#ifdef DEBUG
5434 for (int j = 0; j < length; ++j) {
5435 Object* element = elements->get(j);
5436 ASSERT(element == Smi::FromInt(0) ||
5437 (element->IsString() && String::cast(element)->LooksValid()));
5438 }
5439#endif
5440 return i;
5441}
5442
5443
5444// Converts a String to JSArray.
5445// For example, "foo" => ["f", "o", "o"].
5446static Object* Runtime_StringToArray(Arguments args) {
5447 HandleScope scope;
5448 ASSERT(args.length() == 1);
5449 CONVERT_ARG_CHECKED(String, s, 0);
5450
5451 s->TryFlatten();
5452 const int length = s->length();
5453
5454 Handle<FixedArray> elements;
5455 if (s->IsFlat() && s->IsAsciiRepresentation()) {
5456 Object* obj = Heap::AllocateUninitializedFixedArray(length);
5457 if (obj->IsFailure()) return obj;
5458 elements = Handle<FixedArray>(FixedArray::cast(obj));
5459
5460 Vector<const char> chars = s->ToAsciiVector();
5461 // Note, this will initialize all elements (not only the prefix)
5462 // to prevent GC from seeing partially initialized array.
5463 int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
5464 *elements,
5465 length);
5466
5467 for (int i = num_copied_from_cache; i < length; ++i) {
5468 elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
5469 }
5470 } else {
5471 elements = Factory::NewFixedArray(length);
5472 for (int i = 0; i < length; ++i) {
5473 elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
5474 }
5475 }
5476
5477#ifdef DEBUG
5478 for (int i = 0; i < length; ++i) {
5479 ASSERT(String::cast(elements->get(i))->length() == 1);
5480 }
5481#endif
5482
5483 return *Factory::NewJSArrayWithElements(elements);
5484}
5485
5486
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00005487bool Runtime::IsUpperCaseChar(uint16_t ch) {
5488 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
5489 int char_length = to_upper_mapping.get(ch, 0, chars);
5490 return char_length == 0;
5491}
5492
5493
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005494static Object* Runtime_NumberToString(Arguments args) {
5495 NoHandleAllocation ha;
5496 ASSERT(args.length() == 1);
5497
5498 Object* number = args[0];
5499 RUNTIME_ASSERT(number->IsNumber());
5500
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005501 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005502}
5503
5504
ager@chromium.org357bf652010-04-12 11:30:10 +00005505static Object* Runtime_NumberToStringSkipCache(Arguments args) {
5506 NoHandleAllocation ha;
5507 ASSERT(args.length() == 1);
5508
5509 Object* number = args[0];
5510 RUNTIME_ASSERT(number->IsNumber());
5511
5512 return Heap::NumberToString(number, false);
5513}
5514
5515
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005516static Object* Runtime_NumberToInteger(Arguments args) {
5517 NoHandleAllocation ha;
5518 ASSERT(args.length() == 1);
5519
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005520 CONVERT_DOUBLE_CHECKED(number, args[0]);
5521
5522 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5523 if (number > 0 && number <= Smi::kMaxValue) {
5524 return Smi::FromInt(static_cast<int>(number));
5525 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005526 return Heap::NumberFromDouble(DoubleToInteger(number));
5527}
5528
5529
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005530static Object* Runtime_NumberToIntegerMapMinusZero(Arguments args) {
5531 NoHandleAllocation ha;
5532 ASSERT(args.length() == 1);
5533
5534 CONVERT_DOUBLE_CHECKED(number, args[0]);
5535
5536 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5537 if (number > 0 && number <= Smi::kMaxValue) {
5538 return Smi::FromInt(static_cast<int>(number));
5539 }
5540
5541 double double_value = DoubleToInteger(number);
5542 // Map both -0 and +0 to +0.
5543 if (double_value == 0) double_value = 0;
5544
5545 return Heap::NumberFromDouble(double_value);
5546}
5547
5548
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005549static Object* Runtime_NumberToJSUint32(Arguments args) {
5550 NoHandleAllocation ha;
5551 ASSERT(args.length() == 1);
5552
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005553 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005554 return Heap::NumberFromUint32(number);
5555}
5556
5557
5558static Object* Runtime_NumberToJSInt32(Arguments args) {
5559 NoHandleAllocation ha;
5560 ASSERT(args.length() == 1);
5561
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005562 CONVERT_DOUBLE_CHECKED(number, args[0]);
5563
5564 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5565 if (number > 0 && number <= Smi::kMaxValue) {
5566 return Smi::FromInt(static_cast<int>(number));
5567 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005568 return Heap::NumberFromInt32(DoubleToInt32(number));
5569}
5570
5571
ager@chromium.org870a0b62008-11-04 11:43:05 +00005572// Converts a Number to a Smi, if possible. Returns NaN if the number is not
5573// a small integer.
5574static Object* Runtime_NumberToSmi(Arguments args) {
5575 NoHandleAllocation ha;
5576 ASSERT(args.length() == 1);
5577
5578 Object* obj = args[0];
5579 if (obj->IsSmi()) {
5580 return obj;
5581 }
5582 if (obj->IsHeapNumber()) {
5583 double value = HeapNumber::cast(obj)->value();
5584 int int_value = FastD2I(value);
5585 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
5586 return Smi::FromInt(int_value);
5587 }
5588 }
5589 return Heap::nan_value();
5590}
5591
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005592
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005593static Object* Runtime_NumberAdd(Arguments args) {
5594 NoHandleAllocation ha;
5595 ASSERT(args.length() == 2);
5596
5597 CONVERT_DOUBLE_CHECKED(x, args[0]);
5598 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005599 return Heap::NumberFromDouble(x + y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005600}
5601
5602
5603static Object* Runtime_NumberSub(Arguments args) {
5604 NoHandleAllocation ha;
5605 ASSERT(args.length() == 2);
5606
5607 CONVERT_DOUBLE_CHECKED(x, args[0]);
5608 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005609 return Heap::NumberFromDouble(x - y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005610}
5611
5612
5613static Object* Runtime_NumberMul(Arguments args) {
5614 NoHandleAllocation ha;
5615 ASSERT(args.length() == 2);
5616
5617 CONVERT_DOUBLE_CHECKED(x, args[0]);
5618 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005619 return Heap::NumberFromDouble(x * y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005620}
5621
5622
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005623static Object* Runtime_NumberUnaryMinus(Arguments args) {
5624 NoHandleAllocation ha;
5625 ASSERT(args.length() == 1);
5626
5627 CONVERT_DOUBLE_CHECKED(x, args[0]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005628 return Heap::NumberFromDouble(-x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005629}
5630
5631
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00005632static Object* Runtime_NumberAlloc(Arguments args) {
5633 NoHandleAllocation ha;
5634 ASSERT(args.length() == 0);
5635
5636 return Heap::NumberFromDouble(9876543210.0);
5637}
5638
5639
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005640static Object* Runtime_NumberDiv(Arguments args) {
5641 NoHandleAllocation ha;
5642 ASSERT(args.length() == 2);
5643
5644 CONVERT_DOUBLE_CHECKED(x, args[0]);
5645 CONVERT_DOUBLE_CHECKED(y, args[1]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005646 return Heap::NumberFromDouble(x / y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005647}
5648
5649
5650static Object* Runtime_NumberMod(Arguments args) {
5651 NoHandleAllocation ha;
5652 ASSERT(args.length() == 2);
5653
5654 CONVERT_DOUBLE_CHECKED(x, args[0]);
5655 CONVERT_DOUBLE_CHECKED(y, args[1]);
5656
ager@chromium.org3811b432009-10-28 14:53:37 +00005657 x = modulo(x, y);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005658 // NumberFromDouble may return a Smi instead of a Number object
5659 return Heap::NumberFromDouble(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005660}
5661
5662
5663static Object* Runtime_StringAdd(Arguments args) {
5664 NoHandleAllocation ha;
5665 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005666 CONVERT_CHECKED(String, str1, args[0]);
5667 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005668 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00005669 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005670}
5671
5672
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005673template <typename sinkchar>
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005674static inline void StringBuilderConcatHelper(String* special,
5675 sinkchar* sink,
5676 FixedArray* fixed_array,
5677 int array_length) {
5678 int position = 0;
5679 for (int i = 0; i < array_length; i++) {
5680 Object* element = fixed_array->get(i);
5681 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005682 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005683 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005684 int pos;
5685 int len;
5686 if (encoded_slice > 0) {
5687 // Position and length encoded in one smi.
5688 pos = StringBuilderSubstringPosition::decode(encoded_slice);
5689 len = StringBuilderSubstringLength::decode(encoded_slice);
5690 } else {
5691 // Position and length encoded in two smis.
5692 Object* obj = fixed_array->get(++i);
5693 ASSERT(obj->IsSmi());
5694 pos = Smi::cast(obj)->value();
5695 len = -encoded_slice;
5696 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00005697 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00005698 sink + position,
5699 pos,
5700 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005701 position += len;
5702 } else {
5703 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005704 int element_length = string->length();
5705 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005706 position += element_length;
5707 }
5708 }
5709}
5710
5711
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005712static Object* Runtime_StringBuilderConcat(Arguments args) {
5713 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005714 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005715 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005716 if (!args[1]->IsSmi()) {
5717 Top::context()->mark_out_of_memory();
5718 return Failure::OutOfMemoryException();
5719 }
5720 int array_length = Smi::cast(args[1])->value();
5721 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005722
5723 // This assumption is used by the slice encoding in one or two smis.
5724 ASSERT(Smi::kMaxValue >= String::kMaxLength);
5725
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005726 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005727 if (!array->HasFastElements()) {
5728 return Top::Throw(Heap::illegal_argument_symbol());
5729 }
5730 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005731 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005732 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005733 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005734
5735 if (array_length == 0) {
5736 return Heap::empty_string();
5737 } else if (array_length == 1) {
5738 Object* first = fixed_array->get(0);
5739 if (first->IsString()) return first;
5740 }
5741
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005742 bool ascii = special->HasOnlyAsciiChars();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005743 int position = 0;
5744 for (int i = 0; i < array_length; i++) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005745 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005746 Object* elt = fixed_array->get(i);
5747 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005748 // Smi encoding of position and length.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005749 int smi_value = Smi::cast(elt)->value();
5750 int pos;
5751 int len;
5752 if (smi_value > 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005753 // Position and length encoded in one smi.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005754 pos = StringBuilderSubstringPosition::decode(smi_value);
5755 len = StringBuilderSubstringLength::decode(smi_value);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005756 } else {
5757 // Position and length encoded in two smis.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005758 len = -smi_value;
5759 // Get the position and check that it is a positive smi.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005760 i++;
5761 if (i >= array_length) {
5762 return Top::Throw(Heap::illegal_argument_symbol());
5763 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005764 Object* next_smi = fixed_array->get(i);
5765 if (!next_smi->IsSmi()) {
5766 return Top::Throw(Heap::illegal_argument_symbol());
5767 }
5768 pos = Smi::cast(next_smi)->value();
5769 if (pos < 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005770 return Top::Throw(Heap::illegal_argument_symbol());
5771 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005772 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005773 ASSERT(pos >= 0);
5774 ASSERT(len >= 0);
5775 if (pos > special_length || len > special_length - pos) {
5776 return Top::Throw(Heap::illegal_argument_symbol());
5777 }
5778 increment = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005779 } else if (elt->IsString()) {
5780 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005781 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005782 increment = element_length;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005783 if (ascii && !element->HasOnlyAsciiChars()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005784 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005785 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005786 } else {
5787 return Top::Throw(Heap::illegal_argument_symbol());
5788 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005789 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005790 Top::context()->mark_out_of_memory();
5791 return Failure::OutOfMemoryException();
5792 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005793 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005794 }
5795
5796 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005797 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005798
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005799 if (ascii) {
5800 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005801 if (object->IsFailure()) return object;
5802 SeqAsciiString* answer = SeqAsciiString::cast(object);
5803 StringBuilderConcatHelper(special,
5804 answer->GetChars(),
5805 fixed_array,
5806 array_length);
5807 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005808 } else {
5809 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005810 if (object->IsFailure()) return object;
5811 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
5812 StringBuilderConcatHelper(special,
5813 answer->GetChars(),
5814 fixed_array,
5815 array_length);
5816 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005817 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005818}
5819
5820
5821static Object* Runtime_NumberOr(Arguments args) {
5822 NoHandleAllocation ha;
5823 ASSERT(args.length() == 2);
5824
5825 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5826 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5827 return Heap::NumberFromInt32(x | y);
5828}
5829
5830
5831static Object* Runtime_NumberAnd(Arguments args) {
5832 NoHandleAllocation ha;
5833 ASSERT(args.length() == 2);
5834
5835 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5836 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5837 return Heap::NumberFromInt32(x & y);
5838}
5839
5840
5841static Object* Runtime_NumberXor(Arguments args) {
5842 NoHandleAllocation ha;
5843 ASSERT(args.length() == 2);
5844
5845 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5846 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5847 return Heap::NumberFromInt32(x ^ y);
5848}
5849
5850
5851static Object* Runtime_NumberNot(Arguments args) {
5852 NoHandleAllocation ha;
5853 ASSERT(args.length() == 1);
5854
5855 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5856 return Heap::NumberFromInt32(~x);
5857}
5858
5859
5860static Object* Runtime_NumberShl(Arguments args) {
5861 NoHandleAllocation ha;
5862 ASSERT(args.length() == 2);
5863
5864 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5865 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5866 return Heap::NumberFromInt32(x << (y & 0x1f));
5867}
5868
5869
5870static Object* Runtime_NumberShr(Arguments args) {
5871 NoHandleAllocation ha;
5872 ASSERT(args.length() == 2);
5873
5874 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
5875 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5876 return Heap::NumberFromUint32(x >> (y & 0x1f));
5877}
5878
5879
5880static Object* Runtime_NumberSar(Arguments args) {
5881 NoHandleAllocation ha;
5882 ASSERT(args.length() == 2);
5883
5884 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5885 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5886 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
5887}
5888
5889
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005890static Object* Runtime_NumberEquals(Arguments args) {
5891 NoHandleAllocation ha;
5892 ASSERT(args.length() == 2);
5893
5894 CONVERT_DOUBLE_CHECKED(x, args[0]);
5895 CONVERT_DOUBLE_CHECKED(y, args[1]);
5896 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
5897 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
5898 if (x == y) return Smi::FromInt(EQUAL);
5899 Object* result;
5900 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
5901 result = Smi::FromInt(EQUAL);
5902 } else {
5903 result = Smi::FromInt(NOT_EQUAL);
5904 }
5905 return result;
5906}
5907
5908
5909static Object* Runtime_StringEquals(Arguments args) {
5910 NoHandleAllocation ha;
5911 ASSERT(args.length() == 2);
5912
5913 CONVERT_CHECKED(String, x, args[0]);
5914 CONVERT_CHECKED(String, y, args[1]);
5915
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005916 bool not_equal = !x->Equals(y);
5917 // This is slightly convoluted because the value that signifies
5918 // equality is 0 and inequality is 1 so we have to negate the result
5919 // from String::Equals.
5920 ASSERT(not_equal == 0 || not_equal == 1);
5921 STATIC_CHECK(EQUAL == 0);
5922 STATIC_CHECK(NOT_EQUAL == 1);
5923 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005924}
5925
5926
5927static Object* Runtime_NumberCompare(Arguments args) {
5928 NoHandleAllocation ha;
5929 ASSERT(args.length() == 3);
5930
5931 CONVERT_DOUBLE_CHECKED(x, args[0]);
5932 CONVERT_DOUBLE_CHECKED(y, args[1]);
5933 if (isnan(x) || isnan(y)) return args[2];
5934 if (x == y) return Smi::FromInt(EQUAL);
5935 if (isless(x, y)) return Smi::FromInt(LESS);
5936 return Smi::FromInt(GREATER);
5937}
5938
5939
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005940// Compare two Smis as if they were converted to strings and then
5941// compared lexicographically.
5942static Object* Runtime_SmiLexicographicCompare(Arguments args) {
5943 NoHandleAllocation ha;
5944 ASSERT(args.length() == 2);
5945
5946 // Arrays for the individual characters of the two Smis. Smis are
5947 // 31 bit integers and 10 decimal digits are therefore enough.
5948 static int x_elms[10];
5949 static int y_elms[10];
5950
5951 // Extract the integer values from the Smis.
5952 CONVERT_CHECKED(Smi, x, args[0]);
5953 CONVERT_CHECKED(Smi, y, args[1]);
5954 int x_value = x->value();
5955 int y_value = y->value();
5956
5957 // If the integers are equal so are the string representations.
5958 if (x_value == y_value) return Smi::FromInt(EQUAL);
5959
5960 // If one of the integers are zero the normal integer order is the
5961 // same as the lexicographic order of the string representations.
5962 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
5963
ager@chromium.org32912102009-01-16 10:38:43 +00005964 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005965 // smallest because the char code of '-' is less than the char code
5966 // of any digit. Otherwise, we make both values positive.
5967 if (x_value < 0 || y_value < 0) {
5968 if (y_value >= 0) return Smi::FromInt(LESS);
5969 if (x_value >= 0) return Smi::FromInt(GREATER);
5970 x_value = -x_value;
5971 y_value = -y_value;
5972 }
5973
5974 // Convert the integers to arrays of their decimal digits.
5975 int x_index = 0;
5976 int y_index = 0;
5977 while (x_value > 0) {
5978 x_elms[x_index++] = x_value % 10;
5979 x_value /= 10;
5980 }
5981 while (y_value > 0) {
5982 y_elms[y_index++] = y_value % 10;
5983 y_value /= 10;
5984 }
5985
5986 // Loop through the arrays of decimal digits finding the first place
5987 // where they differ.
5988 while (--x_index >= 0 && --y_index >= 0) {
5989 int diff = x_elms[x_index] - y_elms[y_index];
5990 if (diff != 0) return Smi::FromInt(diff);
5991 }
5992
5993 // If one array is a suffix of the other array, the longest array is
5994 // the representation of the largest of the Smis in the
5995 // lexicographic ordering.
5996 return Smi::FromInt(x_index - y_index);
5997}
5998
5999
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006000static Object* StringInputBufferCompare(String* x, String* y) {
6001 static StringInputBuffer bufx;
6002 static StringInputBuffer bufy;
6003 bufx.Reset(x);
6004 bufy.Reset(y);
6005 while (bufx.has_more() && bufy.has_more()) {
6006 int d = bufx.GetNext() - bufy.GetNext();
6007 if (d < 0) return Smi::FromInt(LESS);
6008 else if (d > 0) return Smi::FromInt(GREATER);
6009 }
6010
6011 // x is (non-trivial) prefix of y:
6012 if (bufy.has_more()) return Smi::FromInt(LESS);
6013 // y is prefix of x:
6014 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
6015}
6016
6017
6018static Object* FlatStringCompare(String* x, String* y) {
6019 ASSERT(x->IsFlat());
6020 ASSERT(y->IsFlat());
6021 Object* equal_prefix_result = Smi::FromInt(EQUAL);
6022 int prefix_length = x->length();
6023 if (y->length() < prefix_length) {
6024 prefix_length = y->length();
6025 equal_prefix_result = Smi::FromInt(GREATER);
6026 } else if (y->length() > prefix_length) {
6027 equal_prefix_result = Smi::FromInt(LESS);
6028 }
6029 int r;
6030 if (x->IsAsciiRepresentation()) {
6031 Vector<const char> x_chars = x->ToAsciiVector();
6032 if (y->IsAsciiRepresentation()) {
6033 Vector<const char> y_chars = y->ToAsciiVector();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006034 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006035 } else {
6036 Vector<const uc16> y_chars = y->ToUC16Vector();
6037 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
6038 }
6039 } else {
6040 Vector<const uc16> x_chars = x->ToUC16Vector();
6041 if (y->IsAsciiRepresentation()) {
6042 Vector<const char> y_chars = y->ToAsciiVector();
6043 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
6044 } else {
6045 Vector<const uc16> y_chars = y->ToUC16Vector();
6046 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
6047 }
6048 }
6049 Object* result;
6050 if (r == 0) {
6051 result = equal_prefix_result;
6052 } else {
6053 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
6054 }
6055 ASSERT(result == StringInputBufferCompare(x, y));
6056 return result;
6057}
6058
6059
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006060static Object* Runtime_StringCompare(Arguments args) {
6061 NoHandleAllocation ha;
6062 ASSERT(args.length() == 2);
6063
6064 CONVERT_CHECKED(String, x, args[0]);
6065 CONVERT_CHECKED(String, y, args[1]);
6066
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006067 Counters::string_compare_runtime.Increment();
6068
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006069 // A few fast case tests before we flatten.
6070 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006071 if (y->length() == 0) {
6072 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006073 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006074 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006075 return Smi::FromInt(LESS);
6076 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006077
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006078 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006079 if (d < 0) return Smi::FromInt(LESS);
6080 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006081
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006082 Object* obj = Heap::PrepareForCompare(x);
6083 if (obj->IsFailure()) return obj;
6084 obj = Heap::PrepareForCompare(y);
6085 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006086
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006087 return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
6088 : StringInputBufferCompare(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006089}
6090
6091
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006092static Object* Runtime_Math_acos(Arguments args) {
6093 NoHandleAllocation ha;
6094 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006095 Counters::math_acos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006096
6097 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006098 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006099}
6100
6101
6102static Object* Runtime_Math_asin(Arguments args) {
6103 NoHandleAllocation ha;
6104 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006105 Counters::math_asin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006106
6107 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006108 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006109}
6110
6111
6112static Object* Runtime_Math_atan(Arguments args) {
6113 NoHandleAllocation ha;
6114 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006115 Counters::math_atan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006116
6117 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006118 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006119}
6120
6121
6122static Object* Runtime_Math_atan2(Arguments args) {
6123 NoHandleAllocation ha;
6124 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006125 Counters::math_atan2.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006126
6127 CONVERT_DOUBLE_CHECKED(x, args[0]);
6128 CONVERT_DOUBLE_CHECKED(y, args[1]);
6129 double result;
6130 if (isinf(x) && isinf(y)) {
6131 // Make sure that the result in case of two infinite arguments
6132 // is a multiple of Pi / 4. The sign of the result is determined
6133 // by the first argument (x) and the sign of the second argument
6134 // determines the multiplier: one or three.
6135 static double kPiDividedBy4 = 0.78539816339744830962;
6136 int multiplier = (x < 0) ? -1 : 1;
6137 if (y < 0) multiplier *= 3;
6138 result = multiplier * kPiDividedBy4;
6139 } else {
6140 result = atan2(x, y);
6141 }
6142 return Heap::AllocateHeapNumber(result);
6143}
6144
6145
6146static Object* Runtime_Math_ceil(Arguments args) {
6147 NoHandleAllocation ha;
6148 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006149 Counters::math_ceil.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006150
6151 CONVERT_DOUBLE_CHECKED(x, args[0]);
6152 return Heap::NumberFromDouble(ceiling(x));
6153}
6154
6155
6156static Object* Runtime_Math_cos(Arguments args) {
6157 NoHandleAllocation ha;
6158 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006159 Counters::math_cos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006160
6161 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006162 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006163}
6164
6165
6166static Object* Runtime_Math_exp(Arguments args) {
6167 NoHandleAllocation ha;
6168 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006169 Counters::math_exp.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006170
6171 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006172 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006173}
6174
6175
6176static Object* Runtime_Math_floor(Arguments args) {
6177 NoHandleAllocation ha;
6178 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006179 Counters::math_floor.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006180
6181 CONVERT_DOUBLE_CHECKED(x, args[0]);
6182 return Heap::NumberFromDouble(floor(x));
6183}
6184
6185
6186static Object* Runtime_Math_log(Arguments args) {
6187 NoHandleAllocation ha;
6188 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006189 Counters::math_log.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006190
6191 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006192 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006193}
6194
6195
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006196// Helper function to compute x^y, where y is known to be an
6197// integer. Uses binary decomposition to limit the number of
6198// multiplications; see the discussion in "Hacker's Delight" by Henry
6199// S. Warren, Jr., figure 11-6, page 213.
6200static double powi(double x, int y) {
6201 ASSERT(y != kMinInt);
6202 unsigned n = (y < 0) ? -y : y;
6203 double m = x;
6204 double p = 1;
6205 while (true) {
6206 if ((n & 1) != 0) p *= m;
6207 n >>= 1;
6208 if (n == 0) {
6209 if (y < 0) {
6210 // Unfortunately, we have to be careful when p has reached
6211 // infinity in the computation, because sometimes the higher
6212 // internal precision in the pow() implementation would have
6213 // given us a finite p. This happens very rarely.
6214 double result = 1.0 / p;
6215 return (result == 0 && isinf(p))
6216 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
6217 : result;
6218 } else {
6219 return p;
6220 }
6221 }
6222 m *= m;
6223 }
6224}
6225
6226
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006227static Object* Runtime_Math_pow(Arguments args) {
6228 NoHandleAllocation ha;
6229 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006230 Counters::math_pow.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006231
6232 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006233
6234 // If the second argument is a smi, it is much faster to call the
6235 // custom powi() function than the generic pow().
6236 if (args[1]->IsSmi()) {
6237 int y = Smi::cast(args[1])->value();
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00006238 return Heap::NumberFromDouble(powi(x, y));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006239 }
6240
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006241 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00006242
6243 if (!isinf(x)) {
6244 if (y == 0.5) {
6245 // It's not uncommon to use Math.pow(x, 0.5) to compute the
6246 // square root of a number. To speed up such computations, we
6247 // explictly check for this case and use the sqrt() function
6248 // which is faster than pow().
6249 return Heap::AllocateHeapNumber(sqrt(x));
6250 } else if (y == -0.5) {
6251 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
6252 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
6253 }
6254 }
6255
6256 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006257 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006258 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6259 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006260 } else {
6261 return Heap::AllocateHeapNumber(pow(x, y));
6262 }
6263}
6264
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006265// Fast version of Math.pow if we know that y is not an integer and
6266// y is not -0.5 or 0.5. Used as slowcase from codegen.
6267static Object* Runtime_Math_pow_cfunction(Arguments args) {
6268 NoHandleAllocation ha;
6269 ASSERT(args.length() == 2);
6270 CONVERT_DOUBLE_CHECKED(x, args[0]);
6271 CONVERT_DOUBLE_CHECKED(y, args[1]);
6272 if (y == 0) {
6273 return Smi::FromInt(1);
6274 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6275 return Heap::nan_value();
6276 } else {
6277 return Heap::AllocateHeapNumber(pow(x, y));
6278 }
6279}
6280
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006281
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006282static Object* Runtime_RoundNumber(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006283 NoHandleAllocation ha;
6284 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006285 Counters::math_round.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006286
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006287 if (!args[0]->IsHeapNumber()) {
6288 // Must be smi. Return the argument unchanged for all the other types
6289 // to make fuzz-natives test happy.
6290 return args[0];
6291 }
6292
6293 HeapNumber* number = reinterpret_cast<HeapNumber*>(args[0]);
6294
6295 double value = number->value();
6296 int exponent = number->get_exponent();
6297 int sign = number->get_sign();
6298
6299 // We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
6300 // should be rounded to 2^30, which is not smi.
6301 if (!sign && exponent <= kSmiValueSize - 3) {
6302 return Smi::FromInt(static_cast<int>(value + 0.5));
6303 }
6304
6305 // If the magnitude is big enough, there's no place for fraction part. If we
6306 // try to add 0.5 to this number, 1.0 will be added instead.
6307 if (exponent >= 52) {
6308 return number;
6309 }
6310
6311 if (sign && value >= -0.5) return Heap::minus_zero_value();
6312
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006313 // Do not call NumberFromDouble() to avoid extra checks.
6314 return Heap::AllocateHeapNumber(floor(value + 0.5));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006315}
6316
6317
6318static Object* Runtime_Math_sin(Arguments args) {
6319 NoHandleAllocation ha;
6320 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006321 Counters::math_sin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006322
6323 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006324 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006325}
6326
6327
6328static Object* Runtime_Math_sqrt(Arguments args) {
6329 NoHandleAllocation ha;
6330 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006331 Counters::math_sqrt.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006332
6333 CONVERT_DOUBLE_CHECKED(x, args[0]);
6334 return Heap::AllocateHeapNumber(sqrt(x));
6335}
6336
6337
6338static Object* Runtime_Math_tan(Arguments args) {
6339 NoHandleAllocation ha;
6340 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006341 Counters::math_tan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006342
6343 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006344 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006345}
6346
6347
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006348static int MakeDay(int year, int month, int day) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006349 static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
6350 181, 212, 243, 273, 304, 334};
6351 static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
6352 182, 213, 244, 274, 305, 335};
6353
6354 year += month / 12;
6355 month %= 12;
6356 if (month < 0) {
6357 year--;
6358 month += 12;
6359 }
6360
6361 ASSERT(month >= 0);
6362 ASSERT(month < 12);
6363
6364 // year_delta is an arbitrary number such that:
6365 // a) year_delta = -1 (mod 400)
6366 // b) year + year_delta > 0 for years in the range defined by
6367 // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
6368 // Jan 1 1970. This is required so that we don't run into integer
6369 // division of negative numbers.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006370 // c) there shouldn't be an overflow for 32-bit integers in the following
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006371 // operations.
6372 static const int year_delta = 399999;
6373 static const int base_day = 365 * (1970 + year_delta) +
6374 (1970 + year_delta) / 4 -
6375 (1970 + year_delta) / 100 +
6376 (1970 + year_delta) / 400;
6377
6378 int year1 = year + year_delta;
6379 int day_from_year = 365 * year1 +
6380 year1 / 4 -
6381 year1 / 100 +
6382 year1 / 400 -
6383 base_day;
6384
6385 if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006386 return day_from_year + day_from_month[month] + day - 1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006387 }
6388
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006389 return day_from_year + day_from_month_leap[month] + day - 1;
6390}
6391
6392
6393static Object* Runtime_DateMakeDay(Arguments args) {
6394 NoHandleAllocation ha;
6395 ASSERT(args.length() == 3);
6396
6397 CONVERT_SMI_CHECKED(year, args[0]);
6398 CONVERT_SMI_CHECKED(month, args[1]);
6399 CONVERT_SMI_CHECKED(date, args[2]);
6400
6401 return Smi::FromInt(MakeDay(year, month, date));
6402}
6403
6404
6405static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
6406static const int kDaysIn4Years = 4 * 365 + 1;
6407static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
6408static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
6409static const int kDays1970to2000 = 30 * 365 + 7;
6410static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
6411 kDays1970to2000;
6412static const int kYearsOffset = 400000;
6413
6414static const char kDayInYear[] = {
6415 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6416 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6417 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6418 22, 23, 24, 25, 26, 27, 28,
6419 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6420 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6421 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6422 22, 23, 24, 25, 26, 27, 28, 29, 30,
6423 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6424 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6425 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6426 22, 23, 24, 25, 26, 27, 28, 29, 30,
6427 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6428 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6429 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6430 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6431 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6432 22, 23, 24, 25, 26, 27, 28, 29, 30,
6433 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6434 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6435 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6436 22, 23, 24, 25, 26, 27, 28, 29, 30,
6437 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6438 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6439
6440 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6441 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6442 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6443 22, 23, 24, 25, 26, 27, 28,
6444 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6445 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6446 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6447 22, 23, 24, 25, 26, 27, 28, 29, 30,
6448 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6449 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6450 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6451 22, 23, 24, 25, 26, 27, 28, 29, 30,
6452 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6453 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6454 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6455 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6456 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6457 22, 23, 24, 25, 26, 27, 28, 29, 30,
6458 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6459 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6460 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6461 22, 23, 24, 25, 26, 27, 28, 29, 30,
6462 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6463 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6464
6465 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6466 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6467 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6468 22, 23, 24, 25, 26, 27, 28, 29,
6469 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6470 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6471 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6472 22, 23, 24, 25, 26, 27, 28, 29, 30,
6473 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6474 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6475 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6476 22, 23, 24, 25, 26, 27, 28, 29, 30,
6477 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6478 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6479 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6480 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6481 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6482 22, 23, 24, 25, 26, 27, 28, 29, 30,
6483 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6484 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6485 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6486 22, 23, 24, 25, 26, 27, 28, 29, 30,
6487 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6488 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6489
6490 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6491 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6492 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6493 22, 23, 24, 25, 26, 27, 28,
6494 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6495 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6496 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6497 22, 23, 24, 25, 26, 27, 28, 29, 30,
6498 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6499 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6500 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6501 22, 23, 24, 25, 26, 27, 28, 29, 30,
6502 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6503 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6504 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6505 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6506 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6507 22, 23, 24, 25, 26, 27, 28, 29, 30,
6508 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6509 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6510 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6511 22, 23, 24, 25, 26, 27, 28, 29, 30,
6512 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6513 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
6514
6515static const char kMonthInYear[] = {
6516 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,
6517 0, 0, 0, 0, 0, 0,
6518 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,
6519 1, 1, 1,
6520 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,
6521 2, 2, 2, 2, 2, 2,
6522 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,
6523 3, 3, 3, 3, 3,
6524 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,
6525 4, 4, 4, 4, 4, 4,
6526 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,
6527 5, 5, 5, 5, 5,
6528 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,
6529 6, 6, 6, 6, 6, 6,
6530 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,
6531 7, 7, 7, 7, 7, 7,
6532 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,
6533 8, 8, 8, 8, 8,
6534 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,
6535 9, 9, 9, 9, 9, 9,
6536 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6537 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6538 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6539 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6540
6541 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,
6542 0, 0, 0, 0, 0, 0,
6543 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,
6544 1, 1, 1,
6545 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,
6546 2, 2, 2, 2, 2, 2,
6547 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,
6548 3, 3, 3, 3, 3,
6549 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,
6550 4, 4, 4, 4, 4, 4,
6551 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,
6552 5, 5, 5, 5, 5,
6553 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,
6554 6, 6, 6, 6, 6, 6,
6555 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,
6556 7, 7, 7, 7, 7, 7,
6557 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,
6558 8, 8, 8, 8, 8,
6559 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,
6560 9, 9, 9, 9, 9, 9,
6561 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6562 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6563 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6564 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6565
6566 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,
6567 0, 0, 0, 0, 0, 0,
6568 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,
6569 1, 1, 1, 1,
6570 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,
6571 2, 2, 2, 2, 2, 2,
6572 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,
6573 3, 3, 3, 3, 3,
6574 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,
6575 4, 4, 4, 4, 4, 4,
6576 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,
6577 5, 5, 5, 5, 5,
6578 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,
6579 6, 6, 6, 6, 6, 6,
6580 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,
6581 7, 7, 7, 7, 7, 7,
6582 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,
6583 8, 8, 8, 8, 8,
6584 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,
6585 9, 9, 9, 9, 9, 9,
6586 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6587 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6588 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6589 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6590
6591 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,
6592 0, 0, 0, 0, 0, 0,
6593 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,
6594 1, 1, 1,
6595 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,
6596 2, 2, 2, 2, 2, 2,
6597 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,
6598 3, 3, 3, 3, 3,
6599 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,
6600 4, 4, 4, 4, 4, 4,
6601 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,
6602 5, 5, 5, 5, 5,
6603 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,
6604 6, 6, 6, 6, 6, 6,
6605 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,
6606 7, 7, 7, 7, 7, 7,
6607 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,
6608 8, 8, 8, 8, 8,
6609 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,
6610 9, 9, 9, 9, 9, 9,
6611 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6612 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6613 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6614 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
6615
6616
6617// This function works for dates from 1970 to 2099.
6618static inline void DateYMDFromTimeAfter1970(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006619 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006620#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006621 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006622#endif
6623
6624 year = 1970 + (4 * date + 2) / kDaysIn4Years;
6625 date %= kDaysIn4Years;
6626
6627 month = kMonthInYear[date];
6628 day = kDayInYear[date];
6629
6630 ASSERT(MakeDay(year, month, day) == save_date);
6631}
6632
6633
6634static inline void DateYMDFromTimeSlow(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006635 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006636#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006637 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006638#endif
6639
6640 date += kDaysOffset;
6641 year = 400 * (date / kDaysIn400Years) - kYearsOffset;
6642 date %= kDaysIn400Years;
6643
6644 ASSERT(MakeDay(year, 0, 1) + date == save_date);
6645
6646 date--;
6647 int yd1 = date / kDaysIn100Years;
6648 date %= kDaysIn100Years;
6649 year += 100 * yd1;
6650
6651 date++;
6652 int yd2 = date / kDaysIn4Years;
6653 date %= kDaysIn4Years;
6654 year += 4 * yd2;
6655
6656 date--;
6657 int yd3 = date / 365;
6658 date %= 365;
6659 year += yd3;
6660
6661 bool is_leap = (!yd1 || yd2) && !yd3;
6662
6663 ASSERT(date >= -1);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006664 ASSERT(is_leap || (date >= 0));
6665 ASSERT((date < 365) || (is_leap && (date < 366)));
6666 ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0))));
6667 ASSERT(is_leap || ((MakeDay(year, 0, 1) + date) == save_date));
6668 ASSERT(!is_leap || ((MakeDay(year, 0, 1) + date + 1) == save_date));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006669
6670 if (is_leap) {
6671 day = kDayInYear[2*365 + 1 + date];
6672 month = kMonthInYear[2*365 + 1 + date];
6673 } else {
6674 day = kDayInYear[date];
6675 month = kMonthInYear[date];
6676 }
6677
6678 ASSERT(MakeDay(year, month, day) == save_date);
6679}
6680
6681
6682static inline void DateYMDFromTime(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006683 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006684 if (date >= 0 && date < 32 * kDaysIn4Years) {
6685 DateYMDFromTimeAfter1970(date, year, month, day);
6686 } else {
6687 DateYMDFromTimeSlow(date, year, month, day);
6688 }
6689}
6690
6691
6692static Object* Runtime_DateYMDFromTime(Arguments args) {
6693 NoHandleAllocation ha;
6694 ASSERT(args.length() == 2);
6695
6696 CONVERT_DOUBLE_CHECKED(t, args[0]);
6697 CONVERT_CHECKED(JSArray, res_array, args[1]);
6698
6699 int year, month, day;
6700 DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
6701
6702 res_array->SetElement(0, Smi::FromInt(year));
6703 res_array->SetElement(1, Smi::FromInt(month));
6704 res_array->SetElement(2, Smi::FromInt(day));
6705
6706 return Heap::undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006707}
6708
6709
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006710static Object* Runtime_NewArgumentsFast(Arguments args) {
6711 NoHandleAllocation ha;
6712 ASSERT(args.length() == 3);
6713
6714 JSFunction* callee = JSFunction::cast(args[0]);
6715 Object** parameters = reinterpret_cast<Object**>(args[1]);
6716 const int length = Smi::cast(args[2])->value();
6717
6718 Object* result = Heap::AllocateArgumentsObject(callee, length);
6719 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006720 // Allocate the elements if needed.
6721 if (length > 0) {
6722 // Allocate the fixed array.
6723 Object* obj = Heap::AllocateRawFixedArray(length);
6724 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006725
6726 AssertNoAllocation no_gc;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006727 FixedArray* array = reinterpret_cast<FixedArray*>(obj);
6728 array->set_map(Heap::fixed_array_map());
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006729 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006730
6731 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006732 for (int i = 0; i < length; i++) {
6733 array->set(i, *--parameters, mode);
6734 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006735 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006736 }
6737 return result;
6738}
6739
6740
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006741static Object* Runtime_NewClosure(Arguments args) {
6742 HandleScope scope;
6743 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00006744 CONVERT_ARG_CHECKED(Context, context, 0);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006745 CONVERT_ARG_CHECKED(SharedFunctionInfo, shared, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006746
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00006747 PretenureFlag pretenure = (context->global_context() == *context)
6748 ? TENURED // Allocate global closures in old space.
6749 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006750 Handle<JSFunction> result =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006751 Factory::NewFunctionFromSharedFunctionInfo(shared, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006752 return *result;
6753}
6754
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006755static Object* Runtime_NewObjectFromBound(Arguments args) {
6756 HandleScope scope;
6757 ASSERT(args.length() == 2);
6758 CONVERT_ARG_CHECKED(JSFunction, function, 0);
6759 CONVERT_ARG_CHECKED(JSArray, params, 1);
6760
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006761 RUNTIME_ASSERT(params->HasFastElements());
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006762 FixedArray* fixed = FixedArray::cast(params->elements());
6763
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006764 int fixed_length = Smi::cast(params->length())->value();
6765 SmartPointer<Object**> param_data(NewArray<Object**>(fixed_length));
6766 for (int i = 0; i < fixed_length; i++) {
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006767 Handle<Object> val = Handle<Object>(fixed->get(i));
6768 param_data[i] = val.location();
6769 }
6770
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006771 bool exception = false;
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006772 Handle<Object> result = Execution::New(
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006773 function, fixed_length, *param_data, &exception);
6774 if (exception) {
6775 return Failure::Exception();
6776 }
6777 ASSERT(!result.is_null());
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006778 return *result;
6779}
6780
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006781
ager@chromium.org5c838252010-02-19 08:53:10 +00006782static Code* ComputeConstructStub(Handle<JSFunction> function) {
6783 Handle<Object> prototype = Factory::null_value();
6784 if (function->has_instance_prototype()) {
6785 prototype = Handle<Object>(function->instance_prototype());
6786 }
6787 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006788 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00006789 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006790 if (code->IsFailure()) {
6791 return Builtins::builtin(Builtins::JSConstructStubGeneric);
6792 }
6793 return Code::cast(code);
6794 }
6795
ager@chromium.org5c838252010-02-19 08:53:10 +00006796 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006797}
6798
6799
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006800static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006801 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006802 ASSERT(args.length() == 1);
6803
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006804 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006805
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006806 // If the constructor isn't a proper function we throw a type error.
6807 if (!constructor->IsJSFunction()) {
6808 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6809 Handle<Object> type_error =
6810 Factory::NewTypeError("not_constructor", arguments);
6811 return Top::Throw(*type_error);
6812 }
6813
6814 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006815
6816 // If function should not have prototype, construction is not allowed. In this
6817 // case generated code bailouts here, since function has no initial_map.
6818 if (!function->should_have_prototype()) {
6819 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6820 Handle<Object> type_error =
6821 Factory::NewTypeError("not_constructor", arguments);
6822 return Top::Throw(*type_error);
6823 }
6824
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006825#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006826 // Handle stepping into constructors if step into is active.
6827 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006828 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006829 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006830#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006831
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006832 if (function->has_initial_map()) {
6833 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006834 // The 'Function' function ignores the receiver object when
6835 // called using 'new' and creates a new JSFunction object that
6836 // is returned. The receiver object is only used for error
6837 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006838 // JSFunction. Factory::NewJSObject() should not be used to
6839 // allocate JSFunctions since it does not properly initialize
6840 // the shared part of the function. Since the receiver is
6841 // ignored anyway, we use the global object as the receiver
6842 // instead of a new JSFunction object. This way, errors are
6843 // reported the same way whether or not 'Function' is called
6844 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006845 return Top::context()->global();
6846 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006847 }
6848
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006849 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006850 Handle<SharedFunctionInfo> shared(function->shared());
6851 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006852
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006853 bool first_allocation = !function->has_initial_map();
6854 Handle<JSObject> result = Factory::NewJSObject(function);
6855 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006856 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00006857 ComputeConstructStub(Handle<JSFunction>(function)));
6858 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006859 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006860
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006861 Counters::constructed_objects.Increment();
6862 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006863
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006864 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006865}
6866
6867
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006868static Object* Runtime_LazyCompile(Arguments args) {
6869 HandleScope scope;
6870 ASSERT(args.length() == 1);
6871
6872 Handle<JSFunction> function = args.at<JSFunction>(0);
6873#ifdef DEBUG
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00006874 if (FLAG_trace_lazy && !function->shared()->is_compiled()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006875 PrintF("[lazy: ");
6876 function->shared()->name()->Print();
6877 PrintF("]\n");
6878 }
6879#endif
6880
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006881 // Compile the target function. Here we compile using CompileLazyInLoop in
6882 // order to get the optimized version. This helps code like delta-blue
6883 // that calls performance-critical routines through constructors. A
6884 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
6885 // direct call. Since the in-loop tracking takes place through CallICs
6886 // this means that things called through constructors are never known to
6887 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006888 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006889 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006890 return Failure::Exception();
6891 }
6892
6893 return function->code();
6894}
6895
6896
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006897static Object* Runtime_GetFunctionDelegate(Arguments args) {
6898 HandleScope scope;
6899 ASSERT(args.length() == 1);
6900 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6901 return *Execution::GetFunctionDelegate(args.at<Object>(0));
6902}
6903
6904
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00006905static Object* Runtime_GetConstructorDelegate(Arguments args) {
6906 HandleScope scope;
6907 ASSERT(args.length() == 1);
6908 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6909 return *Execution::GetConstructorDelegate(args.at<Object>(0));
6910}
6911
6912
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006913static Object* Runtime_NewContext(Arguments args) {
6914 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00006915 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006916
kasper.lund7276f142008-07-30 08:49:36 +00006917 CONVERT_CHECKED(JSFunction, function, args[0]);
ager@chromium.orgb5737492010-07-15 09:29:43 +00006918 int length = function->shared()->scope_info()->NumberOfContextSlots();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006919 Object* result = Heap::AllocateFunctionContext(length, function);
6920 if (result->IsFailure()) return result;
6921
6922 Top::set_context(Context::cast(result));
6923
kasper.lund7276f142008-07-30 08:49:36 +00006924 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006925}
6926
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006927static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006928 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006929 Object* js_object = object;
6930 if (!js_object->IsJSObject()) {
6931 js_object = js_object->ToObject();
6932 if (js_object->IsFailure()) {
6933 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006934 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006935 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006936 Handle<Object> result =
6937 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
6938 return Top::Throw(*result);
6939 }
6940 }
6941
6942 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006943 Heap::AllocateWithContext(Top::context(),
6944 JSObject::cast(js_object),
6945 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006946 if (result->IsFailure()) return result;
6947
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006948 Context* context = Context::cast(result);
6949 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006950
kasper.lund7276f142008-07-30 08:49:36 +00006951 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006952}
6953
6954
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006955static Object* Runtime_PushContext(Arguments args) {
6956 NoHandleAllocation ha;
6957 ASSERT(args.length() == 1);
6958 return PushContextHelper(args[0], false);
6959}
6960
6961
6962static Object* Runtime_PushCatchContext(Arguments args) {
6963 NoHandleAllocation ha;
6964 ASSERT(args.length() == 1);
6965 return PushContextHelper(args[0], true);
6966}
6967
6968
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006969static Object* Runtime_LookupContext(Arguments args) {
6970 HandleScope scope;
6971 ASSERT(args.length() == 2);
6972
6973 CONVERT_ARG_CHECKED(Context, context, 0);
6974 CONVERT_ARG_CHECKED(String, name, 1);
6975
6976 int index;
6977 PropertyAttributes attributes;
6978 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006979 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006980 context->Lookup(name, flags, &index, &attributes);
6981
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006982 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006983 ASSERT(holder->IsJSObject());
6984 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006985 }
6986
6987 // No intermediate context found. Use global object by default.
6988 return Top::context()->global();
6989}
6990
6991
ager@chromium.orga1645e22009-09-09 19:27:10 +00006992// A mechanism to return a pair of Object pointers in registers (if possible).
6993// How this is achieved is calling convention-dependent.
6994// All currently supported x86 compiles uses calling conventions that are cdecl
6995// variants where a 64-bit value is returned in two 32-bit registers
6996// (edx:eax on ia32, r1:r0 on ARM).
6997// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
6998// In Win64 calling convention, a struct of two pointers is returned in memory,
6999// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007000#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007001struct ObjectPair {
7002 Object* x;
7003 Object* y;
7004};
ager@chromium.orga1645e22009-09-09 19:27:10 +00007005
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007006static inline ObjectPair MakePair(Object* x, Object* y) {
7007 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00007008 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
7009 // In Win64 they are assigned to a hidden first argument.
7010 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007011}
7012#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007013typedef uint64_t ObjectPair;
7014static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007015 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007016 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007017}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007018#endif
7019
7020
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007021static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007022 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
7023 USE(attributes);
7024 return x->IsTheHole() ? Heap::undefined_value() : x;
7025}
7026
7027
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007028static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
7029 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007030 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007031 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007032 JSFunction* context_extension_function =
7033 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007034 // If the holder isn't a context extension object, we just return it
7035 // as the receiver. This allows arguments objects to be used as
7036 // receivers, but only if they are put in the context scope chain
7037 // explicitly via a with-statement.
7038 Object* constructor = holder->map()->constructor();
7039 if (constructor != context_extension_function) return holder;
7040 // Fall back to using the global object as the receiver if the
7041 // property turns out to be a local variable allocated in a context
7042 // extension object - introduced via eval.
7043 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007044}
7045
7046
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007047static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007048 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00007049 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007050
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007051 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00007052 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007053 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007054 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007055 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007056
7057 int index;
7058 PropertyAttributes attributes;
7059 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007060 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007061 context->Lookup(name, flags, &index, &attributes);
7062
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007063 // If the index is non-negative, the slot has been found in a local
7064 // variable or a parameter. Read it from the context object or the
7065 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007066 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007067 // If the "property" we were looking for is a local variable or an
7068 // argument in a context, the receiver is the global object; see
7069 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
7070 JSObject* receiver = Top::context()->global()->global_receiver();
7071 Object* value = (holder->IsContext())
7072 ? Context::cast(*holder)->get(index)
7073 : JSObject::cast(*holder)->GetElement(index);
7074 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007075 }
7076
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007077 // If the holder is found, we read the property from it.
7078 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007079 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007080 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007081 JSObject* receiver;
7082 if (object->IsGlobalObject()) {
7083 receiver = GlobalObject::cast(object)->global_receiver();
7084 } else if (context->is_exception_holder(*holder)) {
7085 receiver = Top::context()->global()->global_receiver();
7086 } else {
7087 receiver = ComputeReceiverForNonGlobal(object);
7088 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007089 // No need to unhole the value here. This is taken care of by the
7090 // GetProperty function.
7091 Object* value = object->GetProperty(*name);
7092 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007093 }
7094
7095 if (throw_error) {
7096 // The property doesn't exist - throw exception.
7097 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007098 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007099 return MakePair(Top::Throw(*reference_error), NULL);
7100 } else {
7101 // The property doesn't exist - return undefined
7102 return MakePair(Heap::undefined_value(), Heap::undefined_value());
7103 }
7104}
7105
7106
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007107static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007108 return LoadContextSlotHelper(args, true);
7109}
7110
7111
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007112static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007113 return LoadContextSlotHelper(args, false);
7114}
7115
7116
7117static Object* Runtime_StoreContextSlot(Arguments args) {
7118 HandleScope scope;
7119 ASSERT(args.length() == 3);
7120
7121 Handle<Object> value(args[0]);
7122 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007123 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007124
7125 int index;
7126 PropertyAttributes attributes;
7127 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007128 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007129 context->Lookup(name, flags, &index, &attributes);
7130
7131 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007132 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007133 // Ignore if read_only variable.
7134 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007135 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007136 }
7137 } else {
7138 ASSERT((attributes & READ_ONLY) == 0);
7139 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007140 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007141 USE(result);
7142 ASSERT(!result->IsFailure());
7143 }
7144 return *value;
7145 }
7146
7147 // Slow case: The property is not in a FixedArray context.
7148 // It is either in an JSObject extension context or it was not found.
7149 Handle<JSObject> context_ext;
7150
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007151 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007152 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007153 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007154 } else {
7155 // The property was not found. It needs to be stored in the global context.
7156 ASSERT(attributes == ABSENT);
7157 attributes = NONE;
7158 context_ext = Handle<JSObject>(Top::context()->global());
7159 }
7160
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007161 // Set the property, but ignore if read_only variable on the context
7162 // extension object itself.
7163 if ((attributes & READ_ONLY) == 0 ||
7164 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007165 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
7166 if (set.is_null()) {
7167 // Failure::Exception is converted to a null handle in the
7168 // handle-based methods such as SetProperty. We therefore need
7169 // to convert null handles back to exceptions.
7170 ASSERT(Top::has_pending_exception());
7171 return Failure::Exception();
7172 }
7173 }
7174 return *value;
7175}
7176
7177
7178static Object* Runtime_Throw(Arguments args) {
7179 HandleScope scope;
7180 ASSERT(args.length() == 1);
7181
7182 return Top::Throw(args[0]);
7183}
7184
7185
7186static Object* Runtime_ReThrow(Arguments args) {
7187 HandleScope scope;
7188 ASSERT(args.length() == 1);
7189
7190 return Top::ReThrow(args[0]);
7191}
7192
7193
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007194static Object* Runtime_PromoteScheduledException(Arguments args) {
7195 ASSERT_EQ(0, args.length());
7196 return Top::PromoteScheduledException();
7197}
7198
7199
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007200static Object* Runtime_ThrowReferenceError(Arguments args) {
7201 HandleScope scope;
7202 ASSERT(args.length() == 1);
7203
7204 Handle<Object> name(args[0]);
7205 Handle<Object> reference_error =
7206 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
7207 return Top::Throw(*reference_error);
7208}
7209
7210
7211static Object* Runtime_StackOverflow(Arguments args) {
7212 NoHandleAllocation na;
7213 return Top::StackOverflow();
7214}
7215
7216
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007217static Object* Runtime_StackGuard(Arguments args) {
7218 ASSERT(args.length() == 1);
7219
7220 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007221 if (StackGuard::IsStackOverflow()) {
7222 return Runtime_StackOverflow(args);
7223 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007224
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007225 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007226}
7227
7228
7229// NOTE: These PrintXXX functions are defined for all builds (not just
7230// DEBUG builds) because we may want to be able to trace function
7231// calls in all modes.
7232static void PrintString(String* str) {
7233 // not uncommon to have empty strings
7234 if (str->length() > 0) {
7235 SmartPointer<char> s =
7236 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
7237 PrintF("%s", *s);
7238 }
7239}
7240
7241
7242static void PrintObject(Object* obj) {
7243 if (obj->IsSmi()) {
7244 PrintF("%d", Smi::cast(obj)->value());
7245 } else if (obj->IsString() || obj->IsSymbol()) {
7246 PrintString(String::cast(obj));
7247 } else if (obj->IsNumber()) {
7248 PrintF("%g", obj->Number());
7249 } else if (obj->IsFailure()) {
7250 PrintF("<failure>");
7251 } else if (obj->IsUndefined()) {
7252 PrintF("<undefined>");
7253 } else if (obj->IsNull()) {
7254 PrintF("<null>");
7255 } else if (obj->IsTrue()) {
7256 PrintF("<true>");
7257 } else if (obj->IsFalse()) {
7258 PrintF("<false>");
7259 } else {
7260 PrintF("%p", obj);
7261 }
7262}
7263
7264
7265static int StackSize() {
7266 int n = 0;
7267 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
7268 return n;
7269}
7270
7271
7272static void PrintTransition(Object* result) {
7273 // indentation
7274 { const int nmax = 80;
7275 int n = StackSize();
7276 if (n <= nmax)
7277 PrintF("%4d:%*s", n, n, "");
7278 else
7279 PrintF("%4d:%*s", n, nmax, "...");
7280 }
7281
7282 if (result == NULL) {
7283 // constructor calls
7284 JavaScriptFrameIterator it;
7285 JavaScriptFrame* frame = it.frame();
7286 if (frame->IsConstructor()) PrintF("new ");
7287 // function name
7288 Object* fun = frame->function();
7289 if (fun->IsJSFunction()) {
7290 PrintObject(JSFunction::cast(fun)->shared()->name());
7291 } else {
7292 PrintObject(fun);
7293 }
7294 // function arguments
7295 // (we are intentionally only printing the actually
7296 // supplied parameters, not all parameters required)
7297 PrintF("(this=");
7298 PrintObject(frame->receiver());
7299 const int length = frame->GetProvidedParametersCount();
7300 for (int i = 0; i < length; i++) {
7301 PrintF(", ");
7302 PrintObject(frame->GetParameter(i));
7303 }
7304 PrintF(") {\n");
7305
7306 } else {
7307 // function result
7308 PrintF("} -> ");
7309 PrintObject(result);
7310 PrintF("\n");
7311 }
7312}
7313
7314
7315static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007316 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007317 NoHandleAllocation ha;
7318 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007319 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007320}
7321
7322
7323static Object* Runtime_TraceExit(Arguments args) {
7324 NoHandleAllocation ha;
7325 PrintTransition(args[0]);
7326 return args[0]; // return TOS
7327}
7328
7329
7330static Object* Runtime_DebugPrint(Arguments args) {
7331 NoHandleAllocation ha;
7332 ASSERT(args.length() == 1);
7333
7334#ifdef DEBUG
7335 if (args[0]->IsString()) {
7336 // If we have a string, assume it's a code "marker"
7337 // and print some interesting cpu debugging info.
7338 JavaScriptFrameIterator it;
7339 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007340 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
7341 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007342 } else {
7343 PrintF("DebugPrint: ");
7344 }
7345 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007346 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007347 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007348 HeapObject::cast(args[0])->map()->Print();
7349 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007350#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007351 // ShortPrint is available in release mode. Print is not.
7352 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007353#endif
7354 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00007355 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007356
7357 return args[0]; // return TOS
7358}
7359
7360
7361static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007362 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007363 NoHandleAllocation ha;
7364 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007365 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007366}
7367
7368
mads.s.ager31e71382008-08-13 09:32:07 +00007369static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007370 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007371 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007372
7373 // According to ECMA-262, section 15.9.1, page 117, the precision of
7374 // the number in a Date object representing a particular instant in
7375 // time is milliseconds. Therefore, we floor the result of getting
7376 // the OS time.
7377 double millis = floor(OS::TimeCurrentMillis());
7378 return Heap::NumberFromDouble(millis);
7379}
7380
7381
7382static Object* Runtime_DateParseString(Arguments args) {
7383 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007384 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007385
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007386 CONVERT_ARG_CHECKED(String, str, 0);
7387 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007388
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007389 CONVERT_ARG_CHECKED(JSArray, output, 1);
7390 RUNTIME_ASSERT(output->HasFastElements());
7391
7392 AssertNoAllocation no_allocation;
7393
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007394 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007395 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
7396 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007397 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007398 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007399 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007400 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007401 result = DateParser::Parse(str->ToUC16Vector(), output_array);
7402 }
7403
7404 if (result) {
7405 return *output;
7406 } else {
7407 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007408 }
7409}
7410
7411
7412static Object* Runtime_DateLocalTimezone(Arguments args) {
7413 NoHandleAllocation ha;
7414 ASSERT(args.length() == 1);
7415
7416 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007417 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007418 return Heap::AllocateStringFromUtf8(CStrVector(zone));
7419}
7420
7421
7422static Object* Runtime_DateLocalTimeOffset(Arguments args) {
7423 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007424 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007425
7426 return Heap::NumberFromDouble(OS::LocalTimeOffset());
7427}
7428
7429
7430static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
7431 NoHandleAllocation ha;
7432 ASSERT(args.length() == 1);
7433
7434 CONVERT_DOUBLE_CHECKED(x, args[0]);
7435 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
7436}
7437
7438
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007439static Object* Runtime_GlobalReceiver(Arguments args) {
7440 ASSERT(args.length() == 1);
7441 Object* global = args[0];
7442 if (!global->IsJSGlobalObject()) return Heap::null_value();
7443 return JSGlobalObject::cast(global)->global_receiver();
7444}
7445
7446
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007447static Object* Runtime_CompileString(Arguments args) {
7448 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007449 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007450 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007451 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007452
ager@chromium.org381abbb2009-02-25 13:23:22 +00007453 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007454 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007455 Compiler::ValidationState validate = (is_json->IsTrue())
7456 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007457 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
7458 context,
7459 true,
7460 validate);
7461 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007462 Handle<JSFunction> fun =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007463 Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007464 return *fun;
7465}
7466
7467
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007468static ObjectPair CompileGlobalEval(Handle<String> source,
7469 Handle<Object> receiver) {
7470 // Deal with a normal eval call with a string argument. Compile it
7471 // and return the compiled function bound in the local context.
7472 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
7473 source,
7474 Handle<Context>(Top::context()),
7475 Top::context()->IsGlobalContext(),
7476 Compiler::DONT_VALIDATE_JSON);
7477 if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
7478 Handle<JSFunction> compiled = Factory::NewFunctionFromSharedFunctionInfo(
7479 shared,
7480 Handle<Context>(Top::context()),
7481 NOT_TENURED);
7482 return MakePair(*compiled, *receiver);
7483}
7484
7485
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007486static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
7487 ASSERT(args.length() == 3);
7488 if (!args[0]->IsJSFunction()) {
7489 return MakePair(Top::ThrowIllegalOperation(), NULL);
7490 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007491
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007492 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007493 Handle<JSFunction> callee = args.at<JSFunction>(0);
7494 Handle<Object> receiver; // Will be overwritten.
7495
7496 // Compute the calling context.
7497 Handle<Context> context = Handle<Context>(Top::context());
7498#ifdef DEBUG
7499 // Make sure Top::context() agrees with the old code that traversed
7500 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007501 StackFrameLocator locator;
7502 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007503 ASSERT(Context::cast(frame->context()) == *context);
7504#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007505
7506 // Find where the 'eval' symbol is bound. It is unaliased only if
7507 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007508 int index = -1;
7509 PropertyAttributes attributes = ABSENT;
7510 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007511 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
7512 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007513 // Stop search when eval is found or when the global context is
7514 // reached.
7515 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007516 if (context->is_function_context()) {
7517 context = Handle<Context>(Context::cast(context->closure()->context()));
7518 } else {
7519 context = Handle<Context>(context->previous());
7520 }
7521 }
7522
iposva@chromium.org245aa852009-02-10 00:49:54 +00007523 // If eval could not be resolved, it has been deleted and we need to
7524 // throw a reference error.
7525 if (attributes == ABSENT) {
7526 Handle<Object> name = Factory::eval_symbol();
7527 Handle<Object> reference_error =
7528 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007529 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007530 }
7531
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007532 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007533 // 'eval' is not bound in the global context. Just call the function
7534 // with the given arguments. This is not necessarily the global eval.
7535 if (receiver->IsContext()) {
7536 context = Handle<Context>::cast(receiver);
7537 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007538 } else if (receiver->IsJSContextExtensionObject()) {
7539 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007540 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007541 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007542 }
7543
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007544 // 'eval' is bound in the global context, but it may have been overwritten.
7545 // Compare it to the builtin 'GlobalEval' function to make sure.
7546 if (*callee != Top::global_context()->global_eval_fun() ||
7547 !args[1]->IsString()) {
7548 return MakePair(*callee, Top::context()->global()->global_receiver());
7549 }
7550
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007551 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
7552}
7553
7554
7555static ObjectPair Runtime_ResolvePossiblyDirectEvalNoLookup(Arguments args) {
7556 ASSERT(args.length() == 3);
7557 if (!args[0]->IsJSFunction()) {
7558 return MakePair(Top::ThrowIllegalOperation(), NULL);
7559 }
7560
7561 HandleScope scope;
7562 Handle<JSFunction> callee = args.at<JSFunction>(0);
7563
7564 // 'eval' is bound in the global context, but it may have been overwritten.
7565 // Compare it to the builtin 'GlobalEval' function to make sure.
7566 if (*callee != Top::global_context()->global_eval_fun() ||
7567 !args[1]->IsString()) {
7568 return MakePair(*callee, Top::context()->global()->global_receiver());
7569 }
7570
7571 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007572}
7573
7574
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007575static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
7576 // This utility adjusts the property attributes for newly created Function
7577 // object ("new Function(...)") by changing the map.
7578 // All it does is changing the prototype property to enumerable
7579 // as specified in ECMA262, 15.3.5.2.
7580 HandleScope scope;
7581 ASSERT(args.length() == 1);
7582 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7583 ASSERT(func->map()->instance_type() ==
7584 Top::function_instance_map()->instance_type());
7585 ASSERT(func->map()->instance_size() ==
7586 Top::function_instance_map()->instance_size());
7587 func->set_map(*Top::function_instance_map());
7588 return *func;
7589}
7590
7591
lrn@chromium.orgc4e51ac2010-08-09 09:47:21 +00007592static Object* Runtime_AllocateInNewSpace(Arguments args) {
7593 // Allocate a block of memory in NewSpace (filled with a filler).
7594 // Use as fallback for allocation in generated code when NewSpace
7595 // is full.
7596 ASSERT(args.length() == 1);
7597 CONVERT_ARG_CHECKED(Smi, size_smi, 0);
7598 int size = size_smi->value();
7599 RUNTIME_ASSERT(IsAligned(size, kPointerSize));
7600 RUNTIME_ASSERT(size > 0);
7601 static const int kMinFreeNewSpaceAfterGC =
7602 Heap::InitialSemiSpaceSize() * 3/4;
7603 RUNTIME_ASSERT(size <= kMinFreeNewSpaceAfterGC);
7604 Object* allocation = Heap::new_space()->AllocateRaw(size);
7605 if (!allocation->IsFailure()) {
7606 Heap::CreateFillerObjectAt(HeapObject::cast(allocation)->address(), size);
7607 }
7608 return allocation;
7609}
7610
7611
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007612// Push an array unto an array of arrays if it is not already in the
7613// array. Returns true if the element was pushed on the stack and
7614// false otherwise.
7615static Object* Runtime_PushIfAbsent(Arguments args) {
7616 ASSERT(args.length() == 2);
7617 CONVERT_CHECKED(JSArray, array, args[0]);
7618 CONVERT_CHECKED(JSArray, element, args[1]);
7619 RUNTIME_ASSERT(array->HasFastElements());
7620 int length = Smi::cast(array->length())->value();
7621 FixedArray* elements = FixedArray::cast(array->elements());
7622 for (int i = 0; i < length; i++) {
7623 if (elements->get(i) == element) return Heap::false_value();
7624 }
7625 Object* obj = array->SetFastElement(length, element);
7626 if (obj->IsFailure()) return obj;
7627 return Heap::true_value();
7628}
7629
7630
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007631/**
7632 * A simple visitor visits every element of Array's.
7633 * The backend storage can be a fixed array for fast elements case,
7634 * or a dictionary for sparse array. Since Dictionary is a subtype
7635 * of FixedArray, the class can be used by both fast and slow cases.
7636 * The second parameter of the constructor, fast_elements, specifies
7637 * whether the storage is a FixedArray or Dictionary.
7638 *
7639 * An index limit is used to deal with the situation that a result array
7640 * length overflows 32-bit non-negative integer.
7641 */
7642class ArrayConcatVisitor {
7643 public:
7644 ArrayConcatVisitor(Handle<FixedArray> storage,
7645 uint32_t index_limit,
7646 bool fast_elements) :
7647 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007648 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007649
7650 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007651 if (i >= index_limit_ - index_offset_) return;
7652 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007653
7654 if (fast_elements_) {
7655 ASSERT(index < static_cast<uint32_t>(storage_->length()));
7656 storage_->set(index, *elm);
7657
7658 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007659 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
7660 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007661 Factory::DictionaryAtNumberPut(dict, index, elm);
7662 if (!result.is_identical_to(dict))
7663 storage_ = result;
7664 }
7665 }
7666
7667 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007668 if (index_limit_ - index_offset_ < delta) {
7669 index_offset_ = index_limit_;
7670 } else {
7671 index_offset_ += delta;
7672 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007673 }
7674
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007675 Handle<FixedArray> storage() { return storage_; }
7676
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007677 private:
7678 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007679 // Limit on the accepted indices. Elements with indices larger than the
7680 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007681 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007682 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007683 uint32_t index_offset_;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007684 const bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007685};
7686
7687
ager@chromium.org3811b432009-10-28 14:53:37 +00007688template<class ExternalArrayClass, class ElementType>
7689static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
7690 bool elements_are_ints,
7691 bool elements_are_guaranteed_smis,
7692 uint32_t range,
7693 ArrayConcatVisitor* visitor) {
7694 Handle<ExternalArrayClass> array(
7695 ExternalArrayClass::cast(receiver->elements()));
7696 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
7697
7698 if (visitor != NULL) {
7699 if (elements_are_ints) {
7700 if (elements_are_guaranteed_smis) {
7701 for (uint32_t j = 0; j < len; j++) {
7702 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
7703 visitor->visit(j, e);
7704 }
7705 } else {
7706 for (uint32_t j = 0; j < len; j++) {
7707 int64_t val = static_cast<int64_t>(array->get(j));
7708 if (Smi::IsValid(static_cast<intptr_t>(val))) {
7709 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
7710 visitor->visit(j, e);
7711 } else {
7712 Handle<Object> e(
7713 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
7714 visitor->visit(j, e);
7715 }
7716 }
7717 }
7718 } else {
7719 for (uint32_t j = 0; j < len; j++) {
7720 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
7721 visitor->visit(j, e);
7722 }
7723 }
7724 }
7725
7726 return len;
7727}
7728
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007729/**
7730 * A helper function that visits elements of a JSObject. Only elements
7731 * whose index between 0 and range (exclusive) are visited.
7732 *
7733 * If the third parameter, visitor, is not NULL, the visitor is called
7734 * with parameters, 'visitor_index_offset + element index' and the element.
7735 *
7736 * It returns the number of visisted elements.
7737 */
7738static uint32_t IterateElements(Handle<JSObject> receiver,
7739 uint32_t range,
7740 ArrayConcatVisitor* visitor) {
7741 uint32_t num_of_elements = 0;
7742
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007743 switch (receiver->GetElementsKind()) {
7744 case JSObject::FAST_ELEMENTS: {
7745 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
7746 uint32_t len = elements->length();
7747 if (range < len) {
7748 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007749 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007750
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007751 for (uint32_t j = 0; j < len; j++) {
7752 Handle<Object> e(elements->get(j));
7753 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007754 num_of_elements++;
7755 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007756 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007757 }
7758 }
7759 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007760 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007761 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007762 case JSObject::PIXEL_ELEMENTS: {
7763 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
7764 uint32_t len = pixels->length();
7765 if (range < len) {
7766 len = range;
7767 }
7768
7769 for (uint32_t j = 0; j < len; j++) {
7770 num_of_elements++;
7771 if (visitor != NULL) {
7772 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
7773 visitor->visit(j, e);
7774 }
7775 }
7776 break;
7777 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007778 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
7779 num_of_elements =
7780 IterateExternalArrayElements<ExternalByteArray, int8_t>(
7781 receiver, true, true, range, visitor);
7782 break;
7783 }
7784 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7785 num_of_elements =
7786 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
7787 receiver, true, true, range, visitor);
7788 break;
7789 }
7790 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
7791 num_of_elements =
7792 IterateExternalArrayElements<ExternalShortArray, int16_t>(
7793 receiver, true, true, range, visitor);
7794 break;
7795 }
7796 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7797 num_of_elements =
7798 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
7799 receiver, true, true, range, visitor);
7800 break;
7801 }
7802 case JSObject::EXTERNAL_INT_ELEMENTS: {
7803 num_of_elements =
7804 IterateExternalArrayElements<ExternalIntArray, int32_t>(
7805 receiver, true, false, range, visitor);
7806 break;
7807 }
7808 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7809 num_of_elements =
7810 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
7811 receiver, true, false, range, visitor);
7812 break;
7813 }
7814 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
7815 num_of_elements =
7816 IterateExternalArrayElements<ExternalFloatArray, float>(
7817 receiver, false, false, range, visitor);
7818 break;
7819 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007820 case JSObject::DICTIONARY_ELEMENTS: {
7821 Handle<NumberDictionary> dict(receiver->element_dictionary());
7822 uint32_t capacity = dict->Capacity();
7823 for (uint32_t j = 0; j < capacity; j++) {
7824 Handle<Object> k(dict->KeyAt(j));
7825 if (dict->IsKey(*k)) {
7826 ASSERT(k->IsNumber());
7827 uint32_t index = static_cast<uint32_t>(k->Number());
7828 if (index < range) {
7829 num_of_elements++;
7830 if (visitor) {
7831 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
7832 }
7833 }
7834 }
7835 }
7836 break;
7837 }
7838 default:
7839 UNREACHABLE();
7840 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007841 }
7842
7843 return num_of_elements;
7844}
7845
7846
7847/**
7848 * A helper function that visits elements of an Array object, and elements
7849 * on its prototypes.
7850 *
7851 * Elements on prototypes are visited first, and only elements whose indices
7852 * less than Array length are visited.
7853 *
7854 * If a ArrayConcatVisitor object is given, the visitor is called with
7855 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007856 *
7857 * The returned number of elements is an upper bound on the actual number
7858 * of elements added. If the same element occurs in more than one object
7859 * in the array's prototype chain, it will be counted more than once, but
7860 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007861 */
7862static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
7863 ArrayConcatVisitor* visitor) {
7864 uint32_t range = static_cast<uint32_t>(array->length()->Number());
7865 Handle<Object> obj = array;
7866
7867 static const int kEstimatedPrototypes = 3;
7868 List< Handle<JSObject> > objects(kEstimatedPrototypes);
7869
7870 // Visit prototype first. If an element on the prototype is shadowed by
7871 // the inheritor using the same index, the ArrayConcatVisitor visits
7872 // the prototype element before the shadowing element.
7873 // The visitor can simply overwrite the old value by new value using
7874 // the same index. This follows Array::concat semantics.
7875 while (!obj->IsNull()) {
7876 objects.Add(Handle<JSObject>::cast(obj));
7877 obj = Handle<Object>(obj->GetPrototype());
7878 }
7879
7880 uint32_t nof_elements = 0;
7881 for (int i = objects.length() - 1; i >= 0; i--) {
7882 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007883 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007884 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007885
7886 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
7887 nof_elements = JSObject::kMaxElementCount;
7888 } else {
7889 nof_elements += encountered_elements;
7890 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007891 }
7892
7893 return nof_elements;
7894}
7895
7896
7897/**
7898 * A helper function of Runtime_ArrayConcat.
7899 *
7900 * The first argument is an Array of arrays and objects. It is the
7901 * same as the arguments array of Array::concat JS function.
7902 *
7903 * If an argument is an Array object, the function visits array
7904 * elements. If an argument is not an Array object, the function
7905 * visits the object as if it is an one-element array.
7906 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007907 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007908 * non-negative number is used as new length. For example, if one
7909 * array length is 2^32 - 1, second array length is 1, the
7910 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007911 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
7912 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007913 */
7914static uint32_t IterateArguments(Handle<JSArray> arguments,
7915 ArrayConcatVisitor* visitor) {
7916 uint32_t visited_elements = 0;
7917 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7918
7919 for (uint32_t i = 0; i < num_of_args; i++) {
7920 Handle<Object> obj(arguments->GetElement(i));
7921 if (obj->IsJSArray()) {
7922 Handle<JSArray> array = Handle<JSArray>::cast(obj);
7923 uint32_t len = static_cast<uint32_t>(array->length()->Number());
7924 uint32_t nof_elements =
7925 IterateArrayAndPrototypeElements(array, visitor);
7926 // Total elements of array and its prototype chain can be more than
7927 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007928 // the array length number of elements. We use the length as an estimate
7929 // for the actual number of elements added.
7930 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
7931 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
7932 visited_elements = JSArray::kMaxElementCount;
7933 } else {
7934 visited_elements += added_elements;
7935 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007936 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007937 } else {
7938 if (visitor) {
7939 visitor->visit(0, obj);
7940 visitor->increase_index_offset(1);
7941 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007942 if (visited_elements < JSArray::kMaxElementCount) {
7943 visited_elements++;
7944 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007945 }
7946 }
7947 return visited_elements;
7948}
7949
7950
7951/**
7952 * Array::concat implementation.
7953 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007954 * TODO(lrn): Fix non-compliance for very large concatenations and update to
7955 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007956 */
7957static Object* Runtime_ArrayConcat(Arguments args) {
7958 ASSERT(args.length() == 1);
7959 HandleScope handle_scope;
7960
7961 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
7962 Handle<JSArray> arguments(arg_arrays);
7963
7964 // Pass 1: estimate the number of elements of the result
7965 // (it could be more than real numbers if prototype has elements).
7966 uint32_t result_length = 0;
7967 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7968
7969 { AssertNoAllocation nogc;
7970 for (uint32_t i = 0; i < num_of_args; i++) {
7971 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007972 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007973 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007974 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007975 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
7976 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007977 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007978 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007979 if (JSObject::kMaxElementCount - result_length < length_estimate) {
7980 result_length = JSObject::kMaxElementCount;
7981 break;
7982 }
7983 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007984 }
7985 }
7986
7987 // Allocate an empty array, will set length and content later.
7988 Handle<JSArray> result = Factory::NewJSArray(0);
7989
7990 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
7991 // If estimated number of elements is more than half of length, a
7992 // fixed array (fast case) is more time and space-efficient than a
7993 // dictionary.
7994 bool fast_case = (estimate_nof_elements * 2) >= result_length;
7995
7996 Handle<FixedArray> storage;
7997 if (fast_case) {
7998 // The backing storage array must have non-existing elements to
7999 // preserve holes across concat operations.
8000 storage = Factory::NewFixedArrayWithHoles(result_length);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00008001 result->set_map(*Factory::GetFastElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008002 } else {
8003 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
8004 uint32_t at_least_space_for = estimate_nof_elements +
8005 (estimate_nof_elements >> 2);
8006 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008007 Factory::NewNumberDictionary(at_least_space_for));
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00008008 result->set_map(*Factory::GetSlowElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008009 }
8010
8011 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
8012
8013 ArrayConcatVisitor visitor(storage, result_length, fast_case);
8014
8015 IterateArguments(arguments, &visitor);
8016
8017 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00008018 // Please note the storage might have changed in the visitor.
8019 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008020
8021 return *result;
8022}
8023
8024
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008025// This will not allocate (flatten the string), but it may run
8026// very slowly for very deeply nested ConsStrings. For debugging use only.
8027static Object* Runtime_GlobalPrint(Arguments args) {
8028 NoHandleAllocation ha;
8029 ASSERT(args.length() == 1);
8030
8031 CONVERT_CHECKED(String, string, args[0]);
8032 StringInputBuffer buffer(string);
8033 while (buffer.has_more()) {
8034 uint16_t character = buffer.GetNext();
8035 PrintF("%c", character);
8036 }
8037 return string;
8038}
8039
ager@chromium.org5ec48922009-05-05 07:25:34 +00008040// Moves all own elements of an object, that are below a limit, to positions
8041// starting at zero. All undefined values are placed after non-undefined values,
8042// and are followed by non-existing element. Does not change the length
8043// property.
8044// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008045static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00008046 ASSERT(args.length() == 2);
8047 CONVERT_CHECKED(JSObject, object, args[0]);
8048 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
8049 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008050}
8051
8052
8053// Move contents of argument 0 (an array) to argument 1 (an array)
8054static Object* Runtime_MoveArrayContents(Arguments args) {
8055 ASSERT(args.length() == 2);
8056 CONVERT_CHECKED(JSArray, from, args[0]);
8057 CONVERT_CHECKED(JSArray, to, args[1]);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00008058 HeapObject* new_elements = from->elements();
8059 Object* new_map;
8060 if (new_elements->map() == Heap::fixed_array_map()) {
8061 new_map = to->map()->GetFastElementsMap();
8062 } else {
8063 new_map = to->map()->GetSlowElementsMap();
8064 }
8065 if (new_map->IsFailure()) return new_map;
8066 to->set_map(Map::cast(new_map));
8067 to->set_elements(new_elements);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008068 to->set_length(from->length());
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00008069 Object* obj = from->ResetElements();
8070 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008071 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008072 return to;
8073}
8074
8075
8076// How many elements does this array have?
8077static Object* Runtime_EstimateNumberOfElements(Arguments args) {
8078 ASSERT(args.length() == 1);
8079 CONVERT_CHECKED(JSArray, array, args[0]);
8080 HeapObject* elements = array->elements();
8081 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008082 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008083 } else {
8084 return array->length();
8085 }
8086}
8087
8088
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008089static Object* Runtime_SwapElements(Arguments args) {
8090 HandleScope handle_scope;
8091
8092 ASSERT_EQ(3, args.length());
8093
ager@chromium.orgac091b72010-05-05 07:34:42 +00008094 CONVERT_ARG_CHECKED(JSObject, object, 0);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008095 Handle<Object> key1 = args.at<Object>(1);
8096 Handle<Object> key2 = args.at<Object>(2);
8097
8098 uint32_t index1, index2;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008099 if (!key1->ToArrayIndex(&index1)
8100 || !key2->ToArrayIndex(&index2)) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00008101 return Top::ThrowIllegalOperation();
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008102 }
8103
ager@chromium.orgac091b72010-05-05 07:34:42 +00008104 Handle<JSObject> jsobject = Handle<JSObject>::cast(object);
8105 Handle<Object> tmp1 = GetElement(jsobject, index1);
8106 Handle<Object> tmp2 = GetElement(jsobject, index2);
8107
8108 SetElement(jsobject, index1, tmp2);
8109 SetElement(jsobject, index2, tmp1);
8110
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008111 return Heap::undefined_value();
8112}
8113
8114
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008115// Returns an array that tells you where in the [0, length) interval an array
8116// might have elements. Can either return keys or intervals. Keys can have
8117// gaps in (undefined). Intervals can also span over some undefined keys.
8118static Object* Runtime_GetArrayKeys(Arguments args) {
8119 ASSERT(args.length() == 2);
8120 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00008121 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008122 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008123 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008124 // Create an array and get all the keys into it, then remove all the
8125 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008126 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008127 int keys_length = keys->length();
8128 for (int i = 0; i < keys_length; i++) {
8129 Object* key = keys->get(i);
8130 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008131 if (!key->ToArrayIndex(&index) || index >= length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008132 // Zap invalid keys.
8133 keys->set_undefined(i);
8134 }
8135 }
8136 return *Factory::NewJSArrayWithElements(keys);
8137 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008138 ASSERT(array->HasFastElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008139 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
8140 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008141 single_interval->set(0, Smi::FromInt(-1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008142 uint32_t actual_length =
8143 static_cast<uint32_t>(FixedArray::cast(array->elements())->length());
ager@chromium.org5ec48922009-05-05 07:25:34 +00008144 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008145 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00008146 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008147 single_interval->set(1, *length_object);
8148 return *Factory::NewJSArrayWithElements(single_interval);
8149 }
8150}
8151
8152
8153// DefineAccessor takes an optional final argument which is the
8154// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
8155// to the way accessors are implemented, it is set for both the getter
8156// and setter on the first call to DefineAccessor and ignored on
8157// subsequent calls.
8158static Object* Runtime_DefineAccessor(Arguments args) {
8159 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
8160 // Compute attributes.
8161 PropertyAttributes attributes = NONE;
8162 if (args.length() == 5) {
8163 CONVERT_CHECKED(Smi, attrs, args[4]);
8164 int value = attrs->value();
8165 // Only attribute bits should be set.
8166 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
8167 attributes = static_cast<PropertyAttributes>(value);
8168 }
8169
8170 CONVERT_CHECKED(JSObject, obj, args[0]);
8171 CONVERT_CHECKED(String, name, args[1]);
8172 CONVERT_CHECKED(Smi, flag, args[2]);
8173 CONVERT_CHECKED(JSFunction, fun, args[3]);
8174 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
8175}
8176
8177
8178static Object* Runtime_LookupAccessor(Arguments args) {
8179 ASSERT(args.length() == 3);
8180 CONVERT_CHECKED(JSObject, obj, args[0]);
8181 CONVERT_CHECKED(String, name, args[1]);
8182 CONVERT_CHECKED(Smi, flag, args[2]);
8183 return obj->LookupAccessor(name, flag->value() == 0);
8184}
8185
8186
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008187#ifdef ENABLE_DEBUGGER_SUPPORT
8188static Object* Runtime_DebugBreak(Arguments args) {
8189 ASSERT(args.length() == 0);
8190 return Execution::DebugBreakHelper();
8191}
8192
8193
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008194// Helper functions for wrapping and unwrapping stack frame ids.
8195static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008196 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008197 return Smi::FromInt(id >> 2);
8198}
8199
8200
8201static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
8202 return static_cast<StackFrame::Id>(wrapped->value() << 2);
8203}
8204
8205
8206// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00008207// args[0]: debug event listener function to set or null or undefined for
8208// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008209// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00008210static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008211 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00008212 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
8213 args[0]->IsUndefined() ||
8214 args[0]->IsNull());
8215 Handle<Object> callback = args.at<Object>(0);
8216 Handle<Object> data = args.at<Object>(1);
8217 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008218
8219 return Heap::undefined_value();
8220}
8221
8222
8223static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00008224 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008225 StackGuard::DebugBreak();
8226 return Heap::undefined_value();
8227}
8228
8229
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008230static Object* DebugLookupResultValue(Object* receiver, String* name,
8231 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00008232 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008233 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008234 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008235 case NORMAL:
8236 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008237 if (value->IsTheHole()) {
8238 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008239 }
8240 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008241 case FIELD:
8242 value =
8243 JSObject::cast(
8244 result->holder())->FastPropertyAt(result->GetFieldIndex());
8245 if (value->IsTheHole()) {
8246 return Heap::undefined_value();
8247 }
8248 return value;
8249 case CONSTANT_FUNCTION:
8250 return result->GetConstantFunction();
8251 case CALLBACKS: {
8252 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008253 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008254 value = receiver->GetPropertyWithCallback(
8255 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00008256 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008257 value = Top::pending_exception();
8258 Top::clear_pending_exception();
8259 if (caught_exception != NULL) {
8260 *caught_exception = true;
8261 }
8262 }
8263 return value;
8264 } else {
8265 return Heap::undefined_value();
8266 }
8267 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008268 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008269 case MAP_TRANSITION:
8270 case CONSTANT_TRANSITION:
8271 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008272 return Heap::undefined_value();
8273 default:
8274 UNREACHABLE();
8275 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008276 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008277 return Heap::undefined_value();
8278}
8279
8280
ager@chromium.org32912102009-01-16 10:38:43 +00008281// Get debugger related details for an object property.
8282// args[0]: object holding property
8283// args[1]: name of the property
8284//
8285// The array returned contains the following information:
8286// 0: Property value
8287// 1: Property details
8288// 2: Property value is exception
8289// 3: Getter function if defined
8290// 4: Setter function if defined
8291// Items 2-4 are only filled if the property has either a getter or a setter
8292// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00008293static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008294 HandleScope scope;
8295
8296 ASSERT(args.length() == 2);
8297
8298 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8299 CONVERT_ARG_CHECKED(String, name, 1);
8300
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008301 // Make sure to set the current context to the context before the debugger was
8302 // entered (if the debugger is entered). The reason for switching context here
8303 // is that for some property lookups (accessors and interceptors) callbacks
8304 // into the embedding application can occour, and the embedding application
8305 // could have the assumption that its own global context is the current
8306 // context and not some internal debugger context.
8307 SaveContext save;
8308 if (Debug::InDebugger()) {
8309 Top::set_context(*Debug::debugger_entry()->GetContext());
8310 }
8311
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008312 // Skip the global proxy as it has no properties and always delegates to the
8313 // real global object.
8314 if (obj->IsJSGlobalProxy()) {
8315 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
8316 }
8317
8318
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008319 // Check if the name is trivially convertible to an index and get the element
8320 // if so.
8321 uint32_t index;
8322 if (name->AsArrayIndex(&index)) {
8323 Handle<FixedArray> details = Factory::NewFixedArray(2);
8324 details->set(0, Runtime::GetElementOrCharAt(obj, index));
8325 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
8326 return *Factory::NewJSArrayWithElements(details);
8327 }
8328
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008329 // Find the number of objects making up this.
8330 int length = LocalPrototypeChainLength(*obj);
8331
8332 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008333 Handle<JSObject> jsproto = obj;
8334 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008335 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008336 jsproto->LocalLookup(*name, &result);
8337 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008338 // LookupResult is not GC safe as it holds raw object pointers.
8339 // GC can happen later in this code so put the required fields into
8340 // local variables using handles when required for later use.
8341 PropertyType result_type = result.type();
8342 Handle<Object> result_callback_obj;
8343 if (result_type == CALLBACKS) {
8344 result_callback_obj = Handle<Object>(result.GetCallbackObject());
8345 }
8346 Smi* property_details = result.GetPropertyDetails().AsSmi();
8347 // DebugLookupResultValue can cause GC so details from LookupResult needs
8348 // to be copied to handles before this.
8349 bool caught_exception = false;
8350 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
8351 &caught_exception);
8352 if (raw_value->IsFailure()) return raw_value;
8353 Handle<Object> value(raw_value);
8354
8355 // If the callback object is a fixed array then it contains JavaScript
8356 // getter and/or setter.
8357 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
8358 result_callback_obj->IsFixedArray();
8359 Handle<FixedArray> details =
8360 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
8361 details->set(0, *value);
8362 details->set(1, property_details);
8363 if (hasJavaScriptAccessors) {
8364 details->set(2,
8365 caught_exception ? Heap::true_value()
8366 : Heap::false_value());
8367 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
8368 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
8369 }
8370
8371 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008372 }
8373 if (i < length - 1) {
8374 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
8375 }
8376 }
8377
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008378 return Heap::undefined_value();
8379}
8380
8381
8382static Object* Runtime_DebugGetProperty(Arguments args) {
8383 HandleScope scope;
8384
8385 ASSERT(args.length() == 2);
8386
8387 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8388 CONVERT_ARG_CHECKED(String, name, 1);
8389
8390 LookupResult result;
8391 obj->Lookup(*name, &result);
8392 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008393 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008394 }
8395 return Heap::undefined_value();
8396}
8397
8398
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008399// Return the property type calculated from the property details.
8400// args[0]: smi with property details.
8401static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
8402 ASSERT(args.length() == 1);
8403 CONVERT_CHECKED(Smi, details, args[0]);
8404 PropertyType type = PropertyDetails(details).type();
8405 return Smi::FromInt(static_cast<int>(type));
8406}
8407
8408
8409// Return the property attribute calculated from the property details.
8410// args[0]: smi with property details.
8411static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
8412 ASSERT(args.length() == 1);
8413 CONVERT_CHECKED(Smi, details, args[0]);
8414 PropertyAttributes attributes = PropertyDetails(details).attributes();
8415 return Smi::FromInt(static_cast<int>(attributes));
8416}
8417
8418
8419// Return the property insertion index calculated from the property details.
8420// args[0]: smi with property details.
8421static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
8422 ASSERT(args.length() == 1);
8423 CONVERT_CHECKED(Smi, details, args[0]);
8424 int index = PropertyDetails(details).index();
8425 return Smi::FromInt(index);
8426}
8427
8428
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008429// Return property value from named interceptor.
8430// args[0]: object
8431// args[1]: property name
8432static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
8433 HandleScope scope;
8434 ASSERT(args.length() == 2);
8435 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8436 RUNTIME_ASSERT(obj->HasNamedInterceptor());
8437 CONVERT_ARG_CHECKED(String, name, 1);
8438
8439 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008440 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008441}
8442
8443
8444// Return element value from indexed interceptor.
8445// args[0]: object
8446// args[1]: index
8447static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
8448 HandleScope scope;
8449 ASSERT(args.length() == 2);
8450 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8451 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
8452 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
8453
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008454 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008455}
8456
8457
8458static Object* Runtime_CheckExecutionState(Arguments args) {
8459 ASSERT(args.length() >= 1);
8460 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00008461 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008462 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008463 return Top::Throw(Heap::illegal_execution_state_symbol());
8464 }
8465
8466 return Heap::true_value();
8467}
8468
8469
8470static Object* Runtime_GetFrameCount(Arguments args) {
8471 HandleScope scope;
8472 ASSERT(args.length() == 1);
8473
8474 // Check arguments.
8475 Object* result = Runtime_CheckExecutionState(args);
8476 if (result->IsFailure()) return result;
8477
8478 // Count all frames which are relevant to debugging stack trace.
8479 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008480 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008481 if (id == StackFrame::NO_ID) {
8482 // If there is no JavaScript stack frame count is 0.
8483 return Smi::FromInt(0);
8484 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008485 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
8486 return Smi::FromInt(n);
8487}
8488
8489
8490static const int kFrameDetailsFrameIdIndex = 0;
8491static const int kFrameDetailsReceiverIndex = 1;
8492static const int kFrameDetailsFunctionIndex = 2;
8493static const int kFrameDetailsArgumentCountIndex = 3;
8494static const int kFrameDetailsLocalCountIndex = 4;
8495static const int kFrameDetailsSourcePositionIndex = 5;
8496static const int kFrameDetailsConstructCallIndex = 6;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008497static const int kFrameDetailsAtReturnIndex = 7;
8498static const int kFrameDetailsDebuggerFrameIndex = 8;
8499static const int kFrameDetailsFirstDynamicIndex = 9;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008500
8501// Return an array with frame details
8502// args[0]: number: break id
8503// args[1]: number: frame index
8504//
8505// The array returned contains the following information:
8506// 0: Frame id
8507// 1: Receiver
8508// 2: Function
8509// 3: Argument count
8510// 4: Local count
8511// 5: Source position
8512// 6: Constructor call
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008513// 7: Is at return
8514// 8: Debugger frame
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008515// Arguments name, value
8516// Locals name, value
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008517// Return value if any
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008518static Object* Runtime_GetFrameDetails(Arguments args) {
8519 HandleScope scope;
8520 ASSERT(args.length() == 2);
8521
8522 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008523 Object* check = Runtime_CheckExecutionState(args);
8524 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008525 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8526
8527 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008528 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008529 if (id == StackFrame::NO_ID) {
8530 // If there are no JavaScript stack frames return undefined.
8531 return Heap::undefined_value();
8532 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008533 int count = 0;
8534 JavaScriptFrameIterator it(id);
8535 for (; !it.done(); it.Advance()) {
8536 if (count == index) break;
8537 count++;
8538 }
8539 if (it.done()) return Heap::undefined_value();
8540
8541 // Traverse the saved contexts chain to find the active context for the
8542 // selected frame.
8543 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008544 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008545 save = save->prev();
8546 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008547 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008548
8549 // Get the frame id.
8550 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
8551
8552 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008553 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008554
8555 // Check for constructor frame.
8556 bool constructor = it.frame()->IsConstructor();
8557
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008558 // Get scope info and read from it for local variable information.
8559 Handle<JSFunction> function(JSFunction::cast(it.frame()->function()));
ager@chromium.orgb5737492010-07-15 09:29:43 +00008560 Handle<SerializedScopeInfo> scope_info(function->shared()->scope_info());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008561 ScopeInfo<> info(*scope_info);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008562
8563 // Get the context.
8564 Handle<Context> context(Context::cast(it.frame()->context()));
8565
8566 // Get the locals names and values into a temporary array.
8567 //
8568 // TODO(1240907): Hide compiler-introduced stack variables
8569 // (e.g. .result)? For users of the debugger, they will probably be
8570 // confusing.
8571 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
8572 for (int i = 0; i < info.NumberOfLocals(); i++) {
8573 // Name of the local.
8574 locals->set(i * 2, *info.LocalName(i));
8575
8576 // Fetch the value of the local - either from the stack or from a
8577 // heap-allocated context.
8578 if (i < info.number_of_stack_slots()) {
8579 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
8580 } else {
8581 Handle<String> name = info.LocalName(i);
8582 // Traverse the context chain to the function context as all local
8583 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008584 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008585 context = Handle<Context>(context->previous());
8586 }
8587 ASSERT(context->is_function_context());
8588 locals->set(i * 2 + 1,
ager@chromium.orgb5737492010-07-15 09:29:43 +00008589 context->get(scope_info->ContextSlotIndex(*name, NULL)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008590 }
8591 }
8592
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008593 // Check whether this frame is positioned at return.
8594 int at_return = (index == 0) ? Debug::IsBreakAtReturn(it.frame()) : false;
8595
8596 // If positioned just before return find the value to be returned and add it
8597 // to the frame information.
8598 Handle<Object> return_value = Factory::undefined_value();
8599 if (at_return) {
8600 StackFrameIterator it2;
8601 Address internal_frame_sp = NULL;
8602 while (!it2.done()) {
8603 if (it2.frame()->is_internal()) {
8604 internal_frame_sp = it2.frame()->sp();
8605 } else {
8606 if (it2.frame()->is_java_script()) {
8607 if (it2.frame()->id() == it.frame()->id()) {
8608 // The internal frame just before the JavaScript frame contains the
8609 // value to return on top. A debug break at return will create an
8610 // internal frame to store the return value (eax/rax/r0) before
8611 // entering the debug break exit frame.
8612 if (internal_frame_sp != NULL) {
8613 return_value =
8614 Handle<Object>(Memory::Object_at(internal_frame_sp));
8615 break;
8616 }
8617 }
8618 }
8619
8620 // Indicate that the previous frame was not an internal frame.
8621 internal_frame_sp = NULL;
8622 }
8623 it2.Advance();
8624 }
8625 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008626
8627 // Now advance to the arguments adapter frame (if any). It contains all
8628 // the provided parameters whereas the function frame always have the number
8629 // of arguments matching the functions parameters. The rest of the
8630 // information (except for what is collected above) is the same.
8631 it.AdvanceToArgumentsFrame();
8632
8633 // Find the number of arguments to fill. At least fill the number of
8634 // parameters for the function and fill more if more parameters are provided.
8635 int argument_count = info.number_of_parameters();
8636 if (argument_count < it.frame()->GetProvidedParametersCount()) {
8637 argument_count = it.frame()->GetProvidedParametersCount();
8638 }
8639
8640 // Calculate the size of the result.
8641 int details_size = kFrameDetailsFirstDynamicIndex +
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008642 2 * (argument_count + info.NumberOfLocals()) +
8643 (at_return ? 1 : 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008644 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8645
8646 // Add the frame id.
8647 details->set(kFrameDetailsFrameIdIndex, *frame_id);
8648
8649 // Add the function (same as in function frame).
8650 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
8651
8652 // Add the arguments count.
8653 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
8654
8655 // Add the locals count
8656 details->set(kFrameDetailsLocalCountIndex,
8657 Smi::FromInt(info.NumberOfLocals()));
8658
8659 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00008660 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008661 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
8662 } else {
8663 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
8664 }
8665
8666 // Add the constructor information.
8667 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
8668
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008669 // Add the at return information.
8670 details->set(kFrameDetailsAtReturnIndex, Heap::ToBoolean(at_return));
8671
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008672 // Add information on whether this frame is invoked in the debugger context.
8673 details->set(kFrameDetailsDebuggerFrameIndex,
8674 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
8675
8676 // Fill the dynamic part.
8677 int details_index = kFrameDetailsFirstDynamicIndex;
8678
8679 // Add arguments name and value.
8680 for (int i = 0; i < argument_count; i++) {
8681 // Name of the argument.
8682 if (i < info.number_of_parameters()) {
8683 details->set(details_index++, *info.parameter_name(i));
8684 } else {
8685 details->set(details_index++, Heap::undefined_value());
8686 }
8687
8688 // Parameter value.
8689 if (i < it.frame()->GetProvidedParametersCount()) {
8690 details->set(details_index++, it.frame()->GetParameter(i));
8691 } else {
8692 details->set(details_index++, Heap::undefined_value());
8693 }
8694 }
8695
8696 // Add locals name and value from the temporary copy from the function frame.
8697 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
8698 details->set(details_index++, locals->get(i));
8699 }
8700
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008701 // Add the value being returned.
8702 if (at_return) {
8703 details->set(details_index++, *return_value);
8704 }
8705
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008706 // Add the receiver (same as in function frame).
8707 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
8708 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
8709 Handle<Object> receiver(it.frame()->receiver());
8710 if (!receiver->IsJSObject()) {
8711 // If the receiver is NOT a JSObject we have hit an optimization
8712 // where a value object is not converted into a wrapped JS objects.
8713 // To hide this optimization from the debugger, we wrap the receiver
8714 // by creating correct wrapper object based on the calling frame's
8715 // global context.
8716 it.Advance();
8717 Handle<Context> calling_frames_global_context(
8718 Context::cast(Context::cast(it.frame()->context())->global_context()));
8719 receiver = Factory::ToObject(receiver, calling_frames_global_context);
8720 }
8721 details->set(kFrameDetailsReceiverIndex, *receiver);
8722
8723 ASSERT_EQ(details_size, details_index);
8724 return *Factory::NewJSArrayWithElements(details);
8725}
8726
8727
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008728// Copy all the context locals into an object used to materialize a scope.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008729static void CopyContextLocalsToScopeObject(
8730 Handle<SerializedScopeInfo> serialized_scope_info,
8731 ScopeInfo<>& scope_info,
8732 Handle<Context> context,
8733 Handle<JSObject> scope_object) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008734 // Fill all context locals to the context extension.
8735 for (int i = Context::MIN_CONTEXT_SLOTS;
8736 i < scope_info.number_of_context_slots();
8737 i++) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00008738 int context_index = serialized_scope_info->ContextSlotIndex(
8739 *scope_info.context_slot_name(i), NULL);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008740
8741 // Don't include the arguments shadow (.arguments) context variable.
8742 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
8743 SetProperty(scope_object,
8744 scope_info.context_slot_name(i),
8745 Handle<Object>(context->get(context_index)), NONE);
8746 }
8747 }
8748}
8749
8750
8751// Create a plain JSObject which materializes the local scope for the specified
8752// frame.
8753static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
8754 Handle<JSFunction> function(JSFunction::cast(frame->function()));
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008755 Handle<SharedFunctionInfo> shared(function->shared());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008756 Handle<SerializedScopeInfo> serialized_scope_info(shared->scope_info());
8757 ScopeInfo<> scope_info(*serialized_scope_info);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008758
8759 // Allocate and initialize a JSObject with all the arguments, stack locals
8760 // heap locals and extension properties of the debugged function.
8761 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
8762
8763 // First fill all parameters.
8764 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8765 SetProperty(local_scope,
8766 scope_info.parameter_name(i),
8767 Handle<Object>(frame->GetParameter(i)), NONE);
8768 }
8769
8770 // Second fill all stack locals.
8771 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
8772 SetProperty(local_scope,
8773 scope_info.stack_slot_name(i),
8774 Handle<Object>(frame->GetExpression(i)), NONE);
8775 }
8776
8777 // Third fill all context locals.
8778 Handle<Context> frame_context(Context::cast(frame->context()));
8779 Handle<Context> function_context(frame_context->fcontext());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008780 CopyContextLocalsToScopeObject(serialized_scope_info, scope_info,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008781 function_context, local_scope);
8782
8783 // Finally copy any properties from the function context extension. This will
8784 // be variables introduced by eval.
8785 if (function_context->closure() == *function) {
8786 if (function_context->has_extension() &&
8787 !function_context->IsGlobalContext()) {
8788 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008789 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008790 for (int i = 0; i < keys->length(); i++) {
8791 // Names of variables introduced by eval are strings.
8792 ASSERT(keys->get(i)->IsString());
8793 Handle<String> key(String::cast(keys->get(i)));
8794 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
8795 }
8796 }
8797 }
8798 return local_scope;
8799}
8800
8801
8802// Create a plain JSObject which materializes the closure content for the
8803// context.
8804static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
8805 ASSERT(context->is_function_context());
8806
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008807 Handle<SharedFunctionInfo> shared(context->closure()->shared());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008808 Handle<SerializedScopeInfo> serialized_scope_info(shared->scope_info());
8809 ScopeInfo<> scope_info(*serialized_scope_info);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008810
8811 // Allocate and initialize a JSObject with all the content of theis function
8812 // closure.
8813 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
8814
8815 // Check whether the arguments shadow object exists.
8816 int arguments_shadow_index =
ager@chromium.orgb5737492010-07-15 09:29:43 +00008817 shared->scope_info()->ContextSlotIndex(Heap::arguments_shadow_symbol(),
8818 NULL);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008819 if (arguments_shadow_index >= 0) {
8820 // In this case all the arguments are available in the arguments shadow
8821 // object.
8822 Handle<JSObject> arguments_shadow(
8823 JSObject::cast(context->get(arguments_shadow_index)));
8824 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8825 SetProperty(closure_scope,
8826 scope_info.parameter_name(i),
8827 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
8828 }
8829 }
8830
8831 // Fill all context locals to the context extension.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008832 CopyContextLocalsToScopeObject(serialized_scope_info, scope_info,
8833 context, closure_scope);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008834
8835 // Finally copy any properties from the function context extension. This will
8836 // be variables introduced by eval.
8837 if (context->has_extension()) {
8838 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008839 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008840 for (int i = 0; i < keys->length(); i++) {
8841 // Names of variables introduced by eval are strings.
8842 ASSERT(keys->get(i)->IsString());
8843 Handle<String> key(String::cast(keys->get(i)));
8844 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
8845 }
8846 }
8847
8848 return closure_scope;
8849}
8850
8851
8852// Iterate over the actual scopes visible from a stack frame. All scopes are
8853// backed by an actual context except the local scope, which is inserted
8854// "artifically" in the context chain.
8855class ScopeIterator {
8856 public:
8857 enum ScopeType {
8858 ScopeTypeGlobal = 0,
8859 ScopeTypeLocal,
8860 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00008861 ScopeTypeClosure,
8862 // Every catch block contains an implicit with block (its parameter is
8863 // a JSContextExtensionObject) that extends current scope with a variable
8864 // holding exception object. Such with blocks are treated as scopes of their
8865 // own type.
8866 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008867 };
8868
8869 explicit ScopeIterator(JavaScriptFrame* frame)
8870 : frame_(frame),
8871 function_(JSFunction::cast(frame->function())),
8872 context_(Context::cast(frame->context())),
8873 local_done_(false),
8874 at_local_(false) {
8875
8876 // Check whether the first scope is actually a local scope.
8877 if (context_->IsGlobalContext()) {
8878 // If there is a stack slot for .result then this local scope has been
8879 // created for evaluating top level code and it is not a real local scope.
8880 // Checking for the existence of .result seems fragile, but the scope info
8881 // saved with the code object does not otherwise have that information.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008882 int index = function_->shared()->scope_info()->
8883 StackSlotIndex(Heap::result_symbol());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008884 at_local_ = index < 0;
8885 } else if (context_->is_function_context()) {
8886 at_local_ = true;
8887 }
8888 }
8889
8890 // More scopes?
8891 bool Done() { return context_.is_null(); }
8892
8893 // Move to the next scope.
8894 void Next() {
8895 // If at a local scope mark the local scope as passed.
8896 if (at_local_) {
8897 at_local_ = false;
8898 local_done_ = true;
8899
8900 // If the current context is not associated with the local scope the
8901 // current context is the next real scope, so don't move to the next
8902 // context in this case.
8903 if (context_->closure() != *function_) {
8904 return;
8905 }
8906 }
8907
8908 // The global scope is always the last in the chain.
8909 if (context_->IsGlobalContext()) {
8910 context_ = Handle<Context>();
8911 return;
8912 }
8913
8914 // Move to the next context.
8915 if (context_->is_function_context()) {
8916 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
8917 } else {
8918 context_ = Handle<Context>(context_->previous());
8919 }
8920
8921 // If passing the local scope indicate that the current scope is now the
8922 // local scope.
8923 if (!local_done_ &&
8924 (context_->IsGlobalContext() || (context_->is_function_context()))) {
8925 at_local_ = true;
8926 }
8927 }
8928
8929 // Return the type of the current scope.
8930 int Type() {
8931 if (at_local_) {
8932 return ScopeTypeLocal;
8933 }
8934 if (context_->IsGlobalContext()) {
8935 ASSERT(context_->global()->IsGlobalObject());
8936 return ScopeTypeGlobal;
8937 }
8938 if (context_->is_function_context()) {
8939 return ScopeTypeClosure;
8940 }
8941 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00008942 // Current scope is either an explicit with statement or a with statement
8943 // implicitely generated for a catch block.
8944 // If the extension object here is a JSContextExtensionObject then
8945 // current with statement is one frome a catch block otherwise it's a
8946 // regular with statement.
8947 if (context_->extension()->IsJSContextExtensionObject()) {
8948 return ScopeTypeCatch;
8949 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008950 return ScopeTypeWith;
8951 }
8952
8953 // Return the JavaScript object with the content of the current scope.
8954 Handle<JSObject> ScopeObject() {
8955 switch (Type()) {
8956 case ScopeIterator::ScopeTypeGlobal:
8957 return Handle<JSObject>(CurrentContext()->global());
8958 break;
8959 case ScopeIterator::ScopeTypeLocal:
8960 // Materialize the content of the local scope into a JSObject.
8961 return MaterializeLocalScope(frame_);
8962 break;
8963 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00008964 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008965 // Return the with object.
8966 return Handle<JSObject>(CurrentContext()->extension());
8967 break;
8968 case ScopeIterator::ScopeTypeClosure:
8969 // Materialize the content of the closure scope into a JSObject.
8970 return MaterializeClosure(CurrentContext());
8971 break;
8972 }
8973 UNREACHABLE();
8974 return Handle<JSObject>();
8975 }
8976
8977 // Return the context for this scope. For the local context there might not
8978 // be an actual context.
8979 Handle<Context> CurrentContext() {
8980 if (at_local_ && context_->closure() != *function_) {
8981 return Handle<Context>();
8982 }
8983 return context_;
8984 }
8985
8986#ifdef DEBUG
8987 // Debug print of the content of the current scope.
8988 void DebugPrint() {
8989 switch (Type()) {
8990 case ScopeIterator::ScopeTypeGlobal:
8991 PrintF("Global:\n");
8992 CurrentContext()->Print();
8993 break;
8994
8995 case ScopeIterator::ScopeTypeLocal: {
8996 PrintF("Local:\n");
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008997 ScopeInfo<> scope_info(function_->shared()->scope_info());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008998 scope_info.Print();
8999 if (!CurrentContext().is_null()) {
9000 CurrentContext()->Print();
9001 if (CurrentContext()->has_extension()) {
9002 Handle<JSObject> extension =
9003 Handle<JSObject>(CurrentContext()->extension());
9004 if (extension->IsJSContextExtensionObject()) {
9005 extension->Print();
9006 }
9007 }
9008 }
9009 break;
9010 }
9011
9012 case ScopeIterator::ScopeTypeWith: {
9013 PrintF("With:\n");
9014 Handle<JSObject> extension =
9015 Handle<JSObject>(CurrentContext()->extension());
9016 extension->Print();
9017 break;
9018 }
9019
ager@chromium.orga1645e22009-09-09 19:27:10 +00009020 case ScopeIterator::ScopeTypeCatch: {
9021 PrintF("Catch:\n");
9022 Handle<JSObject> extension =
9023 Handle<JSObject>(CurrentContext()->extension());
9024 extension->Print();
9025 break;
9026 }
9027
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009028 case ScopeIterator::ScopeTypeClosure: {
9029 PrintF("Closure:\n");
9030 CurrentContext()->Print();
9031 if (CurrentContext()->has_extension()) {
9032 Handle<JSObject> extension =
9033 Handle<JSObject>(CurrentContext()->extension());
9034 if (extension->IsJSContextExtensionObject()) {
9035 extension->Print();
9036 }
9037 }
9038 break;
9039 }
9040
9041 default:
9042 UNREACHABLE();
9043 }
9044 PrintF("\n");
9045 }
9046#endif
9047
9048 private:
9049 JavaScriptFrame* frame_;
9050 Handle<JSFunction> function_;
9051 Handle<Context> context_;
9052 bool local_done_;
9053 bool at_local_;
9054
9055 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
9056};
9057
9058
9059static Object* Runtime_GetScopeCount(Arguments args) {
9060 HandleScope scope;
9061 ASSERT(args.length() == 2);
9062
9063 // Check arguments.
9064 Object* check = Runtime_CheckExecutionState(args);
9065 if (check->IsFailure()) return check;
9066 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9067
9068 // Get the frame where the debugging is performed.
9069 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9070 JavaScriptFrameIterator it(id);
9071 JavaScriptFrame* frame = it.frame();
9072
9073 // Count the visible scopes.
9074 int n = 0;
9075 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
9076 n++;
9077 }
9078
9079 return Smi::FromInt(n);
9080}
9081
9082
9083static const int kScopeDetailsTypeIndex = 0;
9084static const int kScopeDetailsObjectIndex = 1;
9085static const int kScopeDetailsSize = 2;
9086
9087// Return an array with scope details
9088// args[0]: number: break id
9089// args[1]: number: frame index
9090// args[2]: number: scope index
9091//
9092// The array returned contains the following information:
9093// 0: Scope type
9094// 1: Scope object
9095static Object* Runtime_GetScopeDetails(Arguments args) {
9096 HandleScope scope;
9097 ASSERT(args.length() == 3);
9098
9099 // Check arguments.
9100 Object* check = Runtime_CheckExecutionState(args);
9101 if (check->IsFailure()) return check;
9102 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9103 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
9104
9105 // Get the frame where the debugging is performed.
9106 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9107 JavaScriptFrameIterator frame_it(id);
9108 JavaScriptFrame* frame = frame_it.frame();
9109
9110 // Find the requested scope.
9111 int n = 0;
9112 ScopeIterator it(frame);
9113 for (; !it.Done() && n < index; it.Next()) {
9114 n++;
9115 }
9116 if (it.Done()) {
9117 return Heap::undefined_value();
9118 }
9119
9120 // Calculate the size of the result.
9121 int details_size = kScopeDetailsSize;
9122 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
9123
9124 // Fill in scope details.
9125 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
9126 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
9127
9128 return *Factory::NewJSArrayWithElements(details);
9129}
9130
9131
9132static Object* Runtime_DebugPrintScopes(Arguments args) {
9133 HandleScope scope;
9134 ASSERT(args.length() == 0);
9135
9136#ifdef DEBUG
9137 // Print the scopes for the top frame.
9138 StackFrameLocator locator;
9139 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
9140 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
9141 it.DebugPrint();
9142 }
9143#endif
9144 return Heap::undefined_value();
9145}
9146
9147
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009148static Object* Runtime_GetCFrames(Arguments args) {
9149 HandleScope scope;
9150 ASSERT(args.length() == 1);
9151 Object* result = Runtime_CheckExecutionState(args);
9152 if (result->IsFailure()) return result;
9153
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00009154#if V8_HOST_ARCH_64_BIT
9155 UNIMPLEMENTED();
9156 return Heap::undefined_value();
9157#else
9158
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009159 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009160 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
9161 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009162 if (frames_count == OS::kStackWalkError) {
9163 return Heap::undefined_value();
9164 }
9165
9166 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
9167 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
9168 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
9169 for (int i = 0; i < frames_count; i++) {
9170 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
9171 frame_value->SetProperty(
9172 *address_str,
9173 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
9174 NONE);
9175
9176 // Get the stack walk text for this frame.
9177 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009178 int frame_text_length = StrLength(frames[i].text);
9179 if (frame_text_length > 0) {
9180 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009181 frame_text = Factory::NewStringFromAscii(str);
9182 }
9183
9184 if (!frame_text.is_null()) {
9185 frame_value->SetProperty(*text_str, *frame_text, NONE);
9186 }
9187
9188 frames_array->set(i, *frame_value);
9189 }
9190 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00009191#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009192}
9193
9194
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00009195static Object* Runtime_GetThreadCount(Arguments args) {
9196 HandleScope scope;
9197 ASSERT(args.length() == 1);
9198
9199 // Check arguments.
9200 Object* result = Runtime_CheckExecutionState(args);
9201 if (result->IsFailure()) return result;
9202
9203 // Count all archived V8 threads.
9204 int n = 0;
9205 for (ThreadState* thread = ThreadState::FirstInUse();
9206 thread != NULL;
9207 thread = thread->Next()) {
9208 n++;
9209 }
9210
9211 // Total number of threads is current thread and archived threads.
9212 return Smi::FromInt(n + 1);
9213}
9214
9215
9216static const int kThreadDetailsCurrentThreadIndex = 0;
9217static const int kThreadDetailsThreadIdIndex = 1;
9218static const int kThreadDetailsSize = 2;
9219
9220// Return an array with thread details
9221// args[0]: number: break id
9222// args[1]: number: thread index
9223//
9224// The array returned contains the following information:
9225// 0: Is current thread?
9226// 1: Thread id
9227static Object* Runtime_GetThreadDetails(Arguments args) {
9228 HandleScope scope;
9229 ASSERT(args.length() == 2);
9230
9231 // Check arguments.
9232 Object* check = Runtime_CheckExecutionState(args);
9233 if (check->IsFailure()) return check;
9234 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
9235
9236 // Allocate array for result.
9237 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
9238
9239 // Thread index 0 is current thread.
9240 if (index == 0) {
9241 // Fill the details.
9242 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
9243 details->set(kThreadDetailsThreadIdIndex,
9244 Smi::FromInt(ThreadManager::CurrentId()));
9245 } else {
9246 // Find the thread with the requested index.
9247 int n = 1;
9248 ThreadState* thread = ThreadState::FirstInUse();
9249 while (index != n && thread != NULL) {
9250 thread = thread->Next();
9251 n++;
9252 }
9253 if (thread == NULL) {
9254 return Heap::undefined_value();
9255 }
9256
9257 // Fill the details.
9258 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
9259 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
9260 }
9261
9262 // Convert to JS array and return.
9263 return *Factory::NewJSArrayWithElements(details);
9264}
9265
9266
whesse@chromium.orge90029b2010-08-02 11:52:17 +00009267// Sets the disable break state
9268// args[0]: disable break state
9269static Object* Runtime_SetDisableBreak(Arguments args) {
9270 HandleScope scope;
9271 ASSERT(args.length() == 1);
9272 CONVERT_BOOLEAN_CHECKED(disable_break, args[0]);
9273 Debug::set_disable_break(disable_break);
9274 return Heap::undefined_value();
9275}
9276
9277
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009278static Object* Runtime_GetBreakLocations(Arguments args) {
9279 HandleScope scope;
9280 ASSERT(args.length() == 1);
9281
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009282 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9283 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009284 // Find the number of break points
9285 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
9286 if (break_locations->IsUndefined()) return Heap::undefined_value();
9287 // Return array as JS array
9288 return *Factory::NewJSArrayWithElements(
9289 Handle<FixedArray>::cast(break_locations));
9290}
9291
9292
9293// Set a break point in a function
9294// args[0]: function
9295// args[1]: number: break source position (within the function source)
9296// args[2]: number: break point object
9297static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
9298 HandleScope scope;
9299 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009300 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9301 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009302 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9303 RUNTIME_ASSERT(source_position >= 0);
9304 Handle<Object> break_point_object_arg = args.at<Object>(2);
9305
9306 // Set break point.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009307 Debug::SetBreakPoint(shared, break_point_object_arg, &source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009308
lrn@chromium.org32d961d2010-06-30 09:09:34 +00009309 return Smi::FromInt(source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009310}
9311
9312
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009313Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
9314 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009315 // Iterate the heap looking for SharedFunctionInfo generated from the
9316 // script. The inner most SharedFunctionInfo containing the source position
9317 // for the requested break point is found.
9318 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
9319 // which is found is not compiled it is compiled and the heap is iterated
9320 // again as the compilation might create inner functions from the newly
9321 // compiled function and the actual requested break point might be in one of
9322 // these functions.
9323 bool done = false;
9324 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00009325 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009326 Handle<SharedFunctionInfo> target;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009327 while (!done) {
9328 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009329 for (HeapObject* obj = iterator.next();
9330 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009331 if (obj->IsSharedFunctionInfo()) {
9332 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
9333 if (shared->script() == *script) {
9334 // If the SharedFunctionInfo found has the requested script data and
9335 // contains the source position it is a candidate.
9336 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00009337 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009338 start_position = shared->start_position();
9339 }
9340 if (start_position <= position &&
9341 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00009342 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009343 // candidate this is the new candidate.
9344 if (target.is_null()) {
9345 target_start_position = start_position;
9346 target = shared;
9347 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00009348 if (target_start_position == start_position &&
9349 shared->end_position() == target->end_position()) {
9350 // If a top-level function contain only one function
9351 // declartion the source for the top-level and the function is
9352 // the same. In that case prefer the non top-level function.
9353 if (!shared->is_toplevel()) {
9354 target_start_position = start_position;
9355 target = shared;
9356 }
9357 } else if (target_start_position <= start_position &&
9358 shared->end_position() <= target->end_position()) {
9359 // This containment check includes equality as a function inside
9360 // a top-level function can share either start or end position
9361 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009362 target_start_position = start_position;
9363 target = shared;
9364 }
9365 }
9366 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009367 }
9368 }
9369 }
9370
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009371 if (target.is_null()) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009372 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009373 }
9374
9375 // If the candidate found is compiled we are done. NOTE: when lazy
9376 // compilation of inner functions is introduced some additional checking
9377 // needs to be done here to compile inner functions.
9378 done = target->is_compiled();
9379 if (!done) {
9380 // If the candidate is not compiled compile it to reveal any inner
9381 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009382 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009383 }
9384 }
9385
9386 return *target;
9387}
9388
9389
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009390// Changes the state of a break point in a script and returns source position
9391// where break point was set. NOTE: Regarding performance see the NOTE for
9392// GetScriptFromScriptData.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009393// args[0]: script to set break point in
9394// args[1]: number: break source position (within the script source)
9395// args[2]: number: break point object
9396static Object* Runtime_SetScriptBreakPoint(Arguments args) {
9397 HandleScope scope;
9398 ASSERT(args.length() == 3);
9399 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
9400 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9401 RUNTIME_ASSERT(source_position >= 0);
9402 Handle<Object> break_point_object_arg = args.at<Object>(2);
9403
9404 // Get the script from the script wrapper.
9405 RUNTIME_ASSERT(wrapper->value()->IsScript());
9406 Handle<Script> script(Script::cast(wrapper->value()));
9407
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009408 Object* result = Runtime::FindSharedFunctionInfoInScript(
9409 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009410 if (!result->IsUndefined()) {
9411 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
9412 // Find position within function. The script position might be before the
9413 // source position of the first function.
9414 int position;
9415 if (shared->start_position() > source_position) {
9416 position = 0;
9417 } else {
9418 position = source_position - shared->start_position();
9419 }
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009420 Debug::SetBreakPoint(shared, break_point_object_arg, &position);
9421 position += shared->start_position();
9422 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009423 }
9424 return Heap::undefined_value();
9425}
9426
9427
9428// Clear a break point
9429// args[0]: number: break point object
9430static Object* Runtime_ClearBreakPoint(Arguments args) {
9431 HandleScope scope;
9432 ASSERT(args.length() == 1);
9433 Handle<Object> break_point_object_arg = args.at<Object>(0);
9434
9435 // Clear break point.
9436 Debug::ClearBreakPoint(break_point_object_arg);
9437
9438 return Heap::undefined_value();
9439}
9440
9441
9442// Change the state of break on exceptions
9443// args[0]: boolean indicating uncaught exceptions
9444// args[1]: boolean indicating on/off
9445static Object* Runtime_ChangeBreakOnException(Arguments args) {
9446 HandleScope scope;
9447 ASSERT(args.length() == 2);
9448 ASSERT(args[0]->IsNumber());
9449 ASSERT(args[1]->IsBoolean());
9450
9451 // Update break point state
9452 ExceptionBreakType type =
9453 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
9454 bool enable = args[1]->ToBoolean()->IsTrue();
9455 Debug::ChangeBreakOnException(type, enable);
9456 return Heap::undefined_value();
9457}
9458
9459
9460// Prepare for stepping
9461// args[0]: break id for checking execution state
9462// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00009463// args[2]: number of times to perform the step, for step out it is the number
9464// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009465static Object* Runtime_PrepareStep(Arguments args) {
9466 HandleScope scope;
9467 ASSERT(args.length() == 3);
9468 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009469 Object* check = Runtime_CheckExecutionState(args);
9470 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009471 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
9472 return Top::Throw(Heap::illegal_argument_symbol());
9473 }
9474
9475 // Get the step action and check validity.
9476 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
9477 if (step_action != StepIn &&
9478 step_action != StepNext &&
9479 step_action != StepOut &&
9480 step_action != StepInMin &&
9481 step_action != StepMin) {
9482 return Top::Throw(Heap::illegal_argument_symbol());
9483 }
9484
9485 // Get the number of steps.
9486 int step_count = NumberToInt32(args[2]);
9487 if (step_count < 1) {
9488 return Top::Throw(Heap::illegal_argument_symbol());
9489 }
9490
ager@chromium.orga1645e22009-09-09 19:27:10 +00009491 // Clear all current stepping setup.
9492 Debug::ClearStepping();
9493
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009494 // Prepare step.
9495 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
9496 return Heap::undefined_value();
9497}
9498
9499
9500// Clear all stepping set by PrepareStep.
9501static Object* Runtime_ClearStepping(Arguments args) {
9502 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009503 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009504 Debug::ClearStepping();
9505 return Heap::undefined_value();
9506}
9507
9508
9509// Creates a copy of the with context chain. The copy of the context chain is
9510// is linked to the function context supplied.
9511static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
9512 Handle<Context> function_context) {
9513 // At the bottom of the chain. Return the function context to link to.
9514 if (context_chain->is_function_context()) {
9515 return function_context;
9516 }
9517
9518 // Recursively copy the with contexts.
9519 Handle<Context> previous(context_chain->previous());
9520 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
9521 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009522 CopyWithContextChain(function_context, previous),
9523 extension,
9524 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009525}
9526
9527
9528// Helper function to find or create the arguments object for
9529// Runtime_DebugEvaluate.
9530static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
9531 Handle<JSFunction> function,
ager@chromium.orgb5737492010-07-15 09:29:43 +00009532 Handle<SerializedScopeInfo> scope_info,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009533 const ScopeInfo<>* sinfo,
9534 Handle<Context> function_context) {
9535 // Try to find the value of 'arguments' to pass as parameter. If it is not
9536 // found (that is the debugged function does not reference 'arguments' and
9537 // does not support eval) then create an 'arguments' object.
9538 int index;
9539 if (sinfo->number_of_stack_slots() > 0) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00009540 index = scope_info->StackSlotIndex(Heap::arguments_symbol());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009541 if (index != -1) {
9542 return Handle<Object>(frame->GetExpression(index));
9543 }
9544 }
9545
9546 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00009547 index = scope_info->ContextSlotIndex(Heap::arguments_symbol(), NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009548 if (index != -1) {
9549 return Handle<Object>(function_context->get(index));
9550 }
9551 }
9552
9553 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009554 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
9555 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009556
9557 AssertNoAllocation no_gc;
9558 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009559 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009560 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009561 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009562 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009563 return arguments;
9564}
9565
9566
9567// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00009568// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009569// extension part has all the parameters and locals of the function on the
9570// stack frame. A function which calls eval with the code to evaluate is then
9571// compiled in this context and called in this context. As this context
9572// replaces the context of the function on the stack frame a new (empty)
9573// function is created as well to be used as the closure for the context.
9574// This function and the context acts as replacements for the function on the
9575// stack frame presenting the same view of the values of parameters and
9576// local variables as if the piece of JavaScript was evaluated at the point
9577// where the function on the stack frame is currently stopped.
9578static Object* Runtime_DebugEvaluate(Arguments args) {
9579 HandleScope scope;
9580
9581 // Check the execution state and decode arguments frame and source to be
9582 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009583 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009584 Object* check_result = Runtime_CheckExecutionState(args);
9585 if (check_result->IsFailure()) return check_result;
9586 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9587 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009588 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
9589
9590 // Handle the processing of break.
9591 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009592
9593 // Get the frame where the debugging is performed.
9594 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9595 JavaScriptFrameIterator it(id);
9596 JavaScriptFrame* frame = it.frame();
9597 Handle<JSFunction> function(JSFunction::cast(frame->function()));
ager@chromium.orgb5737492010-07-15 09:29:43 +00009598 Handle<SerializedScopeInfo> scope_info(function->shared()->scope_info());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009599 ScopeInfo<> sinfo(*scope_info);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009600
9601 // Traverse the saved contexts chain to find the active context for the
9602 // selected frame.
9603 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009604 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009605 save = save->prev();
9606 }
9607 ASSERT(save != NULL);
9608 SaveContext savex;
9609 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009610
9611 // Create the (empty) function replacing the function on the stack frame for
9612 // the purpose of evaluating in the context created below. It is important
9613 // that this function does not describe any parameters and local variables
9614 // in the context. If it does then this will cause problems with the lookup
9615 // in Context::Lookup, where context slots for parameters and local variables
9616 // are looked at before the extension object.
9617 Handle<JSFunction> go_between =
9618 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
9619 go_between->set_context(function->context());
9620#ifdef DEBUG
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009621 ScopeInfo<> go_between_sinfo(go_between->shared()->scope_info());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009622 ASSERT(go_between_sinfo.number_of_parameters() == 0);
9623 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
9624#endif
9625
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009626 // Materialize the content of the local scope into a JSObject.
9627 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009628
9629 // Allocate a new context for the debug evaluation and set the extension
9630 // object build.
9631 Handle<Context> context =
9632 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009633 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009634 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009635 Handle<Context> frame_context(Context::cast(frame->context()));
9636 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009637 context = CopyWithContextChain(frame_context, context);
9638
9639 // Wrap the evaluation statement in a new function compiled in the newly
9640 // created context. The function has one parameter which has to be called
9641 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00009642 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009643 // function(arguments,__source__) {return eval(__source__);}
9644 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00009645 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009646 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009647 Handle<String> function_source =
9648 Factory::NewStringFromAscii(Vector<const char>(source_str,
9649 source_str_length));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009650 Handle<SharedFunctionInfo> shared =
ager@chromium.org381abbb2009-02-25 13:23:22 +00009651 Compiler::CompileEval(function_source,
9652 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00009653 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00009654 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009655 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009656 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009657 Factory::NewFunctionFromSharedFunctionInfo(shared, context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009658
9659 // Invoke the result of the compilation to get the evaluation function.
9660 bool has_pending_exception;
9661 Handle<Object> receiver(frame->receiver());
9662 Handle<Object> evaluation_function =
9663 Execution::Call(compiled_function, receiver, 0, NULL,
9664 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009665 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009666
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009667 Handle<Object> arguments = GetArgumentsObject(frame, function, scope_info,
9668 &sinfo, function_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009669
9670 // Invoke the evaluation function and return the result.
9671 const int argc = 2;
9672 Object** argv[argc] = { arguments.location(),
9673 Handle<Object>::cast(source).location() };
9674 Handle<Object> result =
9675 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
9676 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009677 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009678
9679 // Skip the global proxy as it has no properties and always delegates to the
9680 // real global object.
9681 if (result->IsJSGlobalProxy()) {
9682 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
9683 }
9684
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009685 return *result;
9686}
9687
9688
9689static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
9690 HandleScope scope;
9691
9692 // Check the execution state and decode arguments frame and source to be
9693 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009694 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009695 Object* check_result = Runtime_CheckExecutionState(args);
9696 if (check_result->IsFailure()) return check_result;
9697 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009698 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
9699
9700 // Handle the processing of break.
9701 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009702
9703 // Enter the top context from before the debugger was invoked.
9704 SaveContext save;
9705 SaveContext* top = &save;
9706 while (top != NULL && *top->context() == *Debug::debug_context()) {
9707 top = top->prev();
9708 }
9709 if (top != NULL) {
9710 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009711 }
9712
9713 // Get the global context now set to the top context from before the
9714 // debugger was invoked.
9715 Handle<Context> context = Top::global_context();
9716
9717 // Compile the source to be evaluated.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009718 Handle<SharedFunctionInfo> shared =
9719 Compiler::CompileEval(source,
9720 context,
9721 true,
9722 Compiler::DONT_VALIDATE_JSON);
9723 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009724 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009725 Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
9726 context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009727
9728 // Invoke the result of the compilation to get the evaluation function.
9729 bool has_pending_exception;
9730 Handle<Object> receiver = Top::global();
9731 Handle<Object> result =
9732 Execution::Call(compiled_function, receiver, 0, NULL,
9733 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009734 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009735 return *result;
9736}
9737
9738
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009739static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
9740 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009741 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009742
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009743 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009744 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009745
9746 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009747 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00009748 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
9749 // Get the script wrapper in a local handle before calling GetScriptWrapper,
9750 // because using
9751 // instances->set(i, *GetScriptWrapper(script))
9752 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
9753 // already have deferenced the instances handle.
9754 Handle<JSValue> wrapper = GetScriptWrapper(script);
9755 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009756 }
9757
9758 // Return result as a JS array.
9759 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
9760 Handle<JSArray>::cast(result)->SetContent(*instances);
9761 return *result;
9762}
9763
9764
9765// Helper function used by Runtime_DebugReferencedBy below.
9766static int DebugReferencedBy(JSObject* target,
9767 Object* instance_filter, int max_references,
9768 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009769 JSFunction* arguments_function) {
9770 NoHandleAllocation ha;
9771 AssertNoAllocation no_alloc;
9772
9773 // Iterate the heap.
9774 int count = 0;
9775 JSObject* last = NULL;
9776 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009777 HeapObject* heap_obj = NULL;
9778 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009779 (max_references == 0 || count < max_references)) {
9780 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009781 if (heap_obj->IsJSObject()) {
9782 // Skip context extension objects and argument arrays as these are
9783 // checked in the context of functions using them.
9784 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00009785 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009786 obj->map()->constructor() == arguments_function) {
9787 continue;
9788 }
9789
9790 // Check if the JS object has a reference to the object looked for.
9791 if (obj->ReferencesObject(target)) {
9792 // Check instance filter if supplied. This is normally used to avoid
9793 // references from mirror objects (see Runtime_IsInPrototypeChain).
9794 if (!instance_filter->IsUndefined()) {
9795 Object* V = obj;
9796 while (true) {
9797 Object* prototype = V->GetPrototype();
9798 if (prototype->IsNull()) {
9799 break;
9800 }
9801 if (instance_filter == prototype) {
9802 obj = NULL; // Don't add this object.
9803 break;
9804 }
9805 V = prototype;
9806 }
9807 }
9808
9809 if (obj != NULL) {
9810 // Valid reference found add to instance array if supplied an update
9811 // count.
9812 if (instances != NULL && count < instances_size) {
9813 instances->set(count, obj);
9814 }
9815 last = obj;
9816 count++;
9817 }
9818 }
9819 }
9820 }
9821
9822 // Check for circular reference only. This can happen when the object is only
9823 // referenced from mirrors and has a circular reference in which case the
9824 // object is not really alive and would have been garbage collected if not
9825 // referenced from the mirror.
9826 if (count == 1 && last == target) {
9827 count = 0;
9828 }
9829
9830 // Return the number of referencing objects found.
9831 return count;
9832}
9833
9834
9835// Scan the heap for objects with direct references to an object
9836// args[0]: the object to find references to
9837// args[1]: constructor function for instances to exclude (Mirror)
9838// args[2]: the the maximum number of objects to return
9839static Object* Runtime_DebugReferencedBy(Arguments args) {
9840 ASSERT(args.length() == 3);
9841
9842 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009843 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009844
9845 // Check parameters.
9846 CONVERT_CHECKED(JSObject, target, args[0]);
9847 Object* instance_filter = args[1];
9848 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
9849 instance_filter->IsJSObject());
9850 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
9851 RUNTIME_ASSERT(max_references >= 0);
9852
9853 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009854 JSObject* arguments_boilerplate =
9855 Top::context()->global_context()->arguments_boilerplate();
9856 JSFunction* arguments_function =
9857 JSFunction::cast(arguments_boilerplate->map()->constructor());
9858
9859 // Get the number of referencing objects.
9860 int count;
9861 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009862 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009863
9864 // Allocate an array to hold the result.
9865 Object* object = Heap::AllocateFixedArray(count);
9866 if (object->IsFailure()) return object;
9867 FixedArray* instances = FixedArray::cast(object);
9868
9869 // Fill the referencing objects.
9870 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009871 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009872
9873 // Return result as JS array.
9874 Object* result =
9875 Heap::AllocateJSObject(
9876 Top::context()->global_context()->array_function());
9877 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9878 return result;
9879}
9880
9881
9882// Helper function used by Runtime_DebugConstructedBy below.
9883static int DebugConstructedBy(JSFunction* constructor, int max_references,
9884 FixedArray* instances, int instances_size) {
9885 AssertNoAllocation no_alloc;
9886
9887 // Iterate the heap.
9888 int count = 0;
9889 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009890 HeapObject* heap_obj = NULL;
9891 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009892 (max_references == 0 || count < max_references)) {
9893 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009894 if (heap_obj->IsJSObject()) {
9895 JSObject* obj = JSObject::cast(heap_obj);
9896 if (obj->map()->constructor() == constructor) {
9897 // Valid reference found add to instance array if supplied an update
9898 // count.
9899 if (instances != NULL && count < instances_size) {
9900 instances->set(count, obj);
9901 }
9902 count++;
9903 }
9904 }
9905 }
9906
9907 // Return the number of referencing objects found.
9908 return count;
9909}
9910
9911
9912// Scan the heap for objects constructed by a specific function.
9913// args[0]: the constructor to find instances of
9914// args[1]: the the maximum number of objects to return
9915static Object* Runtime_DebugConstructedBy(Arguments args) {
9916 ASSERT(args.length() == 2);
9917
9918 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009919 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009920
9921 // Check parameters.
9922 CONVERT_CHECKED(JSFunction, constructor, args[0]);
9923 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
9924 RUNTIME_ASSERT(max_references >= 0);
9925
9926 // Get the number of referencing objects.
9927 int count;
9928 count = DebugConstructedBy(constructor, max_references, NULL, 0);
9929
9930 // Allocate an array to hold the result.
9931 Object* object = Heap::AllocateFixedArray(count);
9932 if (object->IsFailure()) return object;
9933 FixedArray* instances = FixedArray::cast(object);
9934
9935 // Fill the referencing objects.
9936 count = DebugConstructedBy(constructor, max_references, instances, count);
9937
9938 // Return result as JS array.
9939 Object* result =
9940 Heap::AllocateJSObject(
9941 Top::context()->global_context()->array_function());
9942 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9943 return result;
9944}
9945
9946
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009947// Find the effective prototype object as returned by __proto__.
9948// args[0]: the object to find the prototype for.
9949static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009950 ASSERT(args.length() == 1);
9951
9952 CONVERT_CHECKED(JSObject, obj, args[0]);
9953
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009954 // Use the __proto__ accessor.
9955 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009956}
9957
9958
9959static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009960 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009961 CPU::DebugBreak();
9962 return Heap::undefined_value();
9963}
9964
9965
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009966static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009967#ifdef DEBUG
9968 HandleScope scope;
9969 ASSERT(args.length() == 1);
9970 // Get the function and make sure it is compiled.
9971 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009972 Handle<SharedFunctionInfo> shared(func->shared());
9973 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009974 return Failure::Exception();
9975 }
9976 func->code()->PrintLn();
9977#endif // DEBUG
9978 return Heap::undefined_value();
9979}
ager@chromium.org9085a012009-05-11 19:22:57 +00009980
9981
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009982static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
9983#ifdef DEBUG
9984 HandleScope scope;
9985 ASSERT(args.length() == 1);
9986 // Get the function and make sure it is compiled.
9987 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009988 Handle<SharedFunctionInfo> shared(func->shared());
9989 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009990 return Failure::Exception();
9991 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009992 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009993#endif // DEBUG
9994 return Heap::undefined_value();
9995}
9996
9997
ager@chromium.org9085a012009-05-11 19:22:57 +00009998static Object* Runtime_FunctionGetInferredName(Arguments args) {
9999 NoHandleAllocation ha;
10000 ASSERT(args.length() == 1);
10001
10002 CONVERT_CHECKED(JSFunction, f, args[0]);
10003 return f->shared()->inferred_name();
10004}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010005
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010006
10007static int FindSharedFunctionInfosForScript(Script* script,
10008 FixedArray* buffer) {
10009 AssertNoAllocation no_allocations;
10010
10011 int counter = 0;
10012 int buffer_size = buffer->length();
10013 HeapIterator iterator;
10014 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
10015 ASSERT(obj != NULL);
10016 if (!obj->IsSharedFunctionInfo()) {
10017 continue;
10018 }
10019 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
10020 if (shared->script() != script) {
10021 continue;
10022 }
10023 if (counter < buffer_size) {
10024 buffer->set(counter, shared);
10025 }
10026 counter++;
10027 }
10028 return counter;
10029}
10030
10031// For a script finds all SharedFunctionInfo's in the heap that points
10032// to this script. Returns JSArray of SharedFunctionInfo wrapped
10033// in OpaqueReferences.
10034static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
10035 Arguments args) {
10036 ASSERT(args.length() == 1);
10037 HandleScope scope;
10038 CONVERT_CHECKED(JSValue, script_value, args[0]);
10039
10040 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
10041
10042 const int kBufferSize = 32;
10043
10044 Handle<FixedArray> array;
10045 array = Factory::NewFixedArray(kBufferSize);
10046 int number = FindSharedFunctionInfosForScript(*script, *array);
10047 if (number > kBufferSize) {
10048 array = Factory::NewFixedArray(number);
10049 FindSharedFunctionInfosForScript(*script, *array);
10050 }
10051
10052 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
10053 result->set_length(Smi::FromInt(number));
10054
10055 LiveEdit::WrapSharedFunctionInfos(result);
10056
10057 return *result;
10058}
10059
10060// For a script calculates compilation information about all its functions.
10061// The script source is explicitly specified by the second argument.
10062// The source of the actual script is not used, however it is important that
10063// all generated code keeps references to this particular instance of script.
10064// Returns a JSArray of compilation infos. The array is ordered so that
10065// each function with all its descendant is always stored in a continues range
10066// with the function itself going first. The root function is a script function.
10067static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
10068 ASSERT(args.length() == 2);
10069 HandleScope scope;
10070 CONVERT_CHECKED(JSValue, script, args[0]);
10071 CONVERT_ARG_CHECKED(String, source, 1);
10072 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
10073
10074 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
10075
10076 if (Top::has_pending_exception()) {
10077 return Failure::Exception();
10078 }
10079
10080 return result;
10081}
10082
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010083// Changes the source of the script to a new_source.
10084// If old_script_name is provided (i.e. is a String), also creates a copy of
10085// the script with its original source and sends notification to debugger.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010086static Object* Runtime_LiveEditReplaceScript(Arguments args) {
10087 ASSERT(args.length() == 3);
10088 HandleScope scope;
10089 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
10090 CONVERT_ARG_CHECKED(String, new_source, 1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010091 Handle<Object> old_script_name(args[2]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010092
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010093 CONVERT_CHECKED(Script, original_script_pointer,
10094 original_script_value->value());
10095 Handle<Script> original_script(original_script_pointer);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010096
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010097 Object* old_script = LiveEdit::ChangeScriptSource(original_script,
10098 new_source,
10099 old_script_name);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010100
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010101 if (old_script->IsScript()) {
10102 Handle<Script> script_handle(Script::cast(old_script));
10103 return *(GetScriptWrapper(script_handle));
10104 } else {
10105 return Heap::null_value();
10106 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010107}
10108
10109// Replaces code of SharedFunctionInfo with a new one.
10110static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
10111 ASSERT(args.length() == 2);
10112 HandleScope scope;
10113 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
10114 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
10115
ager@chromium.orgac091b72010-05-05 07:34:42 +000010116 return LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010117}
10118
10119// Connects SharedFunctionInfo to another script.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010120static Object* Runtime_LiveEditFunctionSetScript(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010121 ASSERT(args.length() == 2);
10122 HandleScope scope;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010123 Handle<Object> function_object(args[0]);
10124 Handle<Object> script_object(args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010125
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010126 if (function_object->IsJSValue()) {
10127 Handle<JSValue> function_wrapper = Handle<JSValue>::cast(function_object);
10128 if (script_object->IsJSValue()) {
10129 CONVERT_CHECKED(Script, script, JSValue::cast(*script_object)->value());
10130 script_object = Handle<Object>(script);
10131 }
10132
10133 LiveEdit::SetFunctionScript(function_wrapper, script_object);
10134 } else {
10135 // Just ignore this. We may not have a SharedFunctionInfo for some functions
10136 // and we check it in this function.
10137 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010138
10139 return Heap::undefined_value();
10140}
10141
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010142
10143// In a code of a parent function replaces original function as embedded object
10144// with a substitution one.
10145static Object* Runtime_LiveEditReplaceRefToNestedFunction(Arguments args) {
10146 ASSERT(args.length() == 3);
10147 HandleScope scope;
10148
10149 CONVERT_ARG_CHECKED(JSValue, parent_wrapper, 0);
10150 CONVERT_ARG_CHECKED(JSValue, orig_wrapper, 1);
10151 CONVERT_ARG_CHECKED(JSValue, subst_wrapper, 2);
10152
10153 LiveEdit::ReplaceRefToNestedFunction(parent_wrapper, orig_wrapper,
10154 subst_wrapper);
10155
10156 return Heap::undefined_value();
10157}
10158
10159
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010160// Updates positions of a shared function info (first parameter) according
10161// to script source change. Text change is described in second parameter as
10162// array of groups of 3 numbers:
10163// (change_begin, change_end, change_end_new_position).
10164// Each group describes a change in text; groups are sorted by change_begin.
10165static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
10166 ASSERT(args.length() == 2);
10167 HandleScope scope;
10168 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
10169 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
10170
ager@chromium.orgac091b72010-05-05 07:34:42 +000010171 return LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010172}
10173
10174
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010175// For array of SharedFunctionInfo's (each wrapped in JSValue)
10176// checks that none of them have activations on stacks (of any thread).
10177// Returns array of the same length with corresponding results of
10178// LiveEdit::FunctionPatchabilityStatus type.
ager@chromium.org357bf652010-04-12 11:30:10 +000010179static Object* Runtime_LiveEditCheckAndDropActivations(Arguments args) {
10180 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010181 HandleScope scope;
10182 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +000010183 CONVERT_BOOLEAN_CHECKED(do_drop, args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010184
ager@chromium.org357bf652010-04-12 11:30:10 +000010185 return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010186}
10187
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010188// Compares 2 strings line-by-line and returns diff in form of JSArray of
fschneider@chromium.org013f3e12010-04-26 13:27:52 +000010189// triplets (pos1, pos1_end, pos2_end) describing list of diff chunks.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010190static Object* Runtime_LiveEditCompareStringsLinewise(Arguments args) {
10191 ASSERT(args.length() == 2);
10192 HandleScope scope;
10193 CONVERT_ARG_CHECKED(String, s1, 0);
10194 CONVERT_ARG_CHECKED(String, s2, 1);
10195
10196 return *LiveEdit::CompareStringsLinewise(s1, s2);
10197}
10198
10199
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010200
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010201// A testing entry. Returns statement position which is the closest to
10202// source_position.
10203static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
10204 ASSERT(args.length() == 2);
10205 HandleScope scope;
10206 CONVERT_ARG_CHECKED(JSFunction, function, 0);
10207 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
10208
10209 Handle<Code> code(function->code());
10210
10211 RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
10212 int closest_pc = 0;
10213 int distance = kMaxInt;
10214 while (!it.done()) {
10215 int statement_position = static_cast<int>(it.rinfo()->data());
10216 // Check if this break point is closer that what was previously found.
10217 if (source_position <= statement_position &&
10218 statement_position - source_position < distance) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +000010219 closest_pc =
10220 static_cast<int>(it.rinfo()->pc() - code->instruction_start());
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010221 distance = statement_position - source_position;
10222 // Check whether we can't get any closer.
10223 if (distance == 0) break;
10224 }
10225 it.next();
10226 }
10227
10228 return Smi::FromInt(closest_pc);
10229}
10230
10231
ager@chromium.org357bf652010-04-12 11:30:10 +000010232// Calls specified function with or without entering the debugger.
10233// This is used in unit tests to run code as if debugger is entered or simply
10234// to have a stack with C++ frame in the middle.
10235static Object* Runtime_ExecuteInDebugContext(Arguments args) {
10236 ASSERT(args.length() == 2);
10237 HandleScope scope;
10238 CONVERT_ARG_CHECKED(JSFunction, function, 0);
10239 CONVERT_BOOLEAN_CHECKED(without_debugger, args[1]);
10240
10241 Handle<Object> result;
10242 bool pending_exception;
10243 {
10244 if (without_debugger) {
10245 result = Execution::Call(function, Top::global(), 0, NULL,
10246 &pending_exception);
10247 } else {
10248 EnterDebugger enter_debugger;
10249 result = Execution::Call(function, Top::global(), 0, NULL,
10250 &pending_exception);
10251 }
10252 }
10253 if (!pending_exception) {
10254 return *result;
10255 } else {
10256 return Failure::Exception();
10257 }
10258}
10259
10260
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010261#endif // ENABLE_DEBUGGER_SUPPORT
10262
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010263#ifdef ENABLE_LOGGING_AND_PROFILING
10264
10265static Object* Runtime_ProfilerResume(Arguments args) {
10266 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010267 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010268
10269 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010270 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10271 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010272 return Heap::undefined_value();
10273}
10274
10275
10276static Object* Runtime_ProfilerPause(Arguments args) {
10277 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010278 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010279
10280 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010281 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10282 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010283 return Heap::undefined_value();
10284}
10285
10286#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010287
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010288// Finds the script object from the script data. NOTE: This operation uses
10289// heap traversal to find the function generated for the source position
10290// for the requested break point. For lazily compiled functions several heap
10291// traversals might be required rendering this operation as a rather slow
10292// operation. However for setting break points which is normally done through
10293// some kind of user interaction the performance is not crucial.
10294static Handle<Object> Runtime_GetScriptFromScriptName(
10295 Handle<String> script_name) {
10296 // Scan the heap for Script objects to find the script with the requested
10297 // script data.
10298 Handle<Script> script;
10299 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010300 HeapObject* obj = NULL;
10301 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010302 // If a script is found check if it has the script data requested.
10303 if (obj->IsScript()) {
10304 if (Script::cast(obj)->name()->IsString()) {
10305 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
10306 script = Handle<Script>(Script::cast(obj));
10307 }
10308 }
10309 }
10310 }
10311
10312 // If no script with the requested script data is found return undefined.
10313 if (script.is_null()) return Factory::undefined_value();
10314
10315 // Return the script found.
10316 return GetScriptWrapper(script);
10317}
10318
10319
10320// Get the script object from script data. NOTE: Regarding performance
10321// see the NOTE for GetScriptFromScriptData.
10322// args[0]: script data for the script to find the source for
10323static Object* Runtime_GetScript(Arguments args) {
10324 HandleScope scope;
10325
10326 ASSERT(args.length() == 1);
10327
10328 CONVERT_CHECKED(String, script_name, args[0]);
10329
10330 // Find the requested script.
10331 Handle<Object> result =
10332 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
10333 return *result;
10334}
10335
10336
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010337// Determines whether the given stack frame should be displayed in
10338// a stack trace. The caller is the error constructor that asked
10339// for the stack trace to be collected. The first time a construct
10340// call to this function is encountered it is skipped. The seen_caller
10341// in/out parameter is used to remember if the caller has been seen
10342// yet.
10343static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
10344 bool* seen_caller) {
10345 // Only display JS frames.
10346 if (!raw_frame->is_java_script())
10347 return false;
10348 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
10349 Object* raw_fun = frame->function();
10350 // Not sure when this can happen but skip it just in case.
10351 if (!raw_fun->IsJSFunction())
10352 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010353 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010354 *seen_caller = true;
10355 return false;
10356 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010357 // Skip all frames until we've seen the caller. Also, skip the most
10358 // obvious builtin calls. Some builtin calls (such as Number.ADD
10359 // which is invoked using 'call') are very difficult to recognize
10360 // so we're leaving them in for now.
10361 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010362}
10363
10364
10365// Collect the raw data for a stack trace. Returns an array of three
10366// element segments each containing a receiver, function and native
10367// code offset.
10368static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010369 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010370 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010371 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
10372
10373 HandleScope scope;
10374
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000010375 limit = Max(limit, 0); // Ensure that limit is not negative.
10376 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010377 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010378
10379 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010380 // If the caller parameter is a function we skip frames until we're
10381 // under it before starting to collect.
10382 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010383 int cursor = 0;
10384 int frames_seen = 0;
10385 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010386 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010387 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010388 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010389 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010390 Object* recv = frame->receiver();
10391 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010392 Address pc = frame->pc();
10393 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010394 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010395 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010396 if (cursor + 2 < elements->length()) {
10397 elements->set(cursor++, recv);
10398 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010399 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010400 } else {
10401 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010402 Handle<Object> recv_handle(recv);
10403 Handle<Object> fun_handle(fun);
10404 SetElement(result, cursor++, recv_handle);
10405 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010406 SetElement(result, cursor++, Handle<Smi>(offset));
10407 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010408 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010409 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010410 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010411
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010412 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010413 return *result;
10414}
10415
10416
ager@chromium.org3811b432009-10-28 14:53:37 +000010417// Returns V8 version as a string.
10418static Object* Runtime_GetV8Version(Arguments args) {
10419 ASSERT_EQ(args.length(), 0);
10420
10421 NoHandleAllocation ha;
10422
10423 const char* version_string = v8::V8::GetVersion();
10424
10425 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
10426}
10427
10428
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010429static Object* Runtime_Abort(Arguments args) {
10430 ASSERT(args.length() == 2);
10431 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
10432 Smi::cast(args[1])->value());
10433 Top::PrintStack();
10434 OS::Abort();
10435 UNREACHABLE();
10436 return NULL;
10437}
10438
10439
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010440static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
10441 ASSERT(args.length() == 0);
10442 HandleScope::DeleteExtensions();
10443 return Heap::undefined_value();
10444}
10445
10446
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010447static Object* CacheMiss(FixedArray* cache_obj, int index, Object* key_obj) {
10448 ASSERT(index % 2 == 0); // index of the key
10449 ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
10450 ASSERT(index < cache_obj->length());
10451
10452 HandleScope scope;
10453
10454 Handle<FixedArray> cache(cache_obj);
10455 Handle<Object> key(key_obj);
10456 Handle<JSFunction> factory(JSFunction::cast(
10457 cache->get(JSFunctionResultCache::kFactoryIndex)));
10458 // TODO(antonm): consider passing a receiver when constructing a cache.
10459 Handle<Object> receiver(Top::global_context()->global());
10460
10461 Handle<Object> value;
10462 {
10463 // This handle is nor shared, nor used later, so it's safe.
10464 Object** argv[] = { key.location() };
10465 bool pending_exception = false;
10466 value = Execution::Call(factory,
10467 receiver,
10468 1,
10469 argv,
10470 &pending_exception);
10471 if (pending_exception) return Failure::Exception();
10472 }
10473
10474 cache->set(index, *key);
10475 cache->set(index + 1, *value);
10476 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(index));
10477
10478 return *value;
10479}
10480
10481
10482static Object* Runtime_GetFromCache(Arguments args) {
10483 // This is only called from codegen, so checks might be more lax.
10484 CONVERT_CHECKED(FixedArray, cache, args[0]);
10485 Object* key = args[1];
10486
10487 const int finger_index =
10488 Smi::cast(cache->get(JSFunctionResultCache::kFingerIndex))->value();
10489
10490 Object* o = cache->get(finger_index);
10491 if (o == key) {
10492 // The fastest case: hit the same place again.
10493 return cache->get(finger_index + 1);
10494 }
10495
10496 for (int i = finger_index - 2;
10497 i >= JSFunctionResultCache::kEntriesIndex;
10498 i -= 2) {
10499 o = cache->get(i);
10500 if (o == key) {
10501 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10502 return cache->get(i + 1);
10503 }
10504 }
10505
10506 const int size =
10507 Smi::cast(cache->get(JSFunctionResultCache::kCacheSizeIndex))->value();
10508 ASSERT(size <= cache->length());
10509
10510 for (int i = size - 2; i > finger_index; i -= 2) {
10511 o = cache->get(i);
10512 if (o == key) {
10513 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10514 return cache->get(i + 1);
10515 }
10516 }
10517
10518 // Cache miss. If we have spare room, put new data into it, otherwise
10519 // evict post finger entry which must be least recently used.
10520 if (size < cache->length()) {
10521 cache->set(JSFunctionResultCache::kCacheSizeIndex, Smi::FromInt(size + 2));
10522 return CacheMiss(cache, size, key);
10523 } else {
antonm@chromium.org397e23c2010-04-21 12:00:05 +000010524 int target_index = finger_index + JSFunctionResultCache::kEntrySize;
10525 if (target_index == cache->length()) {
10526 target_index = JSFunctionResultCache::kEntriesIndex;
10527 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010528 return CacheMiss(cache, target_index, key);
10529 }
10530}
10531
kasper.lund44510672008-07-25 07:37:58 +000010532#ifdef DEBUG
10533// ListNatives is ONLY used by the fuzz-natives.js in debug mode
10534// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010535static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +000010536 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010537 HandleScope scope;
10538 Handle<JSArray> result = Factory::NewJSArray(0);
10539 int index = 0;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010540 bool inline_runtime_functions = false;
ager@chromium.orga1645e22009-09-09 19:27:10 +000010541#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010542 { \
10543 HandleScope inner; \
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010544 Handle<String> name; \
10545 /* Inline runtime functions have an underscore in front of the name. */ \
10546 if (inline_runtime_functions) { \
10547 name = Factory::NewStringFromAscii( \
10548 Vector<const char>("_" #Name, StrLength("_" #Name))); \
10549 } else { \
10550 name = Factory::NewStringFromAscii( \
10551 Vector<const char>(#Name, StrLength(#Name))); \
10552 } \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010553 Handle<JSArray> pair = Factory::NewJSArray(0); \
10554 SetElement(pair, 0, name); \
10555 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
10556 SetElement(result, index++, pair); \
10557 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010558 inline_runtime_functions = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010559 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010560 inline_runtime_functions = true;
10561 INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010562#undef ADD_ENTRY
10563 return *result;
10564}
kasper.lund44510672008-07-25 07:37:58 +000010565#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010566
10567
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010568static Object* Runtime_Log(Arguments args) {
10569 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +000010570 CONVERT_CHECKED(String, format, args[0]);
10571 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010572 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010573 Logger::LogRuntime(chars, elms);
10574 return Heap::undefined_value();
10575}
10576
10577
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010578static Object* Runtime_IS_VAR(Arguments args) {
10579 UNREACHABLE(); // implemented as macro in the parser
10580 return NULL;
10581}
10582
10583
10584// ----------------------------------------------------------------------------
10585// Implementation of Runtime
10586
ager@chromium.orga1645e22009-09-09 19:27:10 +000010587#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010588 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +000010589 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010590
10591static Runtime::Function Runtime_functions[] = {
10592 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010593 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010594};
10595
10596#undef F
10597
10598
10599Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
10600 ASSERT(0 <= fid && fid < kNofFunctions);
10601 return &Runtime_functions[fid];
10602}
10603
10604
10605Runtime::Function* Runtime::FunctionForName(const char* name) {
10606 for (Function* f = Runtime_functions; f->name != NULL; f++) {
10607 if (strcmp(f->name, name) == 0) {
10608 return f;
10609 }
10610 }
10611 return NULL;
10612}
10613
10614
10615void Runtime::PerformGC(Object* result) {
10616 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010617 if (failure->IsRetryAfterGC()) {
10618 // Try to do a garbage collection; ignore it if it fails. The C
10619 // entry stub will throw an out-of-memory exception in that case.
10620 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
10621 } else {
10622 // Handle last resort GC and make sure to allow future allocations
10623 // to grow the heap without causing GCs (if possible).
10624 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +000010625 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010626 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010627}
10628
10629
10630} } // namespace v8::internal