blob: c7d3ff7f1bfbef600176c806619a1e946fa703fb [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;
sgjesse@chromium.orgd3b3be02010-08-06 08:09:27 +0000308 if (key->ToArrayIndex(&element_index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000309 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000310 result = SetElement(boilerplate, element_index, value);
sgjesse@chromium.orgd3b3be02010-08-06 08:09:27 +0000311 } else if (key->IsSymbol()) {
312 // The key is not an array index.
313 Handle<String> name(String::cast(*key));
314 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000315 } else {
316 // Non-uint32 number.
317 ASSERT(key->IsNumber());
318 double num = key->Number();
319 char arr[100];
320 Vector<char> buffer(arr, ARRAY_SIZE(arr));
321 const char* str = DoubleToCString(num, buffer);
322 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000323 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000324 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000325 // If setting the property on the boilerplate throws an
326 // exception, the exception is converted to an empty handle in
327 // the handle based operations. In that case, we need to
328 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000329 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000330 }
331 }
332
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000333 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000334}
335
336
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000337static Handle<Object> CreateArrayLiteralBoilerplate(
338 Handle<FixedArray> literals,
339 Handle<FixedArray> elements) {
340 // Create the JSArray.
341 Handle<JSFunction> constructor(
342 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
343 Handle<Object> object = Factory::NewJSObject(constructor);
344
345 Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
346
347 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
348 for (int i = 0; i < content->length(); i++) {
349 if (content->get(i)->IsFixedArray()) {
350 // The value contains the constant_properties of a
351 // simple object literal.
352 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
353 Handle<Object> result =
354 CreateLiteralBoilerplate(literals, fa);
355 if (result.is_null()) return result;
356 content->set(i, *result);
357 }
358 }
359
360 // Set the elements.
361 Handle<JSArray>::cast(object)->SetContent(*content);
362 return object;
363}
364
365
366static Handle<Object> CreateLiteralBoilerplate(
367 Handle<FixedArray> literals,
368 Handle<FixedArray> array) {
369 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
370 switch (CompileTimeValue::GetType(array)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000371 case CompileTimeValue::OBJECT_LITERAL_FAST_ELEMENTS:
372 return CreateObjectLiteralBoilerplate(literals, elements, true);
373 case CompileTimeValue::OBJECT_LITERAL_SLOW_ELEMENTS:
374 return CreateObjectLiteralBoilerplate(literals, elements, false);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000375 case CompileTimeValue::ARRAY_LITERAL:
376 return CreateArrayLiteralBoilerplate(literals, elements);
377 default:
378 UNREACHABLE();
379 return Handle<Object>::null();
380 }
381}
382
383
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000384static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000385 // Takes a FixedArray of elements containing the literal elements of
386 // the array literal and produces JSArray with those elements.
387 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000388 // which contains the context from which to get the Array function
389 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000390 HandleScope scope;
391 ASSERT(args.length() == 3);
392 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
393 CONVERT_SMI_CHECKED(literals_index, args[1]);
394 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000395
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000396 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
397 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000398
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000399 // Update the functions literal and return the boilerplate.
400 literals->set(literals_index, *object);
401 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000402}
403
404
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000405static Object* Runtime_CreateObjectLiteral(Arguments args) {
406 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000407 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000408 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
409 CONVERT_SMI_CHECKED(literals_index, args[1]);
410 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000411 CONVERT_SMI_CHECKED(fast_elements, args[3]);
412 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000413
414 // Check if boilerplate exists. If not, create it first.
415 Handle<Object> boilerplate(literals->get(literals_index));
416 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000417 boilerplate = CreateObjectLiteralBoilerplate(literals,
418 constant_properties,
419 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000420 if (boilerplate.is_null()) return Failure::Exception();
421 // Update the functions literal and return the boilerplate.
422 literals->set(literals_index, *boilerplate);
423 }
424 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
425}
426
427
428static Object* Runtime_CreateObjectLiteralShallow(Arguments args) {
429 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000430 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000431 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
432 CONVERT_SMI_CHECKED(literals_index, args[1]);
433 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000434 CONVERT_SMI_CHECKED(fast_elements, args[3]);
435 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000436
437 // Check if boilerplate exists. If not, create it first.
438 Handle<Object> boilerplate(literals->get(literals_index));
439 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000440 boilerplate = CreateObjectLiteralBoilerplate(literals,
441 constant_properties,
442 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000443 if (boilerplate.is_null()) return Failure::Exception();
444 // Update the functions literal and return the boilerplate.
445 literals->set(literals_index, *boilerplate);
446 }
447 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
448}
449
450
451static Object* Runtime_CreateArrayLiteral(Arguments args) {
452 HandleScope scope;
453 ASSERT(args.length() == 3);
454 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
455 CONVERT_SMI_CHECKED(literals_index, args[1]);
456 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
457
458 // Check if boilerplate exists. If not, create it first.
459 Handle<Object> boilerplate(literals->get(literals_index));
460 if (*boilerplate == Heap::undefined_value()) {
461 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
462 if (boilerplate.is_null()) return Failure::Exception();
463 // Update the functions literal and return the boilerplate.
464 literals->set(literals_index, *boilerplate);
465 }
466 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
467}
468
469
470static Object* Runtime_CreateArrayLiteralShallow(Arguments args) {
471 HandleScope scope;
472 ASSERT(args.length() == 3);
473 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
474 CONVERT_SMI_CHECKED(literals_index, args[1]);
475 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
476
477 // Check if boilerplate exists. If not, create it first.
478 Handle<Object> boilerplate(literals->get(literals_index));
479 if (*boilerplate == Heap::undefined_value()) {
480 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
481 if (boilerplate.is_null()) return Failure::Exception();
482 // Update the functions literal and return the boilerplate.
483 literals->set(literals_index, *boilerplate);
484 }
485 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
486}
487
488
ager@chromium.org32912102009-01-16 10:38:43 +0000489static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
490 ASSERT(args.length() == 2);
491 CONVERT_CHECKED(String, key, args[0]);
492 Object* value = args[1];
493 // Create a catch context extension object.
494 JSFunction* constructor =
495 Top::context()->global_context()->context_extension_function();
496 Object* object = Heap::AllocateJSObject(constructor);
497 if (object->IsFailure()) return object;
498 // Assign the exception value to the catch variable and make sure
499 // that the catch variable is DontDelete.
500 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
501 if (value->IsFailure()) return value;
502 return object;
503}
504
505
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000506static Object* Runtime_ClassOf(Arguments args) {
507 NoHandleAllocation ha;
508 ASSERT(args.length() == 1);
509 Object* obj = args[0];
510 if (!obj->IsJSObject()) return Heap::null_value();
511 return JSObject::cast(obj)->class_name();
512}
513
ager@chromium.org7c537e22008-10-16 08:43:32 +0000514
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000515static Object* Runtime_IsInPrototypeChain(Arguments args) {
516 NoHandleAllocation ha;
517 ASSERT(args.length() == 2);
518 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
519 Object* O = args[0];
520 Object* V = args[1];
521 while (true) {
522 Object* prototype = V->GetPrototype();
523 if (prototype->IsNull()) return Heap::false_value();
524 if (O == prototype) return Heap::true_value();
525 V = prototype;
526 }
527}
528
529
ager@chromium.org9085a012009-05-11 19:22:57 +0000530// Inserts an object as the hidden prototype of another object.
531static Object* Runtime_SetHiddenPrototype(Arguments args) {
532 NoHandleAllocation ha;
533 ASSERT(args.length() == 2);
534 CONVERT_CHECKED(JSObject, jsobject, args[0]);
535 CONVERT_CHECKED(JSObject, proto, args[1]);
536
537 // Sanity checks. The old prototype (that we are replacing) could
538 // theoretically be null, but if it is not null then check that we
539 // didn't already install a hidden prototype here.
540 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
541 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
542 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
543
544 // Allocate up front before we start altering state in case we get a GC.
545 Object* map_or_failure = proto->map()->CopyDropTransitions();
546 if (map_or_failure->IsFailure()) return map_or_failure;
547 Map* new_proto_map = Map::cast(map_or_failure);
548
549 map_or_failure = jsobject->map()->CopyDropTransitions();
550 if (map_or_failure->IsFailure()) return map_or_failure;
551 Map* new_map = Map::cast(map_or_failure);
552
553 // Set proto's prototype to be the old prototype of the object.
554 new_proto_map->set_prototype(jsobject->GetPrototype());
555 proto->set_map(new_proto_map);
556 new_proto_map->set_is_hidden_prototype();
557
558 // Set the object's prototype to proto.
559 new_map->set_prototype(proto);
560 jsobject->set_map(new_map);
561
562 return Heap::undefined_value();
563}
564
565
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000566static Object* Runtime_IsConstructCall(Arguments args) {
567 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000568 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000569 JavaScriptFrameIterator it;
570 return Heap::ToBoolean(it.frame()->IsConstructor());
571}
572
573
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000574// Recursively traverses hidden prototypes if property is not found
575static void GetOwnPropertyImplementation(JSObject* obj,
576 String* name,
577 LookupResult* result) {
578 obj->LocalLookupRealNamedProperty(name, result);
579
580 if (!result->IsProperty()) {
581 Object* proto = obj->GetPrototype();
582 if (proto->IsJSObject() &&
583 JSObject::cast(proto)->map()->is_hidden_prototype())
584 GetOwnPropertyImplementation(JSObject::cast(proto),
585 name, result);
586 }
587}
588
589
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000590// Enumerator used as indices into the array returned from GetOwnProperty
591enum PropertyDescriptorIndices {
592 IS_ACCESSOR_INDEX,
593 VALUE_INDEX,
594 GETTER_INDEX,
595 SETTER_INDEX,
596 WRITABLE_INDEX,
597 ENUMERABLE_INDEX,
598 CONFIGURABLE_INDEX,
599 DESCRIPTOR_SIZE
600};
601
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000602// Returns an array with the property description:
603// if args[1] is not a property on args[0]
604// returns undefined
605// if args[1] is a data property on args[0]
606// [false, value, Writeable, Enumerable, Configurable]
607// if args[1] is an accessor on args[0]
608// [true, GetFunction, SetFunction, Enumerable, Configurable]
609static Object* Runtime_GetOwnProperty(Arguments args) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000610 ASSERT(args.length() == 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000611 HandleScope scope;
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000612 Handle<FixedArray> elms = Factory::NewFixedArray(DESCRIPTOR_SIZE);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000613 Handle<JSArray> desc = Factory::NewJSArrayWithElements(elms);
614 LookupResult result;
615 CONVERT_CHECKED(JSObject, obj, args[0]);
616 CONVERT_CHECKED(String, name, args[1]);
617
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000618 // This could be an element.
619 uint32_t index;
620 if (name->AsArrayIndex(&index)) {
621 if (!obj->HasLocalElement(index)) {
622 return Heap::undefined_value();
623 }
624
625 // Special handling of string objects according to ECMAScript 5 15.5.5.2.
626 // Note that this might be a string object with elements other than the
627 // actual string value. This is covered by the subsequent cases.
628 if (obj->IsStringObjectWithCharacterAt(index)) {
629 JSValue* js_value = JSValue::cast(obj);
630 String* str = String::cast(js_value->value());
631 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
632 elms->set(VALUE_INDEX, str->SubString(index, index+1));
633 elms->set(WRITABLE_INDEX, Heap::false_value());
634 elms->set(ENUMERABLE_INDEX, Heap::false_value());
635 elms->set(CONFIGURABLE_INDEX, Heap::false_value());
636 return *desc;
637 }
638
639 // This can potentially be an element in the elements dictionary or
640 // a fast element.
641 if (obj->HasDictionaryElements()) {
642 NumberDictionary* dictionary = obj->element_dictionary();
643 int entry = dictionary->FindEntry(index);
644 PropertyDetails details = dictionary->DetailsAt(entry);
645 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
646 elms->set(VALUE_INDEX, dictionary->ValueAt(entry));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000647 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!details.IsReadOnly()));
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000648 elms->set(ENUMERABLE_INDEX, Heap::ToBoolean(!details.IsDontEnum()));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000649 elms->set(CONFIGURABLE_INDEX, Heap::ToBoolean(!details.IsDontDelete()));
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000650 return *desc;
651 } else {
652 // Elements that are stored as array elements always has:
653 // writable: true, configurable: true, enumerable: true.
654 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
655 elms->set(VALUE_INDEX, obj->GetElement(index));
656 elms->set(WRITABLE_INDEX, Heap::true_value());
657 elms->set(ENUMERABLE_INDEX, Heap::true_value());
658 elms->set(CONFIGURABLE_INDEX, Heap::true_value());
659 return *desc;
660 }
661 }
662
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000663 // Use recursive implementation to also traverse hidden prototypes
664 GetOwnPropertyImplementation(obj, name, &result);
665
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000666 if (!result.IsProperty()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000667 return Heap::undefined_value();
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000668 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000669 if (result.type() == CALLBACKS) {
670 Object* structure = result.GetCallbackObject();
ager@chromium.org5c838252010-02-19 08:53:10 +0000671 if (structure->IsProxy() || structure->IsAccessorInfo()) {
672 // Property that is internally implemented as a callback or
673 // an API defined callback.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000674 Object* value = obj->GetPropertyWithCallback(
675 obj, structure, name, result.holder());
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000676 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
677 elms->set(VALUE_INDEX, value);
678 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!result.IsReadOnly()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000679 } else if (structure->IsFixedArray()) {
680 // __defineGetter__/__defineSetter__ callback.
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000681 elms->set(IS_ACCESSOR_INDEX, Heap::true_value());
682 elms->set(GETTER_INDEX, FixedArray::cast(structure)->get(0));
683 elms->set(SETTER_INDEX, FixedArray::cast(structure)->get(1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000684 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000685 return Heap::undefined_value();
686 }
687 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000688 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
689 elms->set(VALUE_INDEX, result.GetLazyValue());
690 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!result.IsReadOnly()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000691 }
692
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000693 elms->set(ENUMERABLE_INDEX, Heap::ToBoolean(!result.IsDontEnum()));
694 elms->set(CONFIGURABLE_INDEX, Heap::ToBoolean(!result.IsDontDelete()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000695 return *desc;
696}
697
698
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +0000699static Object* Runtime_PreventExtensions(Arguments args) {
700 ASSERT(args.length() == 1);
701 CONVERT_CHECKED(JSObject, obj, args[0]);
702 return obj->PreventExtensions();
703}
704
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000705static Object* Runtime_IsExtensible(Arguments args) {
706 ASSERT(args.length() == 1);
707 CONVERT_CHECKED(JSObject, obj, args[0]);
708 return obj->map()->is_extensible() ? Heap::true_value()
709 : Heap::false_value();
710}
711
712
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000713static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000714 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000715 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000716 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
717 CONVERT_ARG_CHECKED(String, pattern, 1);
718 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000719 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
720 if (result.is_null()) return Failure::Exception();
721 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000722}
723
724
725static Object* Runtime_CreateApiFunction(Arguments args) {
726 HandleScope scope;
727 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000728 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000729 return *Factory::CreateApiFunction(data);
730}
731
732
733static Object* Runtime_IsTemplate(Arguments args) {
734 ASSERT(args.length() == 1);
735 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000736 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000737 return Heap::ToBoolean(result);
738}
739
740
741static Object* Runtime_GetTemplateField(Arguments args) {
742 ASSERT(args.length() == 2);
743 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000744 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000745 int index = field->value();
746 int offset = index * kPointerSize + HeapObject::kHeaderSize;
747 InstanceType type = templ->map()->instance_type();
748 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
749 type == OBJECT_TEMPLATE_INFO_TYPE);
750 RUNTIME_ASSERT(offset > 0);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000751 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000752 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
753 } else {
754 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
755 }
756 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000757}
758
759
ager@chromium.org870a0b62008-11-04 11:43:05 +0000760static Object* Runtime_DisableAccessChecks(Arguments args) {
761 ASSERT(args.length() == 1);
762 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000763 Map* old_map = object->map();
764 bool needs_access_checks = old_map->is_access_check_needed();
765 if (needs_access_checks) {
766 // Copy map so it won't interfere constructor's initial map.
767 Object* new_map = old_map->CopyDropTransitions();
768 if (new_map->IsFailure()) return new_map;
769
770 Map::cast(new_map)->set_is_access_check_needed(false);
771 object->set_map(Map::cast(new_map));
772 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000773 return needs_access_checks ? Heap::true_value() : Heap::false_value();
774}
775
776
777static Object* Runtime_EnableAccessChecks(Arguments args) {
778 ASSERT(args.length() == 1);
779 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000780 Map* old_map = object->map();
781 if (!old_map->is_access_check_needed()) {
782 // Copy map so it won't interfere constructor's initial map.
783 Object* new_map = old_map->CopyDropTransitions();
784 if (new_map->IsFailure()) return new_map;
785
786 Map::cast(new_map)->set_is_access_check_needed(true);
787 object->set_map(Map::cast(new_map));
788 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000789 return Heap::undefined_value();
790}
791
792
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000793static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
794 HandleScope scope;
795 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
796 Handle<Object> args[2] = { type_handle, name };
797 Handle<Object> error =
798 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
799 return Top::Throw(*error);
800}
801
802
803static Object* Runtime_DeclareGlobals(Arguments args) {
804 HandleScope scope;
805 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
806
ager@chromium.org3811b432009-10-28 14:53:37 +0000807 Handle<Context> context = args.at<Context>(0);
808 CONVERT_ARG_CHECKED(FixedArray, pairs, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000809 bool is_eval = Smi::cast(args[2])->value() == 1;
810
811 // Compute the property attributes. According to ECMA-262, section
812 // 13, page 71, the property must be read-only and
813 // non-deletable. However, neither SpiderMonkey nor KJS creates the
814 // property as read-only, so we don't either.
815 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
816
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000817 // Traverse the name/value pairs and set the properties.
818 int length = pairs->length();
819 for (int i = 0; i < length; i += 2) {
820 HandleScope scope;
821 Handle<String> name(String::cast(pairs->get(i)));
822 Handle<Object> value(pairs->get(i + 1));
823
824 // We have to declare a global const property. To capture we only
825 // assign to it when evaluating the assignment for "const x =
826 // <expr>" the initial value is the hole.
827 bool is_const_property = value->IsTheHole();
828
829 if (value->IsUndefined() || is_const_property) {
830 // Lookup the property in the global object, and don't set the
831 // value of the variable if the property is already there.
832 LookupResult lookup;
833 global->Lookup(*name, &lookup);
834 if (lookup.IsProperty()) {
835 // Determine if the property is local by comparing the holder
836 // against the global object. The information will be used to
837 // avoid throwing re-declaration errors when declaring
838 // variables or constants that exist in the prototype chain.
839 bool is_local = (*global == lookup.holder());
840 // Get the property attributes and determine if the property is
841 // read-only.
842 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
843 bool is_read_only = (attributes & READ_ONLY) != 0;
844 if (lookup.type() == INTERCEPTOR) {
845 // If the interceptor says the property is there, we
846 // just return undefined without overwriting the property.
847 // Otherwise, we continue to setting the property.
848 if (attributes != ABSENT) {
849 // Check if the existing property conflicts with regards to const.
850 if (is_local && (is_read_only || is_const_property)) {
851 const char* type = (is_read_only) ? "const" : "var";
852 return ThrowRedeclarationError(type, name);
853 };
854 // The property already exists without conflicting: Go to
855 // the next declaration.
856 continue;
857 }
858 // Fall-through and introduce the absent property by using
859 // SetProperty.
860 } else {
861 if (is_local && (is_read_only || is_const_property)) {
862 const char* type = (is_read_only) ? "const" : "var";
863 return ThrowRedeclarationError(type, name);
864 }
865 // The property already exists without conflicting: Go to
866 // the next declaration.
867 continue;
868 }
869 }
870 } else {
871 // Copy the function and update its context. Use it as value.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000872 Handle<SharedFunctionInfo> shared =
873 Handle<SharedFunctionInfo>::cast(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000874 Handle<JSFunction> function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000875 Factory::NewFunctionFromSharedFunctionInfo(shared, context, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000876 value = function;
877 }
878
879 LookupResult lookup;
880 global->LocalLookup(*name, &lookup);
881
882 PropertyAttributes attributes = is_const_property
883 ? static_cast<PropertyAttributes>(base | READ_ONLY)
884 : base;
885
886 if (lookup.IsProperty()) {
887 // There's a local property that we need to overwrite because
888 // we're either declaring a function or there's an interceptor
889 // that claims the property is absent.
890
891 // Check for conflicting re-declarations. We cannot have
892 // conflicting types in case of intercepted properties because
893 // they are absent.
894 if (lookup.type() != INTERCEPTOR &&
895 (lookup.IsReadOnly() || is_const_property)) {
896 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
897 return ThrowRedeclarationError(type, name);
898 }
899 SetProperty(global, name, value, attributes);
900 } else {
901 // If a property with this name does not already exist on the
902 // global object add the property locally. We take special
903 // precautions to always add it as a local property even in case
904 // of callbacks in the prototype chain (this rules out using
905 // SetProperty). Also, we must use the handle-based version to
906 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000907 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000908 }
909 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000910
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000911 return Heap::undefined_value();
912}
913
914
915static Object* Runtime_DeclareContextSlot(Arguments args) {
916 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000917 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000918
ager@chromium.org7c537e22008-10-16 08:43:32 +0000919 CONVERT_ARG_CHECKED(Context, context, 0);
920 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000921 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000922 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000923 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000924 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000925
926 // Declarations are always done in the function context.
927 context = Handle<Context>(context->fcontext());
928
929 int index;
930 PropertyAttributes attributes;
931 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000932 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000933 context->Lookup(name, flags, &index, &attributes);
934
935 if (attributes != ABSENT) {
936 // The name was declared before; check for conflicting
937 // re-declarations: This is similar to the code in parser.cc in
938 // the AstBuildingParser::Declare function.
939 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
940 // Functions are not read-only.
941 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
942 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
943 return ThrowRedeclarationError(type, name);
944 }
945
946 // Initialize it if necessary.
947 if (*initial_value != NULL) {
948 if (index >= 0) {
949 // The variable or constant context slot should always be in
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000950 // the function context or the arguments object.
951 if (holder->IsContext()) {
952 ASSERT(holder.is_identical_to(context));
953 if (((attributes & READ_ONLY) == 0) ||
954 context->get(index)->IsTheHole()) {
955 context->set(index, *initial_value);
956 }
957 } else {
958 Handle<JSObject>::cast(holder)->SetElement(index, *initial_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000959 }
960 } else {
961 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000962 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000963 SetProperty(context_ext, name, initial_value, mode);
964 }
965 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000966
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000967 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000968 // The property is not in the function context. It needs to be
969 // "declared" in the function context's extension context, or in the
970 // global context.
971 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000972 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000973 // The function context's extension context exists - use it.
974 context_ext = Handle<JSObject>(context->extension());
975 } else {
976 // The function context's extension context does not exists - allocate
977 // it.
978 context_ext = Factory::NewJSObject(Top::context_extension_function());
979 // And store it in the extension slot.
980 context->set_extension(*context_ext);
981 }
982 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000983
ager@chromium.org7c537e22008-10-16 08:43:32 +0000984 // Declare the property by setting it to the initial value if provided,
985 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
986 // constant declarations).
987 ASSERT(!context_ext->HasLocalProperty(*name));
988 Handle<Object> value(Heap::undefined_value());
989 if (*initial_value != NULL) value = initial_value;
990 SetProperty(context_ext, name, value, mode);
991 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
992 }
993
994 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000995}
996
997
998static Object* Runtime_InitializeVarGlobal(Arguments args) {
999 NoHandleAllocation nha;
1000
1001 // Determine if we need to assign to the variable if it already
1002 // exists (based on the number of arguments).
1003 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
1004 bool assign = args.length() == 2;
1005
1006 CONVERT_ARG_CHECKED(String, name, 0);
1007 GlobalObject* global = Top::context()->global();
1008
1009 // According to ECMA-262, section 12.2, page 62, the property must
1010 // not be deletable.
1011 PropertyAttributes attributes = DONT_DELETE;
1012
1013 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001014 // there, there is a property with this name in the prototype chain.
1015 // We follow Safari and Firefox behavior and only set the property
1016 // locally if there is an explicit initialization value that we have
1017 // to assign to the property. When adding the property we take
1018 // special precautions to always add it as a local property even in
1019 // case of callbacks in the prototype chain (this rules out using
1020 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
1021 // this.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001022 // Note that objects can have hidden prototypes, so we need to traverse
1023 // the whole chain of hidden prototypes to do a 'local' lookup.
1024 JSObject* real_holder = global;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001025 LookupResult lookup;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001026 while (true) {
1027 real_holder->LocalLookup(*name, &lookup);
1028 if (lookup.IsProperty()) {
1029 // Determine if this is a redeclaration of something read-only.
1030 if (lookup.IsReadOnly()) {
1031 // If we found readonly property on one of hidden prototypes,
1032 // just shadow it.
1033 if (real_holder != Top::context()->global()) break;
1034 return ThrowRedeclarationError("const", name);
1035 }
1036
1037 // Determine if this is a redeclaration of an intercepted read-only
1038 // property and figure out if the property exists at all.
1039 bool found = true;
1040 PropertyType type = lookup.type();
1041 if (type == INTERCEPTOR) {
1042 HandleScope handle_scope;
1043 Handle<JSObject> holder(real_holder);
1044 PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
1045 real_holder = *holder;
1046 if (intercepted == ABSENT) {
1047 // The interceptor claims the property isn't there. We need to
1048 // make sure to introduce it.
1049 found = false;
1050 } else if ((intercepted & READ_ONLY) != 0) {
1051 // The property is present, but read-only. Since we're trying to
1052 // overwrite it with a variable declaration we must throw a
1053 // re-declaration error. However if we found readonly property
1054 // on one of hidden prototypes, just shadow it.
1055 if (real_holder != Top::context()->global()) break;
1056 return ThrowRedeclarationError("const", name);
1057 }
1058 }
1059
1060 if (found && !assign) {
1061 // The global property is there and we're not assigning any value
1062 // to it. Just return.
1063 return Heap::undefined_value();
1064 }
1065
1066 // Assign the value (or undefined) to the property.
1067 Object* value = (assign) ? args[1] : Heap::undefined_value();
1068 return real_holder->SetProperty(&lookup, *name, value, attributes);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001069 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001070
1071 Object* proto = real_holder->GetPrototype();
1072 if (!proto->IsJSObject())
1073 break;
1074
1075 if (!JSObject::cast(proto)->map()->is_hidden_prototype())
1076 break;
1077
1078 real_holder = JSObject::cast(proto);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001079 }
1080
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001081 global = Top::context()->global();
1082 if (assign) {
1083 return global->IgnoreAttributesAndSetLocalProperty(*name,
1084 args[1],
1085 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001086 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001087 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001088}
1089
1090
1091static Object* Runtime_InitializeConstGlobal(Arguments args) {
1092 // All constants are declared with an initial value. The name
1093 // of the constant is the first argument and the initial value
1094 // is the second.
1095 RUNTIME_ASSERT(args.length() == 2);
1096 CONVERT_ARG_CHECKED(String, name, 0);
1097 Handle<Object> value = args.at<Object>(1);
1098
1099 // Get the current global object from top.
1100 GlobalObject* global = Top::context()->global();
1101
1102 // According to ECMA-262, section 12.2, page 62, the property must
1103 // not be deletable. Since it's a const, it must be READ_ONLY too.
1104 PropertyAttributes attributes =
1105 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
1106
1107 // Lookup the property locally in the global object. If it isn't
1108 // there, we add the property and take special precautions to always
1109 // add it as a local property even in case of callbacks in the
1110 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001111 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001112 LookupResult lookup;
1113 global->LocalLookup(*name, &lookup);
1114 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001115 return global->IgnoreAttributesAndSetLocalProperty(*name,
1116 *value,
1117 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001118 }
1119
1120 // Determine if this is a redeclaration of something not
1121 // read-only. In case the result is hidden behind an interceptor we
1122 // need to ask it for the property attributes.
1123 if (!lookup.IsReadOnly()) {
1124 if (lookup.type() != INTERCEPTOR) {
1125 return ThrowRedeclarationError("var", name);
1126 }
1127
1128 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
1129
1130 // Throw re-declaration error if the intercepted property is present
1131 // but not read-only.
1132 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
1133 return ThrowRedeclarationError("var", name);
1134 }
1135
1136 // Restore global object from context (in case of GC) and continue
1137 // with setting the value because the property is either absent or
1138 // read-only. We also have to do redo the lookup.
1139 global = Top::context()->global();
1140
1141 // BUG 1213579: Handle the case where we have to set a read-only
1142 // property through an interceptor and only do it if it's
1143 // uninitialized, e.g. the hole. Nirk...
1144 global->SetProperty(*name, *value, attributes);
1145 return *value;
1146 }
1147
1148 // Set the value, but only we're assigning the initial value to a
1149 // constant. For now, we determine this by checking if the
1150 // current value is the hole.
1151 PropertyType type = lookup.type();
1152 if (type == FIELD) {
1153 FixedArray* properties = global->properties();
1154 int index = lookup.GetFieldIndex();
1155 if (properties->get(index)->IsTheHole()) {
1156 properties->set(index, *value);
1157 }
1158 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001159 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
1160 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001161 }
1162 } else {
1163 // Ignore re-initialization of constants that have already been
1164 // assigned a function value.
1165 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
1166 }
1167
1168 // Use the set value as the result of the operation.
1169 return *value;
1170}
1171
1172
1173static Object* Runtime_InitializeConstContextSlot(Arguments args) {
1174 HandleScope scope;
1175 ASSERT(args.length() == 3);
1176
1177 Handle<Object> value(args[0]);
1178 ASSERT(!value->IsTheHole());
1179 CONVERT_ARG_CHECKED(Context, context, 1);
1180 Handle<String> name(String::cast(args[2]));
1181
1182 // Initializations are always done in the function context.
1183 context = Handle<Context>(context->fcontext());
1184
1185 int index;
1186 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001187 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001188 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001189 context->Lookup(name, flags, &index, &attributes);
1190
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001191 // In most situations, the property introduced by the const
1192 // declaration should be present in the context extension object.
1193 // However, because declaration and initialization are separate, the
1194 // property might have been deleted (if it was introduced by eval)
1195 // before we reach the initialization point.
1196 //
1197 // Example:
1198 //
1199 // function f() { eval("delete x; const x;"); }
1200 //
1201 // In that case, the initialization behaves like a normal assignment
1202 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001203 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001204 // Property was found in a context.
1205 if (holder->IsContext()) {
1206 // The holder cannot be the function context. If it is, there
1207 // should have been a const redeclaration error when declaring
1208 // the const property.
1209 ASSERT(!holder.is_identical_to(context));
1210 if ((attributes & READ_ONLY) == 0) {
1211 Handle<Context>::cast(holder)->set(index, *value);
1212 }
1213 } else {
1214 // The holder is an arguments object.
1215 ASSERT((attributes & READ_ONLY) == 0);
1216 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001217 }
1218 return *value;
1219 }
1220
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001221 // The property could not be found, we introduce it in the global
1222 // context.
1223 if (attributes == ABSENT) {
1224 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1225 SetProperty(global, name, value, NONE);
1226 return *value;
1227 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001228
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001229 // The property was present in a context extension object.
1230 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001231
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001232 if (*context_ext == context->extension()) {
1233 // This is the property that was introduced by the const
1234 // declaration. Set it if it hasn't been set before. NOTE: We
1235 // cannot use GetProperty() to get the current value as it
1236 // 'unholes' the value.
1237 LookupResult lookup;
1238 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1239 ASSERT(lookup.IsProperty()); // the property was declared
1240 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1241
1242 PropertyType type = lookup.type();
1243 if (type == FIELD) {
1244 FixedArray* properties = context_ext->properties();
1245 int index = lookup.GetFieldIndex();
1246 if (properties->get(index)->IsTheHole()) {
1247 properties->set(index, *value);
1248 }
1249 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001250 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
1251 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001252 }
1253 } else {
1254 // We should not reach here. Any real, named property should be
1255 // either a field or a dictionary slot.
1256 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001257 }
1258 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001259 // The property was found in a different context extension object.
1260 // Set it if it is not a read-only property.
1261 if ((attributes & READ_ONLY) == 0) {
1262 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1263 // Setting a property might throw an exception. Exceptions
1264 // are converted to empty handles in handle operations. We
1265 // need to convert back to exceptions here.
1266 if (set.is_null()) {
1267 ASSERT(Top::has_pending_exception());
1268 return Failure::Exception();
1269 }
1270 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001271 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001272
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001273 return *value;
1274}
1275
1276
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001277static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1278 Arguments args) {
1279 HandleScope scope;
1280 ASSERT(args.length() == 2);
1281 CONVERT_ARG_CHECKED(JSObject, object, 0);
1282 CONVERT_SMI_CHECKED(properties, args[1]);
1283 if (object->HasFastProperties()) {
1284 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1285 }
1286 return *object;
1287}
1288
1289
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001290static Object* Runtime_RegExpExec(Arguments args) {
1291 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001292 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001293 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1294 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001295 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001296 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001297 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001298 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001299 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001300 RUNTIME_ASSERT(index >= 0);
1301 RUNTIME_ASSERT(index <= subject->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001302 Counters::regexp_entry_runtime.Increment();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001303 Handle<Object> result = RegExpImpl::Exec(regexp,
1304 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001305 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001306 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001307 if (result.is_null()) return Failure::Exception();
1308 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001309}
1310
1311
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00001312static Object* Runtime_RegExpConstructResult(Arguments args) {
1313 ASSERT(args.length() == 3);
1314 CONVERT_SMI_CHECKED(elements_count, args[0]);
1315 if (elements_count > JSArray::kMaxFastElementsLength) {
1316 return Top::ThrowIllegalOperation();
1317 }
1318 Object* new_object = Heap::AllocateFixedArrayWithHoles(elements_count);
1319 if (new_object->IsFailure()) return new_object;
1320 FixedArray* elements = FixedArray::cast(new_object);
1321 new_object = Heap::AllocateRaw(JSRegExpResult::kSize,
1322 NEW_SPACE,
1323 OLD_POINTER_SPACE);
1324 if (new_object->IsFailure()) return new_object;
1325 {
1326 AssertNoAllocation no_gc;
1327 HandleScope scope;
1328 reinterpret_cast<HeapObject*>(new_object)->
1329 set_map(Top::global_context()->regexp_result_map());
1330 }
1331 JSArray* array = JSArray::cast(new_object);
1332 array->set_properties(Heap::empty_fixed_array());
1333 array->set_elements(elements);
1334 array->set_length(Smi::FromInt(elements_count));
1335 // Write in-object properties after the length of the array.
1336 array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, args[1]);
1337 array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, args[2]);
1338 return array;
1339}
1340
1341
lrn@chromium.org25156de2010-04-06 13:10:27 +00001342static Object* Runtime_RegExpInitializeObject(Arguments args) {
1343 AssertNoAllocation no_alloc;
1344 ASSERT(args.length() == 5);
1345 CONVERT_CHECKED(JSRegExp, regexp, args[0]);
1346 CONVERT_CHECKED(String, source, args[1]);
1347
1348 Object* global = args[2];
1349 if (!global->IsTrue()) global = Heap::false_value();
1350
1351 Object* ignoreCase = args[3];
1352 if (!ignoreCase->IsTrue()) ignoreCase = Heap::false_value();
1353
1354 Object* multiline = args[4];
1355 if (!multiline->IsTrue()) multiline = Heap::false_value();
1356
1357 Map* map = regexp->map();
1358 Object* constructor = map->constructor();
1359 if (constructor->IsJSFunction() &&
1360 JSFunction::cast(constructor)->initial_map() == map) {
1361 // If we still have the original map, set in-object properties directly.
1362 regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, source);
1363 // TODO(lrn): Consider skipping write barrier on booleans as well.
1364 // Both true and false should be in oldspace at all times.
1365 regexp->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, global);
1366 regexp->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex, ignoreCase);
1367 regexp->InObjectPropertyAtPut(JSRegExp::kMultilineFieldIndex, multiline);
1368 regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex,
1369 Smi::FromInt(0),
1370 SKIP_WRITE_BARRIER);
1371 return regexp;
1372 }
1373
1374 // Map has changed, so use generic, but slower, method.
1375 PropertyAttributes final =
1376 static_cast<PropertyAttributes>(READ_ONLY | DONT_ENUM | DONT_DELETE);
1377 PropertyAttributes writable =
1378 static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
1379 regexp->IgnoreAttributesAndSetLocalProperty(Heap::source_symbol(),
1380 source,
1381 final);
1382 regexp->IgnoreAttributesAndSetLocalProperty(Heap::global_symbol(),
1383 global,
1384 final);
1385 regexp->IgnoreAttributesAndSetLocalProperty(Heap::ignore_case_symbol(),
1386 ignoreCase,
1387 final);
1388 regexp->IgnoreAttributesAndSetLocalProperty(Heap::multiline_symbol(),
1389 multiline,
1390 final);
1391 regexp->IgnoreAttributesAndSetLocalProperty(Heap::last_index_symbol(),
1392 Smi::FromInt(0),
1393 writable);
1394 return regexp;
1395}
1396
1397
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001398static Object* Runtime_FinishArrayPrototypeSetup(Arguments args) {
1399 HandleScope scope;
1400 ASSERT(args.length() == 1);
1401 CONVERT_ARG_CHECKED(JSArray, prototype, 0);
1402 // This is necessary to enable fast checks for absence of elements
1403 // on Array.prototype and below.
1404 prototype->set_elements(Heap::empty_fixed_array());
1405 return Smi::FromInt(0);
1406}
1407
1408
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001409static Handle<JSFunction> InstallBuiltin(Handle<JSObject> holder,
1410 const char* name,
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001411 Builtins::Name builtin_name) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001412 Handle<String> key = Factory::LookupAsciiSymbol(name);
1413 Handle<Code> code(Builtins::builtin(builtin_name));
1414 Handle<JSFunction> optimized = Factory::NewFunction(key,
1415 JS_OBJECT_TYPE,
1416 JSObject::kHeaderSize,
1417 code,
1418 false);
1419 optimized->shared()->DontAdaptArguments();
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001420 SetProperty(holder, key, optimized, NONE);
1421 return optimized;
1422}
1423
1424
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001425static Object* Runtime_SpecialArrayFunctions(Arguments args) {
1426 HandleScope scope;
1427 ASSERT(args.length() == 1);
1428 CONVERT_ARG_CHECKED(JSObject, holder, 0);
1429
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001430 InstallBuiltin(holder, "pop", Builtins::ArrayPop);
1431 InstallBuiltin(holder, "push", Builtins::ArrayPush);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001432 InstallBuiltin(holder, "shift", Builtins::ArrayShift);
1433 InstallBuiltin(holder, "unshift", Builtins::ArrayUnshift);
1434 InstallBuiltin(holder, "slice", Builtins::ArraySlice);
1435 InstallBuiltin(holder, "splice", Builtins::ArraySplice);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001436 InstallBuiltin(holder, "concat", Builtins::ArrayConcat);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001437
1438 return *holder;
1439}
1440
1441
ager@chromium.org357bf652010-04-12 11:30:10 +00001442static Object* Runtime_GetGlobalReceiver(Arguments args) {
1443 // Returns a real global receiver, not one of builtins object.
1444 Context* global_context = Top::context()->global()->global_context();
1445 return global_context->global()->global_receiver();
1446}
1447
1448
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001449static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1450 HandleScope scope;
1451 ASSERT(args.length() == 4);
1452 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1453 int index = Smi::cast(args[1])->value();
1454 Handle<String> pattern = args.at<String>(2);
1455 Handle<String> flags = args.at<String>(3);
1456
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001457 // Get the RegExp function from the context in the literals array.
1458 // This is the RegExp function from the context in which the
1459 // function was created. We do not use the RegExp function from the
1460 // current global context because this might be the RegExp function
1461 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001462 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001463 Handle<JSFunction>(
1464 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001465 // Compute the regular expression literal.
1466 bool has_pending_exception;
1467 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001468 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1469 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001470 if (has_pending_exception) {
1471 ASSERT(Top::has_pending_exception());
1472 return Failure::Exception();
1473 }
1474 literals->set(index, *regexp);
1475 return *regexp;
1476}
1477
1478
1479static Object* Runtime_FunctionGetName(Arguments args) {
1480 NoHandleAllocation ha;
1481 ASSERT(args.length() == 1);
1482
1483 CONVERT_CHECKED(JSFunction, f, args[0]);
1484 return f->shared()->name();
1485}
1486
1487
ager@chromium.org236ad962008-09-25 09:45:57 +00001488static Object* Runtime_FunctionSetName(Arguments args) {
1489 NoHandleAllocation ha;
1490 ASSERT(args.length() == 2);
1491
1492 CONVERT_CHECKED(JSFunction, f, args[0]);
1493 CONVERT_CHECKED(String, name, args[1]);
1494 f->shared()->set_name(name);
1495 return Heap::undefined_value();
1496}
1497
1498
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001499static Object* Runtime_FunctionRemovePrototype(Arguments args) {
1500 NoHandleAllocation ha;
1501 ASSERT(args.length() == 1);
1502
1503 CONVERT_CHECKED(JSFunction, f, args[0]);
1504 Object* obj = f->RemovePrototype();
1505 if (obj->IsFailure()) return obj;
1506
1507 return Heap::undefined_value();
1508}
1509
1510
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001511static Object* Runtime_FunctionGetScript(Arguments args) {
1512 HandleScope scope;
1513 ASSERT(args.length() == 1);
1514
1515 CONVERT_CHECKED(JSFunction, fun, args[0]);
1516 Handle<Object> script = Handle<Object>(fun->shared()->script());
1517 if (!script->IsScript()) return Heap::undefined_value();
1518
1519 return *GetScriptWrapper(Handle<Script>::cast(script));
1520}
1521
1522
1523static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1524 NoHandleAllocation ha;
1525 ASSERT(args.length() == 1);
1526
1527 CONVERT_CHECKED(JSFunction, f, args[0]);
1528 return f->shared()->GetSourceCode();
1529}
1530
1531
1532static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1533 NoHandleAllocation ha;
1534 ASSERT(args.length() == 1);
1535
1536 CONVERT_CHECKED(JSFunction, fun, args[0]);
1537 int pos = fun->shared()->start_position();
1538 return Smi::FromInt(pos);
1539}
1540
1541
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001542static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1543 ASSERT(args.length() == 2);
1544
1545 CONVERT_CHECKED(JSFunction, fun, args[0]);
1546 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1547
1548 Code* code = fun->code();
1549 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1550
1551 Address pc = code->address() + offset;
1552 return Smi::FromInt(fun->code()->SourcePosition(pc));
1553}
1554
1555
1556
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001557static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1558 NoHandleAllocation ha;
1559 ASSERT(args.length() == 2);
1560
1561 CONVERT_CHECKED(JSFunction, fun, args[0]);
1562 CONVERT_CHECKED(String, name, args[1]);
1563 fun->SetInstanceClassName(name);
1564 return Heap::undefined_value();
1565}
1566
1567
1568static Object* Runtime_FunctionSetLength(Arguments args) {
1569 NoHandleAllocation ha;
1570 ASSERT(args.length() == 2);
1571
1572 CONVERT_CHECKED(JSFunction, fun, args[0]);
1573 CONVERT_CHECKED(Smi, length, args[1]);
1574 fun->shared()->set_length(length->value());
1575 return length;
1576}
1577
1578
1579static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001580 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001581 ASSERT(args.length() == 2);
1582
1583 CONVERT_CHECKED(JSFunction, fun, args[0]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001584 ASSERT(fun->should_have_prototype());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001585 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1586 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001587 return args[0]; // return TOS
1588}
1589
1590
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001591static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1592 NoHandleAllocation ha;
1593 ASSERT(args.length() == 1);
1594
1595 CONVERT_CHECKED(JSFunction, f, args[0]);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001596 return f->shared()->IsApiFunction() ? Heap::true_value()
1597 : Heap::false_value();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001598}
1599
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001600static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1601 NoHandleAllocation ha;
1602 ASSERT(args.length() == 1);
1603
1604 CONVERT_CHECKED(JSFunction, f, args[0]);
1605 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1606}
1607
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001608
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001609static Object* Runtime_SetCode(Arguments args) {
1610 HandleScope scope;
1611 ASSERT(args.length() == 2);
1612
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001613 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001614 Handle<Object> code = args.at<Object>(1);
1615
1616 Handle<Context> context(target->context());
1617
1618 if (!code->IsNull()) {
1619 RUNTIME_ASSERT(code->IsJSFunction());
1620 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001621 Handle<SharedFunctionInfo> shared(fun->shared());
1622 SetExpectedNofProperties(target, shared->expected_nof_properties());
1623
1624 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001625 return Failure::Exception();
1626 }
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00001627 // Set the code, scope info, formal parameter count,
1628 // and the length of the target function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001629 target->set_code(fun->code());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00001630 target->shared()->set_scope_info(shared->scope_info());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001631 target->shared()->set_length(shared->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001632 target->shared()->set_formal_parameter_count(
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001633 shared->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001634 // Set the source code of the target function to undefined.
1635 // SetCode is only used for built-in constructors like String,
1636 // Array, and Object, and some web code
1637 // doesn't like seeing source code for constructors.
1638 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001639 // Clear the optimization hints related to the compiled code as these are no
1640 // longer valid when the code is overwritten.
1641 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001642 context = Handle<Context>(fun->context());
1643
1644 // Make sure we get a fresh copy of the literal vector to avoid
1645 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001646 int number_of_literals = fun->NumberOfLiterals();
1647 Handle<FixedArray> literals =
1648 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001649 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001650 // Insert the object, regexp and array functions in the literals
1651 // array prefix. These are the functions that will be used when
1652 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001653 literals->set(JSFunction::kLiteralGlobalContextIndex,
1654 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001655 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001656 // It's okay to skip the write barrier here because the literals
1657 // are guaranteed to be in old space.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001658 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001659 }
1660
1661 target->set_context(*context);
1662 return *target;
1663}
1664
1665
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001666static Object* CharFromCode(Object* char_code) {
1667 uint32_t code;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001668 if (char_code->ToArrayIndex(&code)) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001669 if (code <= 0xffff) {
1670 return Heap::LookupSingleCharacterStringFromCode(code);
1671 }
1672 }
1673 return Heap::empty_string();
1674}
1675
1676
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001677static Object* Runtime_StringCharCodeAt(Arguments args) {
1678 NoHandleAllocation ha;
1679 ASSERT(args.length() == 2);
1680
1681 CONVERT_CHECKED(String, subject, args[0]);
1682 Object* index = args[1];
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001683 RUNTIME_ASSERT(index->IsNumber());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001684
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001685 uint32_t i = 0;
1686 if (index->IsSmi()) {
1687 int value = Smi::cast(index)->value();
1688 if (value < 0) return Heap::nan_value();
1689 i = value;
1690 } else {
1691 ASSERT(index->IsHeapNumber());
1692 double value = HeapNumber::cast(index)->value();
1693 i = static_cast<uint32_t>(DoubleToInteger(value));
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001694 }
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001695
1696 // Flatten the string. If someone wants to get a char at an index
1697 // in a cons string, it is likely that more indices will be
1698 // accessed.
1699 Object* flat = subject->TryFlatten();
1700 if (flat->IsFailure()) return flat;
1701 subject = String::cast(flat);
1702
1703 if (i >= static_cast<uint32_t>(subject->length())) {
1704 return Heap::nan_value();
1705 }
1706
1707 return Smi::FromInt(subject->Get(i));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001708}
1709
1710
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001711static Object* Runtime_CharFromCode(Arguments args) {
1712 NoHandleAllocation ha;
1713 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001714 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001715}
1716
lrn@chromium.org25156de2010-04-06 13:10:27 +00001717
1718class FixedArrayBuilder {
1719 public:
1720 explicit FixedArrayBuilder(int initial_capacity)
1721 : array_(Factory::NewFixedArrayWithHoles(initial_capacity)),
1722 length_(0) {
1723 // Require a non-zero initial size. Ensures that doubling the size to
1724 // extend the array will work.
1725 ASSERT(initial_capacity > 0);
1726 }
1727
1728 explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
1729 : array_(backing_store),
1730 length_(0) {
1731 // Require a non-zero initial size. Ensures that doubling the size to
1732 // extend the array will work.
1733 ASSERT(backing_store->length() > 0);
1734 }
1735
1736 bool HasCapacity(int elements) {
1737 int length = array_->length();
1738 int required_length = length_ + elements;
1739 return (length >= required_length);
1740 }
1741
1742 void EnsureCapacity(int elements) {
1743 int length = array_->length();
1744 int required_length = length_ + elements;
1745 if (length < required_length) {
1746 int new_length = length;
1747 do {
1748 new_length *= 2;
1749 } while (new_length < required_length);
1750 Handle<FixedArray> extended_array =
1751 Factory::NewFixedArrayWithHoles(new_length);
1752 array_->CopyTo(0, *extended_array, 0, length_);
1753 array_ = extended_array;
1754 }
1755 }
1756
1757 void Add(Object* value) {
1758 ASSERT(length_ < capacity());
1759 array_->set(length_, value);
1760 length_++;
1761 }
1762
1763 void Add(Smi* value) {
1764 ASSERT(length_ < capacity());
1765 array_->set(length_, value);
1766 length_++;
1767 }
1768
1769 Handle<FixedArray> array() {
1770 return array_;
1771 }
1772
1773 int length() {
1774 return length_;
1775 }
1776
1777 int capacity() {
1778 return array_->length();
1779 }
1780
1781 Handle<JSArray> ToJSArray() {
1782 Handle<JSArray> result_array = Factory::NewJSArrayWithElements(array_);
1783 result_array->set_length(Smi::FromInt(length_));
1784 return result_array;
1785 }
1786
1787 Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
1788 target_array->set_elements(*array_);
1789 target_array->set_length(Smi::FromInt(length_));
1790 return target_array;
1791 }
1792
1793 private:
1794 Handle<FixedArray> array_;
1795 int length_;
1796};
1797
1798
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001799// Forward declarations.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001800const int kStringBuilderConcatHelperLengthBits = 11;
1801const int kStringBuilderConcatHelperPositionBits = 19;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001802
1803template <typename schar>
1804static inline void StringBuilderConcatHelper(String*,
1805 schar*,
1806 FixedArray*,
1807 int);
1808
lrn@chromium.org25156de2010-04-06 13:10:27 +00001809typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
1810 StringBuilderSubstringLength;
1811typedef BitField<int,
1812 kStringBuilderConcatHelperLengthBits,
1813 kStringBuilderConcatHelperPositionBits>
1814 StringBuilderSubstringPosition;
1815
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001816
1817class ReplacementStringBuilder {
1818 public:
1819 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
lrn@chromium.org25156de2010-04-06 13:10:27 +00001820 : array_builder_(estimated_part_count),
1821 subject_(subject),
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001822 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001823 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001824 // Require a non-zero initial size. Ensures that doubling the size to
1825 // extend the array will work.
1826 ASSERT(estimated_part_count > 0);
1827 }
1828
lrn@chromium.org25156de2010-04-06 13:10:27 +00001829 static inline void AddSubjectSlice(FixedArrayBuilder* builder,
1830 int from,
1831 int to) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001832 ASSERT(from >= 0);
1833 int length = to - from;
1834 ASSERT(length > 0);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001835 if (StringBuilderSubstringLength::is_valid(length) &&
1836 StringBuilderSubstringPosition::is_valid(from)) {
1837 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1838 StringBuilderSubstringPosition::encode(from);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001839 builder->Add(Smi::FromInt(encoded_slice));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001840 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001841 // Otherwise encode as two smis.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001842 builder->Add(Smi::FromInt(-length));
1843 builder->Add(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001844 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00001845 }
1846
1847
1848 void EnsureCapacity(int elements) {
1849 array_builder_.EnsureCapacity(elements);
1850 }
1851
1852
1853 void AddSubjectSlice(int from, int to) {
1854 AddSubjectSlice(&array_builder_, from, to);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001855 IncrementCharacterCount(to - from);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001856 }
1857
1858
1859 void AddString(Handle<String> string) {
1860 int length = string->length();
1861 ASSERT(length > 0);
1862 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001863 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001864 is_ascii_ = false;
1865 }
1866 IncrementCharacterCount(length);
1867 }
1868
1869
1870 Handle<String> ToString() {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001871 if (array_builder_.length() == 0) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001872 return Factory::empty_string();
1873 }
1874
1875 Handle<String> joined_string;
1876 if (is_ascii_) {
1877 joined_string = NewRawAsciiString(character_count_);
1878 AssertNoAllocation no_alloc;
1879 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1880 char* char_buffer = seq->GetChars();
1881 StringBuilderConcatHelper(*subject_,
1882 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001883 *array_builder_.array(),
1884 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001885 } else {
1886 // Non-ASCII.
1887 joined_string = NewRawTwoByteString(character_count_);
1888 AssertNoAllocation no_alloc;
1889 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1890 uc16* char_buffer = seq->GetChars();
1891 StringBuilderConcatHelper(*subject_,
1892 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001893 *array_builder_.array(),
1894 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001895 }
1896 return joined_string;
1897 }
1898
1899
1900 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001901 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001902 V8::FatalProcessOutOfMemory("String.replace result too large.");
1903 }
1904 character_count_ += by;
1905 }
1906
lrn@chromium.org25156de2010-04-06 13:10:27 +00001907 Handle<JSArray> GetParts() {
1908 Handle<JSArray> result =
1909 Factory::NewJSArrayWithElements(array_builder_.array());
1910 result->set_length(Smi::FromInt(array_builder_.length()));
1911 return result;
1912 }
kmillikin@chromium.orgd9825192010-03-30 08:36:16 +00001913
lrn@chromium.org25156de2010-04-06 13:10:27 +00001914 private:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001915 Handle<String> NewRawAsciiString(int size) {
1916 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1917 }
1918
1919
1920 Handle<String> NewRawTwoByteString(int size) {
1921 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1922 }
1923
1924
1925 void AddElement(Object* element) {
1926 ASSERT(element->IsSmi() || element->IsString());
lrn@chromium.org25156de2010-04-06 13:10:27 +00001927 ASSERT(array_builder_.capacity() > array_builder_.length());
1928 array_builder_.Add(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001929 }
1930
lrn@chromium.org25156de2010-04-06 13:10:27 +00001931 FixedArrayBuilder array_builder_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001932 Handle<String> subject_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001933 int character_count_;
1934 bool is_ascii_;
1935};
1936
1937
1938class CompiledReplacement {
1939 public:
1940 CompiledReplacement()
1941 : parts_(1), replacement_substrings_(0) {}
1942
1943 void Compile(Handle<String> replacement,
1944 int capture_count,
1945 int subject_length);
1946
1947 void Apply(ReplacementStringBuilder* builder,
1948 int match_from,
1949 int match_to,
1950 Handle<JSArray> last_match_info);
1951
1952 // Number of distinct parts of the replacement pattern.
1953 int parts() {
1954 return parts_.length();
1955 }
1956 private:
1957 enum PartType {
1958 SUBJECT_PREFIX = 1,
1959 SUBJECT_SUFFIX,
1960 SUBJECT_CAPTURE,
1961 REPLACEMENT_SUBSTRING,
1962 REPLACEMENT_STRING,
1963
1964 NUMBER_OF_PART_TYPES
1965 };
1966
1967 struct ReplacementPart {
1968 static inline ReplacementPart SubjectMatch() {
1969 return ReplacementPart(SUBJECT_CAPTURE, 0);
1970 }
1971 static inline ReplacementPart SubjectCapture(int capture_index) {
1972 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1973 }
1974 static inline ReplacementPart SubjectPrefix() {
1975 return ReplacementPart(SUBJECT_PREFIX, 0);
1976 }
1977 static inline ReplacementPart SubjectSuffix(int subject_length) {
1978 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1979 }
1980 static inline ReplacementPart ReplacementString() {
1981 return ReplacementPart(REPLACEMENT_STRING, 0);
1982 }
1983 static inline ReplacementPart ReplacementSubString(int from, int to) {
1984 ASSERT(from >= 0);
1985 ASSERT(to > from);
1986 return ReplacementPart(-from, to);
1987 }
1988
1989 // If tag <= 0 then it is the negation of a start index of a substring of
1990 // the replacement pattern, otherwise it's a value from PartType.
1991 ReplacementPart(int tag, int data)
1992 : tag(tag), data(data) {
1993 // Must be non-positive or a PartType value.
1994 ASSERT(tag < NUMBER_OF_PART_TYPES);
1995 }
1996 // Either a value of PartType or a non-positive number that is
1997 // the negation of an index into the replacement string.
1998 int tag;
1999 // The data value's interpretation depends on the value of tag:
2000 // tag == SUBJECT_PREFIX ||
2001 // tag == SUBJECT_SUFFIX: data is unused.
2002 // tag == SUBJECT_CAPTURE: data is the number of the capture.
2003 // tag == REPLACEMENT_SUBSTRING ||
2004 // tag == REPLACEMENT_STRING: data is index into array of substrings
2005 // of the replacement string.
2006 // tag <= 0: Temporary representation of the substring of the replacement
2007 // string ranging over -tag .. data.
2008 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
2009 // substring objects.
2010 int data;
2011 };
2012
2013 template<typename Char>
2014 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
2015 Vector<Char> characters,
2016 int capture_count,
2017 int subject_length) {
2018 int length = characters.length();
2019 int last = 0;
2020 for (int i = 0; i < length; i++) {
2021 Char c = characters[i];
2022 if (c == '$') {
2023 int next_index = i + 1;
2024 if (next_index == length) { // No next character!
2025 break;
2026 }
2027 Char c2 = characters[next_index];
2028 switch (c2) {
2029 case '$':
2030 if (i > last) {
2031 // There is a substring before. Include the first "$".
2032 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
2033 last = next_index + 1; // Continue after the second "$".
2034 } else {
2035 // Let the next substring start with the second "$".
2036 last = next_index;
2037 }
2038 i = next_index;
2039 break;
2040 case '`':
2041 if (i > last) {
2042 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2043 }
2044 parts->Add(ReplacementPart::SubjectPrefix());
2045 i = next_index;
2046 last = i + 1;
2047 break;
2048 case '\'':
2049 if (i > last) {
2050 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2051 }
2052 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
2053 i = next_index;
2054 last = i + 1;
2055 break;
2056 case '&':
2057 if (i > last) {
2058 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2059 }
2060 parts->Add(ReplacementPart::SubjectMatch());
2061 i = next_index;
2062 last = i + 1;
2063 break;
2064 case '0':
2065 case '1':
2066 case '2':
2067 case '3':
2068 case '4':
2069 case '5':
2070 case '6':
2071 case '7':
2072 case '8':
2073 case '9': {
2074 int capture_ref = c2 - '0';
2075 if (capture_ref > capture_count) {
2076 i = next_index;
2077 continue;
2078 }
2079 int second_digit_index = next_index + 1;
2080 if (second_digit_index < length) {
2081 // Peek ahead to see if we have two digits.
2082 Char c3 = characters[second_digit_index];
2083 if ('0' <= c3 && c3 <= '9') { // Double digits.
2084 int double_digit_ref = capture_ref * 10 + c3 - '0';
2085 if (double_digit_ref <= capture_count) {
2086 next_index = second_digit_index;
2087 capture_ref = double_digit_ref;
2088 }
2089 }
2090 }
2091 if (capture_ref > 0) {
2092 if (i > last) {
2093 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2094 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002095 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002096 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
2097 last = next_index + 1;
2098 }
2099 i = next_index;
2100 break;
2101 }
2102 default:
2103 i = next_index;
2104 break;
2105 }
2106 }
2107 }
2108 if (length > last) {
2109 if (last == 0) {
2110 parts->Add(ReplacementPart::ReplacementString());
2111 } else {
2112 parts->Add(ReplacementPart::ReplacementSubString(last, length));
2113 }
2114 }
2115 }
2116
2117 ZoneList<ReplacementPart> parts_;
2118 ZoneList<Handle<String> > replacement_substrings_;
2119};
2120
2121
2122void CompiledReplacement::Compile(Handle<String> replacement,
2123 int capture_count,
2124 int subject_length) {
2125 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00002126 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002127 AssertNoAllocation no_alloc;
2128 ParseReplacementPattern(&parts_,
2129 replacement->ToAsciiVector(),
2130 capture_count,
2131 subject_length);
2132 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00002133 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002134 AssertNoAllocation no_alloc;
2135
2136 ParseReplacementPattern(&parts_,
2137 replacement->ToUC16Vector(),
2138 capture_count,
2139 subject_length);
2140 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002141 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002142 int substring_index = 0;
2143 for (int i = 0, n = parts_.length(); i < n; i++) {
2144 int tag = parts_[i].tag;
2145 if (tag <= 0) { // A replacement string slice.
2146 int from = -tag;
2147 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002148 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002149 parts_[i].tag = REPLACEMENT_SUBSTRING;
2150 parts_[i].data = substring_index;
2151 substring_index++;
2152 } else if (tag == REPLACEMENT_STRING) {
2153 replacement_substrings_.Add(replacement);
2154 parts_[i].data = substring_index;
2155 substring_index++;
2156 }
2157 }
2158}
2159
2160
2161void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
2162 int match_from,
2163 int match_to,
2164 Handle<JSArray> last_match_info) {
2165 for (int i = 0, n = parts_.length(); i < n; i++) {
2166 ReplacementPart part = parts_[i];
2167 switch (part.tag) {
2168 case SUBJECT_PREFIX:
2169 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
2170 break;
2171 case SUBJECT_SUFFIX: {
2172 int subject_length = part.data;
2173 if (match_to < subject_length) {
2174 builder->AddSubjectSlice(match_to, subject_length);
2175 }
2176 break;
2177 }
2178 case SUBJECT_CAPTURE: {
2179 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002180 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002181 int from = RegExpImpl::GetCapture(match_info, capture * 2);
2182 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
2183 if (from >= 0 && to > from) {
2184 builder->AddSubjectSlice(from, to);
2185 }
2186 break;
2187 }
2188 case REPLACEMENT_SUBSTRING:
2189 case REPLACEMENT_STRING:
2190 builder->AddString(replacement_substrings_[part.data]);
2191 break;
2192 default:
2193 UNREACHABLE();
2194 }
2195 }
2196}
2197
2198
2199
2200static Object* StringReplaceRegExpWithString(String* subject,
2201 JSRegExp* regexp,
2202 String* replacement,
2203 JSArray* last_match_info) {
2204 ASSERT(subject->IsFlat());
2205 ASSERT(replacement->IsFlat());
2206
2207 HandleScope handles;
2208
2209 int length = subject->length();
2210 Handle<String> subject_handle(subject);
2211 Handle<JSRegExp> regexp_handle(regexp);
2212 Handle<String> replacement_handle(replacement);
2213 Handle<JSArray> last_match_info_handle(last_match_info);
2214 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2215 subject_handle,
2216 0,
2217 last_match_info_handle);
2218 if (match.is_null()) {
2219 return Failure::Exception();
2220 }
2221 if (match->IsNull()) {
2222 return *subject_handle;
2223 }
2224
2225 int capture_count = regexp_handle->CaptureCount();
2226
2227 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002228 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002229 CompiledReplacement compiled_replacement;
2230 compiled_replacement.Compile(replacement_handle,
2231 capture_count,
2232 length);
2233
2234 bool is_global = regexp_handle->GetFlags().is_global();
2235
2236 // Guessing the number of parts that the final result string is built
2237 // from. Global regexps can match any number of times, so we guess
2238 // conservatively.
2239 int expected_parts =
2240 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
2241 ReplacementStringBuilder builder(subject_handle, expected_parts);
2242
2243 // Index of end of last match.
2244 int prev = 0;
2245
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002246 // Number of parts added by compiled replacement plus preceeding
2247 // string and possibly suffix after last match. It is possible for
2248 // all components to use two elements when encoded as two smis.
2249 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002250 bool matched = true;
2251 do {
2252 ASSERT(last_match_info_handle->HasFastElements());
2253 // Increase the capacity of the builder before entering local handle-scope,
2254 // so its internal buffer can safely allocate a new handle if it grows.
2255 builder.EnsureCapacity(parts_added_per_loop);
2256
2257 HandleScope loop_scope;
2258 int start, end;
2259 {
2260 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002261 FixedArray* match_info_array =
2262 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002263
2264 ASSERT_EQ(capture_count * 2 + 2,
2265 RegExpImpl::GetLastCaptureCount(match_info_array));
2266 start = RegExpImpl::GetCapture(match_info_array, 0);
2267 end = RegExpImpl::GetCapture(match_info_array, 1);
2268 }
2269
2270 if (prev < start) {
2271 builder.AddSubjectSlice(prev, start);
2272 }
2273 compiled_replacement.Apply(&builder,
2274 start,
2275 end,
2276 last_match_info_handle);
2277 prev = end;
2278
2279 // Only continue checking for global regexps.
2280 if (!is_global) break;
2281
2282 // Continue from where the match ended, unless it was an empty match.
2283 int next = end;
2284 if (start == end) {
2285 next = end + 1;
2286 if (next > length) break;
2287 }
2288
2289 match = RegExpImpl::Exec(regexp_handle,
2290 subject_handle,
2291 next,
2292 last_match_info_handle);
2293 if (match.is_null()) {
2294 return Failure::Exception();
2295 }
2296 matched = !match->IsNull();
2297 } while (matched);
2298
2299 if (prev < length) {
2300 builder.AddSubjectSlice(prev, length);
2301 }
2302
2303 return *(builder.ToString());
2304}
2305
2306
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00002307template <typename ResultSeqString>
2308static Object* StringReplaceRegExpWithEmptyString(String* subject,
2309 JSRegExp* regexp,
2310 JSArray* last_match_info) {
2311 ASSERT(subject->IsFlat());
2312
2313 HandleScope handles;
2314
2315 Handle<String> subject_handle(subject);
2316 Handle<JSRegExp> regexp_handle(regexp);
2317 Handle<JSArray> last_match_info_handle(last_match_info);
2318 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2319 subject_handle,
2320 0,
2321 last_match_info_handle);
2322 if (match.is_null()) return Failure::Exception();
2323 if (match->IsNull()) return *subject_handle;
2324
2325 ASSERT(last_match_info_handle->HasFastElements());
2326
2327 HandleScope loop_scope;
2328 int start, end;
2329 {
2330 AssertNoAllocation match_info_array_is_not_in_a_handle;
2331 FixedArray* match_info_array =
2332 FixedArray::cast(last_match_info_handle->elements());
2333
2334 start = RegExpImpl::GetCapture(match_info_array, 0);
2335 end = RegExpImpl::GetCapture(match_info_array, 1);
2336 }
2337
2338 int length = subject->length();
2339 int new_length = length - (end - start);
2340 if (new_length == 0) {
2341 return Heap::empty_string();
2342 }
2343 Handle<ResultSeqString> answer;
2344 if (ResultSeqString::kHasAsciiEncoding) {
2345 answer =
2346 Handle<ResultSeqString>::cast(Factory::NewRawAsciiString(new_length));
2347 } else {
2348 answer =
2349 Handle<ResultSeqString>::cast(Factory::NewRawTwoByteString(new_length));
2350 }
2351
2352 // If the regexp isn't global, only match once.
2353 if (!regexp_handle->GetFlags().is_global()) {
2354 if (start > 0) {
2355 String::WriteToFlat(*subject_handle,
2356 answer->GetChars(),
2357 0,
2358 start);
2359 }
2360 if (end < length) {
2361 String::WriteToFlat(*subject_handle,
2362 answer->GetChars() + start,
2363 end,
2364 length);
2365 }
2366 return *answer;
2367 }
2368
2369 int prev = 0; // Index of end of last match.
2370 int next = 0; // Start of next search (prev unless last match was empty).
2371 int position = 0;
2372
2373 do {
2374 if (prev < start) {
2375 // Add substring subject[prev;start] to answer string.
2376 String::WriteToFlat(*subject_handle,
2377 answer->GetChars() + position,
2378 prev,
2379 start);
2380 position += start - prev;
2381 }
2382 prev = end;
2383 next = end;
2384 // Continue from where the match ended, unless it was an empty match.
2385 if (start == end) {
2386 next++;
2387 if (next > length) break;
2388 }
2389 match = RegExpImpl::Exec(regexp_handle,
2390 subject_handle,
2391 next,
2392 last_match_info_handle);
2393 if (match.is_null()) return Failure::Exception();
2394 if (match->IsNull()) break;
2395
2396 ASSERT(last_match_info_handle->HasFastElements());
2397 HandleScope loop_scope;
2398 {
2399 AssertNoAllocation match_info_array_is_not_in_a_handle;
2400 FixedArray* match_info_array =
2401 FixedArray::cast(last_match_info_handle->elements());
2402 start = RegExpImpl::GetCapture(match_info_array, 0);
2403 end = RegExpImpl::GetCapture(match_info_array, 1);
2404 }
2405 } while (true);
2406
2407 if (prev < length) {
2408 // Add substring subject[prev;length] to answer string.
2409 String::WriteToFlat(*subject_handle,
2410 answer->GetChars() + position,
2411 prev,
2412 length);
2413 position += length - prev;
2414 }
2415
2416 if (position == 0) {
2417 return Heap::empty_string();
2418 }
2419
2420 // Shorten string and fill
2421 int string_size = ResultSeqString::SizeFor(position);
2422 int allocated_string_size = ResultSeqString::SizeFor(new_length);
2423 int delta = allocated_string_size - string_size;
2424
2425 answer->set_length(position);
2426 if (delta == 0) return *answer;
2427
2428 Address end_of_string = answer->address() + string_size;
2429 Heap::CreateFillerObjectAt(end_of_string, delta);
2430
2431 return *answer;
2432}
2433
2434
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002435static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2436 ASSERT(args.length() == 4);
2437
2438 CONVERT_CHECKED(String, subject, args[0]);
2439 if (!subject->IsFlat()) {
2440 Object* flat_subject = subject->TryFlatten();
2441 if (flat_subject->IsFailure()) {
2442 return flat_subject;
2443 }
2444 subject = String::cast(flat_subject);
2445 }
2446
2447 CONVERT_CHECKED(String, replacement, args[2]);
2448 if (!replacement->IsFlat()) {
2449 Object* flat_replacement = replacement->TryFlatten();
2450 if (flat_replacement->IsFailure()) {
2451 return flat_replacement;
2452 }
2453 replacement = String::cast(flat_replacement);
2454 }
2455
2456 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2457 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2458
2459 ASSERT(last_match_info->HasFastElements());
2460
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00002461 if (replacement->length() == 0) {
2462 if (subject->HasOnlyAsciiChars()) {
2463 return StringReplaceRegExpWithEmptyString<SeqAsciiString>(
2464 subject, regexp, last_match_info);
2465 } else {
2466 return StringReplaceRegExpWithEmptyString<SeqTwoByteString>(
2467 subject, regexp, last_match_info);
2468 }
2469 }
2470
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002471 return StringReplaceRegExpWithString(subject,
2472 regexp,
2473 replacement,
2474 last_match_info);
2475}
2476
2477
ager@chromium.org7c537e22008-10-16 08:43:32 +00002478// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2479// limit, we can fix the size of tables.
2480static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002481// Reduce alphabet to this size.
2482static const int kBMAlphabetSize = 0x100;
2483// For patterns below this length, the skip length of Boyer-Moore is too short
2484// to compensate for the algorithmic overhead compared to simple brute force.
2485static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002486
ager@chromium.org7c537e22008-10-16 08:43:32 +00002487// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2488// shift. Only allows the last kBMMaxShift characters of the needle
2489// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002490class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002491 public:
2492 BMGoodSuffixBuffers() {}
2493 inline void init(int needle_length) {
2494 ASSERT(needle_length > 1);
2495 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2496 int len = needle_length - start;
2497 biased_suffixes_ = suffixes_ - start;
2498 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2499 for (int i = 0; i <= len; i++) {
2500 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002501 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002502 }
2503 inline int& suffix(int index) {
2504 ASSERT(biased_suffixes_ + index >= suffixes_);
2505 return biased_suffixes_[index];
2506 }
2507 inline int& shift(int index) {
2508 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2509 return biased_good_suffix_shift_[index];
2510 }
2511 private:
2512 int suffixes_[kBMMaxShift + 1];
2513 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002514 int* biased_suffixes_;
2515 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002516 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2517};
2518
2519// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002520static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002521static BMGoodSuffixBuffers bmgs_buffers;
2522
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002523// State of the string match tables.
2524// SIMPLE: No usable content in the buffers.
2525// BOYER_MOORE_HORSPOOL: The bad_char_occurences table has been populated.
2526// BOYER_MOORE: The bmgs_buffers tables have also been populated.
2527// Whenever starting with a new needle, one should call InitializeStringSearch
2528// to determine which search strategy to use, and in the case of a long-needle
2529// strategy, the call also initializes the algorithm to SIMPLE.
2530enum StringSearchAlgorithm { SIMPLE_SEARCH, BOYER_MOORE_HORSPOOL, BOYER_MOORE };
2531static StringSearchAlgorithm algorithm;
2532
2533
ager@chromium.org7c537e22008-10-16 08:43:32 +00002534// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002535template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002536static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern) {
2537 // Only preprocess at most kBMMaxShift last characters of pattern.
2538 int start = pattern.length() < kBMMaxShift ? 0
2539 : pattern.length() - kBMMaxShift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002540 // Run forwards to populate bad_char_table, so that *last* instance
2541 // of character equivalence class is the one registered.
2542 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002543 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2544 : kBMAlphabetSize;
2545 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002546 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002547 } else {
2548 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002549 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002550 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002551 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002552 for (int i = start; i < pattern.length() - 1; i++) {
2553 pchar c = pattern[i];
2554 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002555 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002556 }
2557}
2558
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002559
ager@chromium.org7c537e22008-10-16 08:43:32 +00002560template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002561static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002562 int m = pattern.length();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002563 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002564 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002565 // Compute Good Suffix tables.
2566 bmgs_buffers.init(m);
2567
2568 bmgs_buffers.shift(m-1) = 1;
2569 bmgs_buffers.suffix(m) = m + 1;
2570 pchar last_char = pattern[m - 1];
2571 int suffix = m + 1;
2572 for (int i = m; i > start;) {
2573 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2574 if (bmgs_buffers.shift(suffix) == len) {
2575 bmgs_buffers.shift(suffix) = suffix - i;
2576 }
2577 suffix = bmgs_buffers.suffix(suffix);
2578 }
2579 i--;
2580 suffix--;
2581 bmgs_buffers.suffix(i) = suffix;
2582 if (suffix == m) {
2583 // No suffix to extend, so we check against last_char only.
2584 while (i > start && pattern[i - 1] != last_char) {
2585 if (bmgs_buffers.shift(m) == len) {
2586 bmgs_buffers.shift(m) = m - i;
2587 }
2588 i--;
2589 bmgs_buffers.suffix(i) = m;
2590 }
2591 if (i > start) {
2592 i--;
2593 suffix--;
2594 bmgs_buffers.suffix(i) = suffix;
2595 }
2596 }
2597 }
2598 if (suffix < m) {
2599 for (int i = start; i <= m; i++) {
2600 if (bmgs_buffers.shift(i) == len) {
2601 bmgs_buffers.shift(i) = suffix - start;
2602 }
2603 if (i == suffix) {
2604 suffix = bmgs_buffers.suffix(suffix);
2605 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002606 }
2607 }
2608}
2609
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002610
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002611template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002612static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002613 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002614 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002615 }
2616 if (sizeof(pchar) == 1) {
2617 if (char_code > String::kMaxAsciiCharCode) {
2618 return -1;
2619 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002620 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002621 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002622 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002623}
2624
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002625
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002626// Restricted simplified Boyer-Moore string matching.
2627// Uses only the bad-shift table of Boyer-Moore and only uses it
2628// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002629template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002630static int BoyerMooreHorspool(Vector<const schar> subject,
2631 Vector<const pchar> pattern,
2632 int start_index,
2633 bool* complete) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002634 ASSERT(algorithm <= BOYER_MOORE_HORSPOOL);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002635 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002636 int m = pattern.length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002637
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002638 int badness = -m;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002639
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002640 // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002641 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002642 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002643 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002644 // Perform search
2645 for (idx = start_index; idx <= n - m;) {
2646 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002647 int c;
2648 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002649 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002650 int shift = j - bc_occ;
2651 idx += shift;
2652 badness += 1 - shift; // at most zero, so badness cannot increase.
2653 if (idx > n - m) {
2654 *complete = true;
2655 return -1;
2656 }
2657 }
2658 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002659 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002660 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002661 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002662 return idx;
2663 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002664 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002665 // Badness increases by the number of characters we have
2666 // checked, and decreases by the number of characters we
2667 // can skip by shifting. It's a measure of how we are doing
2668 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002669 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002670 if (badness > 0) {
2671 *complete = false;
2672 return idx;
2673 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002674 }
2675 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002676 *complete = true;
2677 return -1;
2678}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002679
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002680
2681template <typename schar, typename pchar>
2682static int BoyerMooreIndexOf(Vector<const schar> subject,
2683 Vector<const pchar> pattern,
2684 int idx) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002685 ASSERT(algorithm <= BOYER_MOORE);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002686 int n = subject.length();
2687 int m = pattern.length();
2688 // Only preprocess at most kBMMaxShift last characters of pattern.
2689 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2690
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002691 pchar last_char = pattern[m - 1];
2692 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002693 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002694 int j = m - 1;
2695 schar c;
2696 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002697 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002698 idx += shift;
2699 if (idx > n - m) {
2700 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002701 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002702 }
2703 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2704 if (j < 0) {
2705 return idx;
2706 } else if (j < start) {
2707 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002708 // Fall back on BMH shift.
2709 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002710 } else {
2711 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002712 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002713 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002714 if (gs_shift > shift) {
2715 shift = gs_shift;
2716 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002717 idx += shift;
2718 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002719 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002720
2721 return -1;
2722}
2723
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002724
2725template <typename schar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002726static inline int SingleCharIndexOf(Vector<const schar> string,
2727 schar pattern_char,
2728 int start_index) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002729 if (sizeof(schar) == 1) {
2730 const schar* pos = reinterpret_cast<const schar*>(
2731 memchr(string.start() + start_index,
2732 pattern_char,
2733 string.length() - start_index));
2734 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002735 return static_cast<int>(pos - string.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002736 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002737 for (int i = start_index, n = string.length(); i < n; i++) {
2738 if (pattern_char == string[i]) {
2739 return i;
2740 }
2741 }
2742 return -1;
2743}
2744
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002745
2746template <typename schar>
2747static int SingleCharLastIndexOf(Vector<const schar> string,
2748 schar pattern_char,
2749 int start_index) {
2750 for (int i = start_index; i >= 0; i--) {
2751 if (pattern_char == string[i]) {
2752 return i;
2753 }
2754 }
2755 return -1;
2756}
2757
2758
ager@chromium.org7c537e22008-10-16 08:43:32 +00002759// Trivial string search for shorter strings.
2760// On return, if "complete" is set to true, the return value is the
2761// final result of searching for the patter in the subject.
2762// If "complete" is set to false, the return value is the index where
2763// further checking should start, i.e., it's guaranteed that the pattern
2764// does not occur at a position prior to the returned index.
2765template <typename pchar, typename schar>
2766static int SimpleIndexOf(Vector<const schar> subject,
2767 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002768 int idx,
2769 bool* complete) {
2770 // Badness is a count of how much work we have done. When we have
2771 // done enough work we decide it's probably worth switching to a better
2772 // algorithm.
2773 int badness = -10 - (pattern.length() << 2);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002774
ager@chromium.org7c537e22008-10-16 08:43:32 +00002775 // We know our pattern is at least 2 characters, we cache the first so
2776 // the common case of the first character not matching is faster.
2777 pchar pattern_first_char = pattern[0];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002778 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2779 badness++;
2780 if (badness > 0) {
2781 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002782 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002783 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002784 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2785 const schar* pos = reinterpret_cast<const schar*>(
2786 memchr(subject.start() + i,
2787 pattern_first_char,
2788 n - i + 1));
2789 if (pos == NULL) {
2790 *complete = true;
2791 return -1;
2792 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002793 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002794 } else {
2795 if (subject[i] != pattern_first_char) continue;
2796 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002797 int j = 1;
2798 do {
2799 if (pattern[j] != subject[i+j]) {
2800 break;
2801 }
2802 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002803 } while (j < pattern.length());
2804 if (j == pattern.length()) {
2805 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002806 return i;
2807 }
2808 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002809 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002810 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002811 return -1;
2812}
2813
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002814// Simple indexOf that never bails out. For short patterns only.
2815template <typename pchar, typename schar>
2816static int SimpleIndexOf(Vector<const schar> subject,
2817 Vector<const pchar> pattern,
2818 int idx) {
2819 pchar pattern_first_char = pattern[0];
2820 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002821 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2822 const schar* pos = reinterpret_cast<const schar*>(
2823 memchr(subject.start() + i,
2824 pattern_first_char,
2825 n - i + 1));
2826 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002827 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002828 } else {
2829 if (subject[i] != pattern_first_char) continue;
2830 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002831 int j = 1;
2832 do {
2833 if (pattern[j] != subject[i+j]) {
2834 break;
2835 }
2836 j++;
2837 } while (j < pattern.length());
2838 if (j == pattern.length()) {
2839 return i;
2840 }
2841 }
2842 return -1;
2843}
2844
2845
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002846// Strategy for searching for a string in another string.
2847enum StringSearchStrategy { SEARCH_FAIL, SEARCH_SHORT, SEARCH_LONG };
ager@chromium.org7c537e22008-10-16 08:43:32 +00002848
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002849
2850template <typename pchar>
2851static inline StringSearchStrategy InitializeStringSearch(
2852 Vector<const pchar> pat, bool ascii_subject) {
2853 ASSERT(pat.length() > 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002854 // We have an ASCII haystack and a non-ASCII needle. Check if there
2855 // really is a non-ASCII character in the needle and bail out if there
2856 // is.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002857 if (ascii_subject && sizeof(pchar) > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002858 for (int i = 0; i < pat.length(); i++) {
2859 uc16 c = pat[i];
2860 if (c > String::kMaxAsciiCharCode) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002861 return SEARCH_FAIL;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002862 }
2863 }
2864 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002865 if (pat.length() < kBMMinPatternLength) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002866 return SEARCH_SHORT;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002867 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002868 algorithm = SIMPLE_SEARCH;
2869 return SEARCH_LONG;
2870}
2871
2872
2873// Dispatch long needle searches to different algorithms.
2874template <typename schar, typename pchar>
2875static int ComplexIndexOf(Vector<const schar> sub,
2876 Vector<const pchar> pat,
2877 int start_index) {
2878 ASSERT(pat.length() >= kBMMinPatternLength);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002879 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002880 bool complete;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002881 int idx = start_index;
2882 switch (algorithm) {
2883 case SIMPLE_SEARCH:
2884 idx = SimpleIndexOf(sub, pat, idx, &complete);
2885 if (complete) return idx;
2886 BoyerMoorePopulateBadCharTable(pat);
2887 algorithm = BOYER_MOORE_HORSPOOL;
2888 // FALLTHROUGH.
2889 case BOYER_MOORE_HORSPOOL:
2890 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
2891 if (complete) return idx;
2892 // Build the Good Suffix table and continue searching.
2893 BoyerMoorePopulateGoodSuffixTable(pat);
2894 algorithm = BOYER_MOORE;
2895 // FALLTHROUGH.
2896 case BOYER_MOORE:
2897 return BoyerMooreIndexOf(sub, pat, idx);
2898 }
2899 UNREACHABLE();
2900 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002901}
2902
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002903
2904// Dispatch to different search strategies for a single search.
2905// If searching multiple times on the same needle, the search
2906// strategy should only be computed once and then dispatch to different
2907// loops.
2908template <typename schar, typename pchar>
2909static int StringSearch(Vector<const schar> sub,
2910 Vector<const pchar> pat,
2911 int start_index) {
2912 bool ascii_subject = (sizeof(schar) == 1);
2913 StringSearchStrategy strategy = InitializeStringSearch(pat, ascii_subject);
2914 switch (strategy) {
2915 case SEARCH_FAIL: return -1;
2916 case SEARCH_SHORT: return SimpleIndexOf(sub, pat, start_index);
2917 case SEARCH_LONG: return ComplexIndexOf(sub, pat, start_index);
2918 }
2919 UNREACHABLE();
2920 return -1;
2921}
2922
2923
ager@chromium.org7c537e22008-10-16 08:43:32 +00002924// Perform string match of pattern on subject, starting at start index.
2925// Caller must ensure that 0 <= start_index <= sub->length(),
2926// and should check that pat->length() + start_index <= sub->length()
2927int Runtime::StringMatch(Handle<String> sub,
2928 Handle<String> pat,
2929 int start_index) {
2930 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002931 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002932
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002933 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002934 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002935
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002936 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002937 if (start_index + pattern_length > subject_length) return -1;
2938
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002939 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002940 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002941 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002942
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002943 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002944 // character patterns linear search is necessary, so any smart
2945 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002946 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002947 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002948 String* seq_sub = *sub;
2949 if (seq_sub->IsConsString()) {
2950 seq_sub = ConsString::cast(seq_sub)->first();
2951 }
2952 if (seq_sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002953 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002954 if (pchar > String::kMaxAsciiCharCode) {
2955 return -1;
2956 }
2957 Vector<const char> ascii_vector =
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002958 seq_sub->ToAsciiVector().SubVector(start_index, subject_length);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002959 const void* pos = memchr(ascii_vector.start(),
2960 static_cast<const char>(pchar),
2961 static_cast<size_t>(ascii_vector.length()));
2962 if (pos == NULL) {
2963 return -1;
2964 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002965 return static_cast<int>(reinterpret_cast<const char*>(pos)
2966 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002967 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002968 return SingleCharIndexOf(seq_sub->ToUC16Vector(),
2969 pat->Get(0),
2970 start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002971 }
2972
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002973 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002974 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002975 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002976
ager@chromium.org7c537e22008-10-16 08:43:32 +00002977 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002978 // Extract flattened substrings of cons strings before determining asciiness.
2979 String* seq_sub = *sub;
2980 if (seq_sub->IsConsString()) {
2981 seq_sub = ConsString::cast(seq_sub)->first();
2982 }
2983 String* seq_pat = *pat;
2984 if (seq_pat->IsConsString()) {
2985 seq_pat = ConsString::cast(seq_pat)->first();
2986 }
2987
ager@chromium.org7c537e22008-10-16 08:43:32 +00002988 // dispatch on type of strings
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002989 if (seq_pat->IsAsciiRepresentation()) {
2990 Vector<const char> pat_vector = seq_pat->ToAsciiVector();
2991 if (seq_sub->IsAsciiRepresentation()) {
2992 return StringSearch(seq_sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002993 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002994 return StringSearch(seq_sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002995 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00002996 Vector<const uc16> pat_vector = seq_pat->ToUC16Vector();
2997 if (seq_sub->IsAsciiRepresentation()) {
2998 return StringSearch(seq_sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002999 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +00003000 return StringSearch(seq_sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003001}
3002
3003
3004static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003005 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003006 ASSERT(args.length() == 3);
3007
ager@chromium.org7c537e22008-10-16 08:43:32 +00003008 CONVERT_ARG_CHECKED(String, sub, 0);
3009 CONVERT_ARG_CHECKED(String, pat, 1);
3010
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003011 Object* index = args[2];
3012 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003013 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003014
ager@chromium.org870a0b62008-11-04 11:43:05 +00003015 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00003016 int position = Runtime::StringMatch(sub, pat, start_index);
3017 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003018}
3019
3020
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003021template <typename schar, typename pchar>
3022static int StringMatchBackwards(Vector<const schar> sub,
3023 Vector<const pchar> pat,
3024 int idx) {
3025 ASSERT(pat.length() >= 1);
3026 ASSERT(idx + pat.length() <= sub.length());
3027
3028 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
3029 for (int i = 0; i < pat.length(); i++) {
3030 uc16 c = pat[i];
3031 if (c > String::kMaxAsciiCharCode) {
3032 return -1;
3033 }
3034 }
3035 }
3036
3037 pchar pattern_first_char = pat[0];
3038 for (int i = idx; i >= 0; i--) {
3039 if (sub[i] != pattern_first_char) continue;
3040 int j = 1;
3041 while (j < pat.length()) {
3042 if (pat[j] != sub[i+j]) {
3043 break;
3044 }
3045 j++;
3046 }
3047 if (j == pat.length()) {
3048 return i;
3049 }
3050 }
3051 return -1;
3052}
3053
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003054static Object* Runtime_StringLastIndexOf(Arguments args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003055 HandleScope scope; // create a new handle scope
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003056 ASSERT(args.length() == 3);
3057
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003058 CONVERT_ARG_CHECKED(String, sub, 0);
3059 CONVERT_ARG_CHECKED(String, pat, 1);
3060
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003061 Object* index = args[2];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003062 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003063 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003064
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003065 uint32_t pat_length = pat->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003066 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003067
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003068 if (start_index + pat_length > sub_length) {
3069 start_index = sub_length - pat_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00003070 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003071
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003072 if (pat_length == 0) {
3073 return Smi::FromInt(start_index);
3074 }
3075
3076 if (!sub->IsFlat()) {
3077 FlattenString(sub);
3078 }
3079
3080 if (pat_length == 1) {
3081 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
3082 if (sub->IsAsciiRepresentation()) {
3083 uc16 pchar = pat->Get(0);
3084 if (pchar > String::kMaxAsciiCharCode) {
3085 return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003086 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003087 return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
3088 static_cast<char>(pat->Get(0)),
3089 start_index));
3090 } else {
3091 return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
3092 pat->Get(0),
3093 start_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003094 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003095 }
3096
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003097 if (!pat->IsFlat()) {
3098 FlattenString(pat);
3099 }
3100
3101 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
3102
3103 int position = -1;
3104
3105 if (pat->IsAsciiRepresentation()) {
3106 Vector<const char> pat_vector = pat->ToAsciiVector();
3107 if (sub->IsAsciiRepresentation()) {
3108 position = StringMatchBackwards(sub->ToAsciiVector(),
3109 pat_vector,
3110 start_index);
3111 } else {
3112 position = StringMatchBackwards(sub->ToUC16Vector(),
3113 pat_vector,
3114 start_index);
3115 }
3116 } else {
3117 Vector<const uc16> pat_vector = pat->ToUC16Vector();
3118 if (sub->IsAsciiRepresentation()) {
3119 position = StringMatchBackwards(sub->ToAsciiVector(),
3120 pat_vector,
3121 start_index);
3122 } else {
3123 position = StringMatchBackwards(sub->ToUC16Vector(),
3124 pat_vector,
3125 start_index);
3126 }
3127 }
3128
3129 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003130}
3131
3132
3133static Object* Runtime_StringLocaleCompare(Arguments args) {
3134 NoHandleAllocation ha;
3135 ASSERT(args.length() == 2);
3136
3137 CONVERT_CHECKED(String, str1, args[0]);
3138 CONVERT_CHECKED(String, str2, args[1]);
3139
3140 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003141 int str1_length = str1->length();
3142 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003143
3144 // Decide trivial cases without flattening.
3145 if (str1_length == 0) {
3146 if (str2_length == 0) return Smi::FromInt(0); // Equal.
3147 return Smi::FromInt(-str2_length);
3148 } else {
3149 if (str2_length == 0) return Smi::FromInt(str1_length);
3150 }
3151
3152 int end = str1_length < str2_length ? str1_length : str2_length;
3153
3154 // No need to flatten if we are going to find the answer on the first
3155 // character. At this point we know there is at least one character
3156 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003157 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003158 if (d != 0) return Smi::FromInt(d);
3159
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003160 str1->TryFlatten();
3161 str2->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003162
3163 static StringInputBuffer buf1;
3164 static StringInputBuffer buf2;
3165
3166 buf1.Reset(str1);
3167 buf2.Reset(str2);
3168
3169 for (int i = 0; i < end; i++) {
3170 uint16_t char1 = buf1.GetNext();
3171 uint16_t char2 = buf2.GetNext();
3172 if (char1 != char2) return Smi::FromInt(char1 - char2);
3173 }
3174
3175 return Smi::FromInt(str1_length - str2_length);
3176}
3177
3178
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003179static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003180 NoHandleAllocation ha;
3181 ASSERT(args.length() == 3);
3182
3183 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00003184 Object* from = args[1];
3185 Object* to = args[2];
3186 int start, end;
3187 // We have a fast integer-only case here to avoid a conversion to double in
3188 // the common case where from and to are Smis.
3189 if (from->IsSmi() && to->IsSmi()) {
3190 start = Smi::cast(from)->value();
3191 end = Smi::cast(to)->value();
3192 } else {
3193 CONVERT_DOUBLE_CHECKED(from_number, from);
3194 CONVERT_DOUBLE_CHECKED(to_number, to);
3195 start = FastD2I(from_number);
3196 end = FastD2I(to_number);
3197 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003198 RUNTIME_ASSERT(end >= start);
3199 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003200 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003201 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003202 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003203}
3204
3205
ager@chromium.org41826e72009-03-30 13:30:57 +00003206static Object* Runtime_StringMatch(Arguments args) {
3207 ASSERT_EQ(3, args.length());
3208
3209 CONVERT_ARG_CHECKED(String, subject, 0);
3210 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
3211 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
3212 HandleScope handles;
3213
3214 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
3215
3216 if (match.is_null()) {
3217 return Failure::Exception();
3218 }
3219 if (match->IsNull()) {
3220 return Heap::null_value();
3221 }
3222 int length = subject->length();
3223
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00003224 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00003225 ZoneList<int> offsets(8);
3226 do {
3227 int start;
3228 int end;
3229 {
3230 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003231 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00003232 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
3233 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
3234 }
3235 offsets.Add(start);
3236 offsets.Add(end);
3237 int index = start < end ? end : end + 1;
3238 if (index > length) break;
3239 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
3240 if (match.is_null()) {
3241 return Failure::Exception();
3242 }
3243 } while (!match->IsNull());
3244 int matches = offsets.length() / 2;
3245 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
3246 for (int i = 0; i < matches ; i++) {
3247 int from = offsets.at(i * 2);
3248 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003249 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00003250 }
3251 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
3252 result->set_length(Smi::FromInt(matches));
3253 return *result;
3254}
3255
3256
lrn@chromium.org25156de2010-04-06 13:10:27 +00003257// Two smis before and after the match, for very long strings.
3258const int kMaxBuilderEntriesPerRegExpMatch = 5;
3259
3260
3261static void SetLastMatchInfoNoCaptures(Handle<String> subject,
3262 Handle<JSArray> last_match_info,
3263 int match_start,
3264 int match_end) {
3265 // Fill last_match_info with a single capture.
3266 last_match_info->EnsureSize(2 + RegExpImpl::kLastMatchOverhead);
3267 AssertNoAllocation no_gc;
3268 FixedArray* elements = FixedArray::cast(last_match_info->elements());
3269 RegExpImpl::SetLastCaptureCount(elements, 2);
3270 RegExpImpl::SetLastInput(elements, *subject);
3271 RegExpImpl::SetLastSubject(elements, *subject);
3272 RegExpImpl::SetCapture(elements, 0, match_start);
3273 RegExpImpl::SetCapture(elements, 1, match_end);
3274}
3275
3276
3277template <typename schar>
3278static bool SearchCharMultiple(Vector<schar> subject,
3279 String* pattern,
3280 schar pattern_char,
3281 FixedArrayBuilder* builder,
3282 int* match_pos) {
3283 // Position of last match.
3284 int pos = *match_pos;
3285 int subject_length = subject.length();
3286 while (pos < subject_length) {
3287 int match_end = pos + 1;
3288 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3289 *match_pos = pos;
3290 return false;
3291 }
3292 int new_pos = SingleCharIndexOf(subject, pattern_char, match_end);
3293 if (new_pos >= 0) {
3294 // Match has been found.
3295 if (new_pos > match_end) {
3296 ReplacementStringBuilder::AddSubjectSlice(builder, match_end, new_pos);
3297 }
3298 pos = new_pos;
3299 builder->Add(pattern);
3300 } else {
3301 break;
3302 }
3303 }
3304 if (pos + 1 < subject_length) {
3305 ReplacementStringBuilder::AddSubjectSlice(builder, pos + 1, subject_length);
3306 }
3307 *match_pos = pos;
3308 return true;
3309}
3310
3311
3312static bool SearchCharMultiple(Handle<String> subject,
3313 Handle<String> pattern,
3314 Handle<JSArray> last_match_info,
3315 FixedArrayBuilder* builder) {
3316 ASSERT(subject->IsFlat());
3317 ASSERT_EQ(1, pattern->length());
3318 uc16 pattern_char = pattern->Get(0);
3319 // Treating position before first as initial "previous match position".
3320 int match_pos = -1;
3321
3322 for (;;) { // Break when search complete.
3323 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3324 AssertNoAllocation no_gc;
3325 if (subject->IsAsciiRepresentation()) {
3326 if (pattern_char > String::kMaxAsciiCharCode) {
3327 break;
3328 }
3329 Vector<const char> subject_vector = subject->ToAsciiVector();
3330 char pattern_ascii_char = static_cast<char>(pattern_char);
3331 bool complete = SearchCharMultiple<const char>(subject_vector,
3332 *pattern,
3333 pattern_ascii_char,
3334 builder,
3335 &match_pos);
3336 if (complete) break;
3337 } else {
3338 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3339 bool complete = SearchCharMultiple<const uc16>(subject_vector,
3340 *pattern,
3341 pattern_char,
3342 builder,
3343 &match_pos);
3344 if (complete) break;
3345 }
3346 }
3347
3348 if (match_pos >= 0) {
3349 SetLastMatchInfoNoCaptures(subject,
3350 last_match_info,
3351 match_pos,
3352 match_pos + 1);
3353 return true;
3354 }
3355 return false; // No matches at all.
3356}
3357
3358
3359template <typename schar, typename pchar>
3360static bool SearchStringMultiple(Vector<schar> subject,
3361 String* pattern,
3362 Vector<pchar> pattern_string,
3363 FixedArrayBuilder* builder,
3364 int* match_pos) {
3365 int pos = *match_pos;
3366 int subject_length = subject.length();
3367 int pattern_length = pattern_string.length();
3368 int max_search_start = subject_length - pattern_length;
3369 bool is_ascii = (sizeof(schar) == 1);
3370 StringSearchStrategy strategy =
3371 InitializeStringSearch(pattern_string, is_ascii);
3372 switch (strategy) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003373 case SEARCH_FAIL: break;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003374 case SEARCH_SHORT:
3375 while (pos <= max_search_start) {
3376 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3377 *match_pos = pos;
3378 return false;
3379 }
3380 // Position of end of previous match.
3381 int match_end = pos + pattern_length;
3382 int new_pos = SimpleIndexOf(subject, pattern_string, match_end);
3383 if (new_pos >= 0) {
3384 // A match.
3385 if (new_pos > match_end) {
3386 ReplacementStringBuilder::AddSubjectSlice(builder,
3387 match_end,
3388 new_pos);
3389 }
3390 pos = new_pos;
3391 builder->Add(pattern);
3392 } else {
3393 break;
3394 }
3395 }
3396 break;
3397 case SEARCH_LONG:
3398 while (pos <= max_search_start) {
3399 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003400 *match_pos = pos;
3401 return false;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003402 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003403 int match_end = pos + pattern_length;
3404 int new_pos = ComplexIndexOf(subject, pattern_string, match_end);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003405 if (new_pos >= 0) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003406 // A match has been found.
3407 if (new_pos > match_end) {
3408 ReplacementStringBuilder::AddSubjectSlice(builder,
3409 match_end,
3410 new_pos);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003411 }
3412 pos = new_pos;
3413 builder->Add(pattern);
3414 } else {
3415 break;
3416 }
3417 }
3418 break;
3419 }
3420 if (pos < max_search_start) {
3421 ReplacementStringBuilder::AddSubjectSlice(builder,
3422 pos + pattern_length,
3423 subject_length);
3424 }
3425 *match_pos = pos;
3426 return true;
3427}
3428
3429
3430static bool SearchStringMultiple(Handle<String> subject,
3431 Handle<String> pattern,
3432 Handle<JSArray> last_match_info,
3433 FixedArrayBuilder* builder) {
3434 ASSERT(subject->IsFlat());
3435 ASSERT(pattern->IsFlat());
3436 ASSERT(pattern->length() > 1);
3437
3438 // Treating as if a previous match was before first character.
3439 int match_pos = -pattern->length();
3440
3441 for (;;) { // Break when search complete.
3442 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3443 AssertNoAllocation no_gc;
3444 if (subject->IsAsciiRepresentation()) {
3445 Vector<const char> subject_vector = subject->ToAsciiVector();
3446 if (pattern->IsAsciiRepresentation()) {
3447 if (SearchStringMultiple(subject_vector,
3448 *pattern,
3449 pattern->ToAsciiVector(),
3450 builder,
3451 &match_pos)) break;
3452 } else {
3453 if (SearchStringMultiple(subject_vector,
3454 *pattern,
3455 pattern->ToUC16Vector(),
3456 builder,
3457 &match_pos)) break;
3458 }
3459 } else {
3460 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3461 if (pattern->IsAsciiRepresentation()) {
3462 if (SearchStringMultiple(subject_vector,
3463 *pattern,
3464 pattern->ToAsciiVector(),
3465 builder,
3466 &match_pos)) break;
3467 } else {
3468 if (SearchStringMultiple(subject_vector,
3469 *pattern,
3470 pattern->ToUC16Vector(),
3471 builder,
3472 &match_pos)) break;
3473 }
3474 }
3475 }
3476
3477 if (match_pos >= 0) {
3478 SetLastMatchInfoNoCaptures(subject,
3479 last_match_info,
3480 match_pos,
3481 match_pos + pattern->length());
3482 return true;
3483 }
3484 return false; // No matches at all.
3485}
3486
3487
3488static RegExpImpl::IrregexpResult SearchRegExpNoCaptureMultiple(
3489 Handle<String> subject,
3490 Handle<JSRegExp> regexp,
3491 Handle<JSArray> last_match_array,
3492 FixedArrayBuilder* builder) {
3493 ASSERT(subject->IsFlat());
3494 int match_start = -1;
3495 int match_end = 0;
3496 int pos = 0;
3497 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3498 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3499
3500 OffsetsVector registers(required_registers);
3501 Vector<int> register_vector(registers.vector(), registers.length());
3502 int subject_length = subject->length();
3503
3504 for (;;) { // Break on failure, return on exception.
3505 RegExpImpl::IrregexpResult result =
3506 RegExpImpl::IrregexpExecOnce(regexp,
3507 subject,
3508 pos,
3509 register_vector);
3510 if (result == RegExpImpl::RE_SUCCESS) {
3511 match_start = register_vector[0];
3512 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3513 if (match_end < match_start) {
3514 ReplacementStringBuilder::AddSubjectSlice(builder,
3515 match_end,
3516 match_start);
3517 }
3518 match_end = register_vector[1];
3519 HandleScope loop_scope;
3520 builder->Add(*Factory::NewSubString(subject, match_start, match_end));
3521 if (match_start != match_end) {
3522 pos = match_end;
3523 } else {
3524 pos = match_end + 1;
3525 if (pos > subject_length) break;
3526 }
3527 } else if (result == RegExpImpl::RE_FAILURE) {
3528 break;
3529 } else {
3530 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3531 return result;
3532 }
3533 }
3534
3535 if (match_start >= 0) {
3536 if (match_end < subject_length) {
3537 ReplacementStringBuilder::AddSubjectSlice(builder,
3538 match_end,
3539 subject_length);
3540 }
3541 SetLastMatchInfoNoCaptures(subject,
3542 last_match_array,
3543 match_start,
3544 match_end);
3545 return RegExpImpl::RE_SUCCESS;
3546 } else {
3547 return RegExpImpl::RE_FAILURE; // No matches at all.
3548 }
3549}
3550
3551
3552static RegExpImpl::IrregexpResult SearchRegExpMultiple(
3553 Handle<String> subject,
3554 Handle<JSRegExp> regexp,
3555 Handle<JSArray> last_match_array,
3556 FixedArrayBuilder* builder) {
3557
3558 ASSERT(subject->IsFlat());
3559 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3560 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3561
3562 OffsetsVector registers(required_registers);
3563 Vector<int> register_vector(registers.vector(), registers.length());
3564
3565 RegExpImpl::IrregexpResult result =
3566 RegExpImpl::IrregexpExecOnce(regexp,
3567 subject,
3568 0,
3569 register_vector);
3570
3571 int capture_count = regexp->CaptureCount();
3572 int subject_length = subject->length();
3573
3574 // Position to search from.
3575 int pos = 0;
3576 // End of previous match. Differs from pos if match was empty.
3577 int match_end = 0;
3578 if (result == RegExpImpl::RE_SUCCESS) {
3579 // Need to keep a copy of the previous match for creating last_match_info
3580 // at the end, so we have two vectors that we swap between.
3581 OffsetsVector registers2(required_registers);
3582 Vector<int> prev_register_vector(registers2.vector(), registers2.length());
3583
3584 do {
3585 int match_start = register_vector[0];
3586 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3587 if (match_end < match_start) {
3588 ReplacementStringBuilder::AddSubjectSlice(builder,
3589 match_end,
3590 match_start);
3591 }
3592 match_end = register_vector[1];
3593
3594 {
3595 // Avoid accumulating new handles inside loop.
3596 HandleScope temp_scope;
3597 // Arguments array to replace function is match, captures, index and
3598 // subject, i.e., 3 + capture count in total.
3599 Handle<FixedArray> elements = Factory::NewFixedArray(3 + capture_count);
3600 elements->set(0, *Factory::NewSubString(subject,
3601 match_start,
3602 match_end));
3603 for (int i = 1; i <= capture_count; i++) {
3604 int start = register_vector[i * 2];
3605 if (start >= 0) {
3606 int end = register_vector[i * 2 + 1];
3607 ASSERT(start <= end);
3608 Handle<String> substring = Factory::NewSubString(subject,
3609 start,
3610 end);
3611 elements->set(i, *substring);
3612 } else {
3613 ASSERT(register_vector[i * 2 + 1] < 0);
3614 elements->set(i, Heap::undefined_value());
3615 }
3616 }
3617 elements->set(capture_count + 1, Smi::FromInt(match_start));
3618 elements->set(capture_count + 2, *subject);
3619 builder->Add(*Factory::NewJSArrayWithElements(elements));
3620 }
3621 // Swap register vectors, so the last successful match is in
3622 // prev_register_vector.
3623 Vector<int> tmp = prev_register_vector;
3624 prev_register_vector = register_vector;
3625 register_vector = tmp;
3626
3627 if (match_end > match_start) {
3628 pos = match_end;
3629 } else {
3630 pos = match_end + 1;
3631 if (pos > subject_length) {
3632 break;
3633 }
3634 }
3635
3636 result = RegExpImpl::IrregexpExecOnce(regexp,
3637 subject,
3638 pos,
3639 register_vector);
3640 } while (result == RegExpImpl::RE_SUCCESS);
3641
3642 if (result != RegExpImpl::RE_EXCEPTION) {
3643 // Finished matching, with at least one match.
3644 if (match_end < subject_length) {
3645 ReplacementStringBuilder::AddSubjectSlice(builder,
3646 match_end,
3647 subject_length);
3648 }
3649
3650 int last_match_capture_count = (capture_count + 1) * 2;
3651 int last_match_array_size =
3652 last_match_capture_count + RegExpImpl::kLastMatchOverhead;
3653 last_match_array->EnsureSize(last_match_array_size);
3654 AssertNoAllocation no_gc;
3655 FixedArray* elements = FixedArray::cast(last_match_array->elements());
3656 RegExpImpl::SetLastCaptureCount(elements, last_match_capture_count);
3657 RegExpImpl::SetLastSubject(elements, *subject);
3658 RegExpImpl::SetLastInput(elements, *subject);
3659 for (int i = 0; i < last_match_capture_count; i++) {
3660 RegExpImpl::SetCapture(elements, i, prev_register_vector[i]);
3661 }
3662 return RegExpImpl::RE_SUCCESS;
3663 }
3664 }
3665 // No matches at all, return failure or exception result directly.
3666 return result;
3667}
3668
3669
3670static Object* Runtime_RegExpExecMultiple(Arguments args) {
3671 ASSERT(args.length() == 4);
3672 HandleScope handles;
3673
3674 CONVERT_ARG_CHECKED(String, subject, 1);
3675 if (!subject->IsFlat()) { FlattenString(subject); }
3676 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
3677 CONVERT_ARG_CHECKED(JSArray, last_match_info, 2);
3678 CONVERT_ARG_CHECKED(JSArray, result_array, 3);
3679
3680 ASSERT(last_match_info->HasFastElements());
3681 ASSERT(regexp->GetFlags().is_global());
3682 Handle<FixedArray> result_elements;
3683 if (result_array->HasFastElements()) {
3684 result_elements =
3685 Handle<FixedArray>(FixedArray::cast(result_array->elements()));
3686 } else {
3687 result_elements = Factory::NewFixedArrayWithHoles(16);
3688 }
3689 FixedArrayBuilder builder(result_elements);
3690
3691 if (regexp->TypeTag() == JSRegExp::ATOM) {
3692 Handle<String> pattern(
3693 String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex)));
3694 int pattern_length = pattern->length();
3695 if (pattern_length == 1) {
3696 if (SearchCharMultiple(subject, pattern, last_match_info, &builder)) {
3697 return *builder.ToJSArray(result_array);
3698 }
3699 return Heap::null_value();
3700 }
3701
3702 if (!pattern->IsFlat()) FlattenString(pattern);
3703 if (SearchStringMultiple(subject, pattern, last_match_info, &builder)) {
3704 return *builder.ToJSArray(result_array);
3705 }
3706 return Heap::null_value();
3707 }
3708
3709 ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
3710
3711 RegExpImpl::IrregexpResult result;
3712 if (regexp->CaptureCount() == 0) {
3713 result = SearchRegExpNoCaptureMultiple(subject,
3714 regexp,
3715 last_match_info,
3716 &builder);
3717 } else {
3718 result = SearchRegExpMultiple(subject, regexp, last_match_info, &builder);
3719 }
3720 if (result == RegExpImpl::RE_SUCCESS) return *builder.ToJSArray(result_array);
3721 if (result == RegExpImpl::RE_FAILURE) return Heap::null_value();
3722 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3723 return Failure::Exception();
3724}
3725
3726
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003727static Object* Runtime_NumberToRadixString(Arguments args) {
3728 NoHandleAllocation ha;
3729 ASSERT(args.length() == 2);
3730
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003731 // Fast case where the result is a one character string.
3732 if (args[0]->IsSmi() && args[1]->IsSmi()) {
3733 int value = Smi::cast(args[0])->value();
3734 int radix = Smi::cast(args[1])->value();
3735 if (value >= 0 && value < radix) {
3736 RUNTIME_ASSERT(radix <= 36);
3737 // Character array used for conversion.
3738 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
3739 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
3740 }
3741 }
3742
3743 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003744 CONVERT_DOUBLE_CHECKED(value, args[0]);
3745 if (isnan(value)) {
3746 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3747 }
3748 if (isinf(value)) {
3749 if (value < 0) {
3750 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3751 }
3752 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3753 }
3754 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
3755 int radix = FastD2I(radix_number);
3756 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3757 char* str = DoubleToRadixCString(value, radix);
3758 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3759 DeleteArray(str);
3760 return result;
3761}
3762
3763
3764static Object* Runtime_NumberToFixed(Arguments args) {
3765 NoHandleAllocation ha;
3766 ASSERT(args.length() == 2);
3767
3768 CONVERT_DOUBLE_CHECKED(value, args[0]);
3769 if (isnan(value)) {
3770 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3771 }
3772 if (isinf(value)) {
3773 if (value < 0) {
3774 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3775 }
3776 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3777 }
3778 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3779 int f = FastD2I(f_number);
3780 RUNTIME_ASSERT(f >= 0);
3781 char* str = DoubleToFixedCString(value, f);
3782 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3783 DeleteArray(str);
3784 return res;
3785}
3786
3787
3788static Object* Runtime_NumberToExponential(Arguments args) {
3789 NoHandleAllocation ha;
3790 ASSERT(args.length() == 2);
3791
3792 CONVERT_DOUBLE_CHECKED(value, args[0]);
3793 if (isnan(value)) {
3794 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3795 }
3796 if (isinf(value)) {
3797 if (value < 0) {
3798 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3799 }
3800 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3801 }
3802 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3803 int f = FastD2I(f_number);
3804 RUNTIME_ASSERT(f >= -1 && f <= 20);
3805 char* str = DoubleToExponentialCString(value, f);
3806 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3807 DeleteArray(str);
3808 return res;
3809}
3810
3811
3812static Object* Runtime_NumberToPrecision(Arguments args) {
3813 NoHandleAllocation ha;
3814 ASSERT(args.length() == 2);
3815
3816 CONVERT_DOUBLE_CHECKED(value, args[0]);
3817 if (isnan(value)) {
3818 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3819 }
3820 if (isinf(value)) {
3821 if (value < 0) {
3822 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3823 }
3824 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3825 }
3826 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3827 int f = FastD2I(f_number);
3828 RUNTIME_ASSERT(f >= 1 && f <= 21);
3829 char* str = DoubleToPrecisionCString(value, f);
3830 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3831 DeleteArray(str);
3832 return res;
3833}
3834
3835
3836// Returns a single character string where first character equals
3837// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003838static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003839 if (index < static_cast<uint32_t>(string->length())) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003840 string->TryFlatten();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003841 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003842 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003843 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003844 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003845}
3846
3847
3848Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
3849 // Handle [] indexing on Strings
3850 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003851 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
3852 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003853 }
3854
3855 // Handle [] indexing on String objects
3856 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003857 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
3858 Handle<Object> result =
3859 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
3860 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003861 }
3862
3863 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003864 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003865 return prototype->GetElement(index);
3866 }
3867
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003868 return GetElement(object, index);
3869}
3870
3871
3872Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003873 return object->GetElement(index);
3874}
3875
3876
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003877Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
3878 HandleScope scope;
3879
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003880 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003881 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003882 Handle<Object> error =
3883 Factory::NewTypeError("non_object_property_load",
3884 HandleVector(args, 2));
3885 return Top::Throw(*error);
3886 }
3887
3888 // Check if the given key is an array index.
3889 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003890 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003891 return GetElementOrCharAt(object, index);
3892 }
3893
3894 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003895 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003896 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003897 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003898 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003899 bool has_pending_exception = false;
3900 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003901 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003902 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003903 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003904 }
3905
ager@chromium.org32912102009-01-16 10:38:43 +00003906 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003907 // the element if so.
3908 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003909 return GetElementOrCharAt(object, index);
3910 } else {
3911 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003912 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003913 }
3914}
3915
3916
3917static Object* Runtime_GetProperty(Arguments args) {
3918 NoHandleAllocation ha;
3919 ASSERT(args.length() == 2);
3920
3921 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003922 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003923
3924 return Runtime::GetObjectProperty(object, key);
3925}
3926
3927
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003928// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003929static Object* Runtime_KeyedGetProperty(Arguments args) {
3930 NoHandleAllocation ha;
3931 ASSERT(args.length() == 2);
3932
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003933 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00003934 // itself.
3935 //
3936 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00003937 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00003938 // global proxy object never has properties. This is the case
3939 // because the global proxy object forwards everything to its hidden
3940 // prototype including local lookups.
3941 //
3942 // Additionally, we need to make sure that we do not cache results
3943 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003944 if (args[0]->IsJSObject() &&
3945 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00003946 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003947 args[1]->IsString()) {
3948 JSObject* receiver = JSObject::cast(args[0]);
3949 String* key = String::cast(args[1]);
3950 if (receiver->HasFastProperties()) {
3951 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003952 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003953 int offset = KeyedLookupCache::Lookup(receiver_map, key);
3954 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003955 Object* value = receiver->FastPropertyAt(offset);
3956 return value->IsTheHole() ? Heap::undefined_value() : value;
3957 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003958 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003959 LookupResult result;
3960 receiver->LocalLookup(key, &result);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00003961 if (result.IsProperty() && result.type() == FIELD) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003962 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003963 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003964 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003965 }
3966 } else {
3967 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003968 StringDictionary* dictionary = receiver->property_dictionary();
3969 int entry = dictionary->FindEntry(key);
3970 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003971 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003972 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003973 if (!receiver->IsGlobalObject()) return value;
3974 value = JSGlobalPropertyCell::cast(value)->value();
3975 if (!value->IsTheHole()) return value;
3976 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003977 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003978 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003979 } else if (args[0]->IsString() && args[1]->IsSmi()) {
3980 // Fast case for string indexing using [] with a smi index.
3981 HandleScope scope;
3982 Handle<String> str = args.at<String>(0);
3983 int index = Smi::cast(args[1])->value();
3984 Handle<Object> result = GetCharAt(str, index);
3985 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003986 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003987
3988 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003989 return Runtime::GetObjectProperty(args.at<Object>(0),
3990 args.at<Object>(1));
3991}
3992
3993
ager@chromium.org5c838252010-02-19 08:53:10 +00003994static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
3995 ASSERT(args.length() == 5);
3996 HandleScope scope;
3997 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3998 CONVERT_CHECKED(String, name, args[1]);
3999 CONVERT_CHECKED(Smi, flag_setter, args[2]);
4000 CONVERT_CHECKED(JSFunction, fun, args[3]);
4001 CONVERT_CHECKED(Smi, flag_attr, args[4]);
4002 int unchecked = flag_attr->value();
4003 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4004 RUNTIME_ASSERT(!obj->IsNull());
4005 LookupResult result;
4006 obj->LocalLookupRealNamedProperty(name, &result);
4007
4008 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
4009 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
4010 // delete it to avoid running into trouble in DefineAccessor, which
4011 // handles this incorrectly if the property is readonly (does nothing)
4012 if (result.IsProperty() &&
4013 (result.type() == FIELD || result.type() == NORMAL
4014 || result.type() == CONSTANT_FUNCTION)) {
4015 obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
4016 }
4017 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
4018}
4019
4020static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
4021 ASSERT(args.length() == 4);
4022 HandleScope scope;
4023 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
4024 CONVERT_ARG_CHECKED(String, name, 1);
4025 Handle<Object> obj_value = args.at<Object>(2);
4026
4027 CONVERT_CHECKED(Smi, flag, args[3]);
4028 int unchecked = flag->value();
4029 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4030
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004031 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
4032
4033 // Check if this is an element.
4034 uint32_t index;
4035 bool is_element = name->AsArrayIndex(&index);
4036
4037 // Special case for elements if any of the flags are true.
4038 // If elements are in fast case we always implicitly assume that:
4039 // DONT_DELETE: false, DONT_ENUM: false, READ_ONLY: false.
4040 if (((unchecked & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) &&
4041 is_element) {
4042 // Normalize the elements to enable attributes on the property.
4043 js_object->NormalizeElements();
4044 NumberDictionary* dictionary = js_object->element_dictionary();
4045 // Make sure that we never go back to fast case.
4046 dictionary->set_requires_slow_elements();
4047 PropertyDetails details = PropertyDetails(attr, NORMAL);
4048 dictionary->Set(index, *obj_value, details);
4049 }
4050
ager@chromium.org5c838252010-02-19 08:53:10 +00004051 LookupResult result;
4052 js_object->LocalLookupRealNamedProperty(*name, &result);
4053
ager@chromium.org5c838252010-02-19 08:53:10 +00004054 // Take special care when attributes are different and there is already
4055 // a property. For simplicity we normalize the property which enables us
4056 // to not worry about changing the instance_descriptor and creating a new
4057 // map. The current version of SetObjectProperty does not handle attributes
4058 // correctly in the case where a property is a field and is reset with
4059 // new attributes.
4060 if (result.IsProperty() && attr != result.GetAttributes()) {
4061 // New attributes - normalize to avoid writing to instance descriptor
4062 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
4063 // Use IgnoreAttributes version since a readonly property may be
4064 // overridden and SetProperty does not allow this.
4065 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
4066 *obj_value,
4067 attr);
4068 }
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004069
ager@chromium.org5c838252010-02-19 08:53:10 +00004070 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
4071}
4072
4073
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004074Object* Runtime::SetObjectProperty(Handle<Object> object,
4075 Handle<Object> key,
4076 Handle<Object> value,
4077 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004078 HandleScope scope;
4079
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004080 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004081 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004082 Handle<Object> error =
4083 Factory::NewTypeError("non_object_property_store",
4084 HandleVector(args, 2));
4085 return Top::Throw(*error);
4086 }
4087
4088 // If the object isn't a JavaScript object, we ignore the store.
4089 if (!object->IsJSObject()) return *value;
4090
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004091 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
4092
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004093 // Check if the given key is an array index.
4094 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004095 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004096 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
4097 // of a string using [] notation. We need to support this too in
4098 // JavaScript.
4099 // In the case of a String object we just need to redirect the assignment to
4100 // the underlying string if the index is in range. Since the underlying
4101 // string does nothing with the assignment then we can ignore such
4102 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004103 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004104 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004105 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004106
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004107 Handle<Object> result = SetElement(js_object, index, value);
4108 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004109 return *value;
4110 }
4111
4112 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004113 Handle<Object> result;
4114 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004115 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004116 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004117 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004118 key_string->TryFlatten();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004119 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004120 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004121 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004122 return *value;
4123 }
4124
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004125 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004126 bool has_pending_exception = false;
4127 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4128 if (has_pending_exception) return Failure::Exception();
4129 Handle<String> name = Handle<String>::cast(converted);
4130
4131 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004132 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004133 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004134 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004135 }
4136}
4137
4138
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004139Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
4140 Handle<Object> key,
4141 Handle<Object> value,
4142 PropertyAttributes attr) {
4143 HandleScope scope;
4144
4145 // Check if the given key is an array index.
4146 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004147 if (key->ToArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004148 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
4149 // of a string using [] notation. We need to support this too in
4150 // JavaScript.
4151 // In the case of a String object we just need to redirect the assignment to
4152 // the underlying string if the index is in range. Since the underlying
4153 // string does nothing with the assignment then we can ignore such
4154 // assignments.
4155 if (js_object->IsStringObjectWithCharacterAt(index)) {
4156 return *value;
4157 }
4158
4159 return js_object->SetElement(index, *value);
4160 }
4161
4162 if (key->IsString()) {
4163 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004164 return js_object->SetElement(index, *value);
4165 } else {
4166 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004167 key_string->TryFlatten();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004168 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
4169 *value,
4170 attr);
4171 }
4172 }
4173
4174 // Call-back into JavaScript to convert the key to a string.
4175 bool has_pending_exception = false;
4176 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4177 if (has_pending_exception) return Failure::Exception();
4178 Handle<String> name = Handle<String>::cast(converted);
4179
4180 if (name->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004181 return js_object->SetElement(index, *value);
4182 } else {
4183 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
4184 }
4185}
4186
4187
ager@chromium.orge2902be2009-06-08 12:21:35 +00004188Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
4189 Handle<Object> key) {
4190 HandleScope scope;
4191
4192 // Check if the given key is an array index.
4193 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004194 if (key->ToArrayIndex(&index)) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00004195 // In Firefox/SpiderMonkey, Safari and Opera you can access the
4196 // characters of a string using [] notation. In the case of a
4197 // String object we just need to redirect the deletion to the
4198 // underlying string if the index is in range. Since the
4199 // underlying string does nothing with the deletion, we can ignore
4200 // such deletions.
4201 if (js_object->IsStringObjectWithCharacterAt(index)) {
4202 return Heap::true_value();
4203 }
4204
4205 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
4206 }
4207
4208 Handle<String> key_string;
4209 if (key->IsString()) {
4210 key_string = Handle<String>::cast(key);
4211 } else {
4212 // Call-back into JavaScript to convert the key to a string.
4213 bool has_pending_exception = false;
4214 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4215 if (has_pending_exception) return Failure::Exception();
4216 key_string = Handle<String>::cast(converted);
4217 }
4218
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004219 key_string->TryFlatten();
ager@chromium.orge2902be2009-06-08 12:21:35 +00004220 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
4221}
4222
4223
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004224static Object* Runtime_SetProperty(Arguments args) {
4225 NoHandleAllocation ha;
4226 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
4227
4228 Handle<Object> object = args.at<Object>(0);
4229 Handle<Object> key = args.at<Object>(1);
4230 Handle<Object> value = args.at<Object>(2);
4231
4232 // Compute attributes.
4233 PropertyAttributes attributes = NONE;
4234 if (args.length() == 4) {
4235 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004236 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004237 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004238 RUNTIME_ASSERT(
4239 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4240 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004241 }
4242 return Runtime::SetObjectProperty(object, key, value, attributes);
4243}
4244
4245
4246// Set a local property, even if it is READ_ONLY. If the property does not
4247// exist, it will be added with attributes NONE.
4248static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
4249 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004250 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004251 CONVERT_CHECKED(JSObject, object, args[0]);
4252 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004253 // Compute attributes.
4254 PropertyAttributes attributes = NONE;
4255 if (args.length() == 4) {
4256 CONVERT_CHECKED(Smi, value_obj, args[3]);
4257 int unchecked_value = value_obj->value();
4258 // Only attribute bits should be set.
4259 RUNTIME_ASSERT(
4260 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4261 attributes = static_cast<PropertyAttributes>(unchecked_value);
4262 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004263
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004264 return object->
4265 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004266}
4267
4268
4269static Object* Runtime_DeleteProperty(Arguments args) {
4270 NoHandleAllocation ha;
4271 ASSERT(args.length() == 2);
4272
4273 CONVERT_CHECKED(JSObject, object, args[0]);
4274 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00004275 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004276}
4277
4278
ager@chromium.org9085a012009-05-11 19:22:57 +00004279static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
4280 Handle<String> key) {
4281 if (object->HasLocalProperty(*key)) return Heap::true_value();
4282 // Handle hidden prototypes. If there's a hidden prototype above this thing
4283 // then we have to check it for properties, because they are supposed to
4284 // look like they are on this object.
4285 Handle<Object> proto(object->GetPrototype());
4286 if (proto->IsJSObject() &&
4287 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
4288 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
4289 }
4290 return Heap::false_value();
4291}
4292
4293
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004294static Object* Runtime_HasLocalProperty(Arguments args) {
4295 NoHandleAllocation ha;
4296 ASSERT(args.length() == 2);
4297 CONVERT_CHECKED(String, key, args[1]);
4298
ager@chromium.org9085a012009-05-11 19:22:57 +00004299 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004300 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00004301 if (obj->IsJSObject()) {
4302 JSObject* object = JSObject::cast(obj);
4303 // Fast case - no interceptors.
4304 if (object->HasRealNamedProperty(key)) return Heap::true_value();
4305 // Slow case. Either it's not there or we have an interceptor. We should
4306 // have handles for this kind of deal.
4307 HandleScope scope;
4308 return HasLocalPropertyImplementation(Handle<JSObject>(object),
4309 Handle<String>(key));
4310 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004311 // Well, there is one exception: Handle [] on strings.
4312 uint32_t index;
4313 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00004314 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004315 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004316 return Heap::true_value();
4317 }
4318 }
4319 return Heap::false_value();
4320}
4321
4322
4323static Object* Runtime_HasProperty(Arguments args) {
4324 NoHandleAllocation na;
4325 ASSERT(args.length() == 2);
4326
4327 // Only JS objects can have properties.
4328 if (args[0]->IsJSObject()) {
4329 JSObject* object = JSObject::cast(args[0]);
4330 CONVERT_CHECKED(String, key, args[1]);
4331 if (object->HasProperty(key)) return Heap::true_value();
4332 }
4333 return Heap::false_value();
4334}
4335
4336
4337static Object* Runtime_HasElement(Arguments args) {
4338 NoHandleAllocation na;
4339 ASSERT(args.length() == 2);
4340
4341 // Only JS objects can have elements.
4342 if (args[0]->IsJSObject()) {
4343 JSObject* object = JSObject::cast(args[0]);
4344 CONVERT_CHECKED(Smi, index_obj, args[1]);
4345 uint32_t index = index_obj->value();
4346 if (object->HasElement(index)) return Heap::true_value();
4347 }
4348 return Heap::false_value();
4349}
4350
4351
4352static Object* Runtime_IsPropertyEnumerable(Arguments args) {
4353 NoHandleAllocation ha;
4354 ASSERT(args.length() == 2);
4355
4356 CONVERT_CHECKED(JSObject, object, args[0]);
4357 CONVERT_CHECKED(String, key, args[1]);
4358
4359 uint32_t index;
4360 if (key->AsArrayIndex(&index)) {
4361 return Heap::ToBoolean(object->HasElement(index));
4362 }
4363
ager@chromium.org870a0b62008-11-04 11:43:05 +00004364 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
4365 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004366}
4367
4368
4369static Object* Runtime_GetPropertyNames(Arguments args) {
4370 HandleScope scope;
4371 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004372 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004373 return *GetKeysFor(object);
4374}
4375
4376
4377// Returns either a FixedArray as Runtime_GetPropertyNames,
4378// or, if the given object has an enum cache that contains
4379// all enumerable properties of the object and its prototypes
4380// have none, the map of the object. This is used to speed up
4381// the check for deletions during a for-in.
4382static Object* Runtime_GetPropertyNamesFast(Arguments args) {
4383 ASSERT(args.length() == 1);
4384
4385 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4386
4387 if (raw_object->IsSimpleEnum()) return raw_object->map();
4388
4389 HandleScope scope;
4390 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004391 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
4392 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004393
4394 // Test again, since cache may have been built by preceding call.
4395 if (object->IsSimpleEnum()) return object->map();
4396
4397 return *content;
4398}
4399
4400
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004401// Find the length of the prototype chain that is to to handled as one. If a
4402// prototype object is hidden it is to be viewed as part of the the object it
4403// is prototype for.
4404static int LocalPrototypeChainLength(JSObject* obj) {
4405 int count = 1;
4406 Object* proto = obj->GetPrototype();
4407 while (proto->IsJSObject() &&
4408 JSObject::cast(proto)->map()->is_hidden_prototype()) {
4409 count++;
4410 proto = JSObject::cast(proto)->GetPrototype();
4411 }
4412 return count;
4413}
4414
4415
4416// Return the names of the local named properties.
4417// args[0]: object
4418static Object* Runtime_GetLocalPropertyNames(Arguments args) {
4419 HandleScope scope;
4420 ASSERT(args.length() == 1);
4421 if (!args[0]->IsJSObject()) {
4422 return Heap::undefined_value();
4423 }
4424 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4425
4426 // Skip the global proxy as it has no properties and always delegates to the
4427 // real global object.
4428 if (obj->IsJSGlobalProxy()) {
4429 // Only collect names if access is permitted.
4430 if (obj->IsAccessCheckNeeded() &&
4431 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
4432 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
4433 return *Factory::NewJSArray(0);
4434 }
4435 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
4436 }
4437
4438 // Find the number of objects making up this.
4439 int length = LocalPrototypeChainLength(*obj);
4440
4441 // Find the number of local properties for each of the objects.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004442 ScopedVector<int> local_property_count(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004443 int total_property_count = 0;
4444 Handle<JSObject> jsproto = obj;
4445 for (int i = 0; i < length; i++) {
4446 // Only collect names if access is permitted.
4447 if (jsproto->IsAccessCheckNeeded() &&
4448 !Top::MayNamedAccess(*jsproto,
4449 Heap::undefined_value(),
4450 v8::ACCESS_KEYS)) {
4451 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
4452 return *Factory::NewJSArray(0);
4453 }
4454 int n;
4455 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4456 local_property_count[i] = n;
4457 total_property_count += n;
4458 if (i < length - 1) {
4459 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4460 }
4461 }
4462
4463 // Allocate an array with storage for all the property names.
4464 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
4465
4466 // Get the property names.
4467 jsproto = obj;
4468 int proto_with_hidden_properties = 0;
4469 for (int i = 0; i < length; i++) {
4470 jsproto->GetLocalPropertyNames(*names,
4471 i == 0 ? 0 : local_property_count[i - 1]);
4472 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
4473 proto_with_hidden_properties++;
4474 }
4475 if (i < length - 1) {
4476 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4477 }
4478 }
4479
4480 // Filter out name of hidden propeties object.
4481 if (proto_with_hidden_properties > 0) {
4482 Handle<FixedArray> old_names = names;
4483 names = Factory::NewFixedArray(
4484 names->length() - proto_with_hidden_properties);
4485 int dest_pos = 0;
4486 for (int i = 0; i < total_property_count; i++) {
4487 Object* name = old_names->get(i);
4488 if (name == Heap::hidden_symbol()) {
4489 continue;
4490 }
4491 names->set(dest_pos++, name);
4492 }
4493 }
4494
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004495 return *Factory::NewJSArrayWithElements(names);
4496}
4497
4498
4499// Return the names of the local indexed properties.
4500// args[0]: object
4501static Object* Runtime_GetLocalElementNames(Arguments args) {
4502 HandleScope scope;
4503 ASSERT(args.length() == 1);
4504 if (!args[0]->IsJSObject()) {
4505 return Heap::undefined_value();
4506 }
4507 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4508
4509 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4510 Handle<FixedArray> names = Factory::NewFixedArray(n);
4511 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4512 return *Factory::NewJSArrayWithElements(names);
4513}
4514
4515
4516// Return information on whether an object has a named or indexed interceptor.
4517// args[0]: object
4518static Object* Runtime_GetInterceptorInfo(Arguments args) {
4519 HandleScope scope;
4520 ASSERT(args.length() == 1);
4521 if (!args[0]->IsJSObject()) {
4522 return Smi::FromInt(0);
4523 }
4524 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4525
4526 int result = 0;
4527 if (obj->HasNamedInterceptor()) result |= 2;
4528 if (obj->HasIndexedInterceptor()) result |= 1;
4529
4530 return Smi::FromInt(result);
4531}
4532
4533
4534// Return property names from named interceptor.
4535// args[0]: object
4536static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
4537 HandleScope scope;
4538 ASSERT(args.length() == 1);
4539 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4540
4541 if (obj->HasNamedInterceptor()) {
4542 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4543 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4544 }
4545 return Heap::undefined_value();
4546}
4547
4548
4549// Return element names from indexed interceptor.
4550// args[0]: object
4551static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
4552 HandleScope scope;
4553 ASSERT(args.length() == 1);
4554 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4555
4556 if (obj->HasIndexedInterceptor()) {
4557 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4558 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4559 }
4560 return Heap::undefined_value();
4561}
4562
4563
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004564static Object* Runtime_LocalKeys(Arguments args) {
4565 ASSERT_EQ(args.length(), 1);
4566 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4567 HandleScope scope;
4568 Handle<JSObject> object(raw_object);
4569 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
4570 LOCAL_ONLY);
4571 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
4572 // property array and since the result is mutable we have to create
4573 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004574 int length = contents->length();
4575 Handle<FixedArray> copy = Factory::NewFixedArray(length);
4576 for (int i = 0; i < length; i++) {
4577 Object* entry = contents->get(i);
4578 if (entry->IsString()) {
4579 copy->set(i, entry);
4580 } else {
4581 ASSERT(entry->IsNumber());
4582 HandleScope scope;
4583 Handle<Object> entry_handle(entry);
4584 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
4585 copy->set(i, *entry_str);
4586 }
4587 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004588 return *Factory::NewJSArrayWithElements(copy);
4589}
4590
4591
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004592static Object* Runtime_GetArgumentsProperty(Arguments args) {
4593 NoHandleAllocation ha;
4594 ASSERT(args.length() == 1);
4595
4596 // Compute the frame holding the arguments.
4597 JavaScriptFrameIterator it;
4598 it.AdvanceToArgumentsFrame();
4599 JavaScriptFrame* frame = it.frame();
4600
4601 // Get the actual number of provided arguments.
4602 const uint32_t n = frame->GetProvidedParametersCount();
4603
4604 // Try to convert the key to an index. If successful and within
4605 // index return the the argument from the frame.
4606 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004607 if (args[0]->ToArrayIndex(&index) && index < n) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004608 return frame->GetParameter(index);
4609 }
4610
4611 // Convert the key to a string.
4612 HandleScope scope;
4613 bool exception = false;
4614 Handle<Object> converted =
4615 Execution::ToString(args.at<Object>(0), &exception);
4616 if (exception) return Failure::Exception();
4617 Handle<String> key = Handle<String>::cast(converted);
4618
4619 // Try to convert the string key into an array index.
4620 if (key->AsArrayIndex(&index)) {
4621 if (index < n) {
4622 return frame->GetParameter(index);
4623 } else {
4624 return Top::initial_object_prototype()->GetElement(index);
4625 }
4626 }
4627
4628 // Handle special arguments properties.
4629 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
4630 if (key->Equals(Heap::callee_symbol())) return frame->function();
4631
4632 // Lookup in the initial Object.prototype object.
4633 return Top::initial_object_prototype()->GetProperty(*key);
4634}
4635
4636
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004637static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004638 HandleScope scope;
4639
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004640 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004641 Handle<Object> object = args.at<Object>(0);
4642 if (object->IsJSObject()) {
4643 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00004644 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
4645 js_object->TransformToFastProperties(0);
4646 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004647 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004648 return *object;
4649}
4650
4651
4652static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004653 HandleScope scope;
4654
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004655 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004656 Handle<Object> object = args.at<Object>(0);
4657 if (object->IsJSObject()) {
4658 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004659 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004660 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004661 return *object;
4662}
4663
4664
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004665static Object* Runtime_ToBool(Arguments args) {
4666 NoHandleAllocation ha;
4667 ASSERT(args.length() == 1);
4668
4669 return args[0]->ToBoolean();
4670}
4671
4672
4673// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
4674// Possible optimizations: put the type string into the oddballs.
4675static Object* Runtime_Typeof(Arguments args) {
4676 NoHandleAllocation ha;
4677
4678 Object* obj = args[0];
4679 if (obj->IsNumber()) return Heap::number_symbol();
4680 HeapObject* heap_obj = HeapObject::cast(obj);
4681
4682 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004683 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004684
4685 InstanceType instance_type = heap_obj->map()->instance_type();
4686 if (instance_type < FIRST_NONSTRING_TYPE) {
4687 return Heap::string_symbol();
4688 }
4689
4690 switch (instance_type) {
4691 case ODDBALL_TYPE:
4692 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
4693 return Heap::boolean_symbol();
4694 }
4695 if (heap_obj->IsNull()) {
4696 return Heap::object_symbol();
4697 }
4698 ASSERT(heap_obj->IsUndefined());
4699 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004700 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004701 return Heap::function_symbol();
4702 default:
4703 // For any kind of object not handled above, the spec rule for
4704 // host objects gives that it is okay to return "object"
4705 return Heap::object_symbol();
4706 }
4707}
4708
4709
lrn@chromium.org25156de2010-04-06 13:10:27 +00004710static bool AreDigits(const char*s, int from, int to) {
4711 for (int i = from; i < to; i++) {
4712 if (s[i] < '0' || s[i] > '9') return false;
4713 }
4714
4715 return true;
4716}
4717
4718
4719static int ParseDecimalInteger(const char*s, int from, int to) {
4720 ASSERT(to - from < 10); // Overflow is not possible.
4721 ASSERT(from < to);
4722 int d = s[from] - '0';
4723
4724 for (int i = from + 1; i < to; i++) {
4725 d = 10 * d + (s[i] - '0');
4726 }
4727
4728 return d;
4729}
4730
4731
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004732static Object* Runtime_StringToNumber(Arguments args) {
4733 NoHandleAllocation ha;
4734 ASSERT(args.length() == 1);
4735 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004736 subject->TryFlatten();
lrn@chromium.org25156de2010-04-06 13:10:27 +00004737
4738 // Fast case: short integer or some sorts of junk values.
4739 int len = subject->length();
4740 if (subject->IsSeqAsciiString()) {
4741 if (len == 0) return Smi::FromInt(0);
4742
4743 char const* data = SeqAsciiString::cast(subject)->GetChars();
4744 bool minus = (data[0] == '-');
4745 int start_pos = (minus ? 1 : 0);
4746
4747 if (start_pos == len) {
4748 return Heap::nan_value();
4749 } else if (data[start_pos] > '9') {
4750 // Fast check for a junk value. A valid string may start from a
4751 // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit or
4752 // the 'I' character ('Infinity'). All of that have codes not greater than
4753 // '9' except 'I'.
4754 if (data[start_pos] != 'I') {
4755 return Heap::nan_value();
4756 }
4757 } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
4758 // The maximal/minimal smi has 10 digits. If the string has less digits we
4759 // know it will fit into the smi-data type.
4760 int d = ParseDecimalInteger(data, start_pos, len);
4761 if (minus) {
4762 if (d == 0) return Heap::minus_zero_value();
4763 d = -d;
4764 }
4765 return Smi::FromInt(d);
4766 }
4767 }
4768
4769 // Slower case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004770 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
4771}
4772
4773
4774static Object* Runtime_StringFromCharCodeArray(Arguments args) {
4775 NoHandleAllocation ha;
4776 ASSERT(args.length() == 1);
4777
4778 CONVERT_CHECKED(JSArray, codes, args[0]);
4779 int length = Smi::cast(codes->length())->value();
4780
4781 // Check if the string can be ASCII.
4782 int i;
4783 for (i = 0; i < length; i++) {
4784 Object* element = codes->GetElement(i);
4785 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
4786 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
4787 break;
4788 }
4789
4790 Object* object = NULL;
4791 if (i == length) { // The string is ASCII.
4792 object = Heap::AllocateRawAsciiString(length);
4793 } else { // The string is not ASCII.
4794 object = Heap::AllocateRawTwoByteString(length);
4795 }
4796
4797 if (object->IsFailure()) return object;
4798 String* result = String::cast(object);
4799 for (int i = 0; i < length; i++) {
4800 Object* element = codes->GetElement(i);
4801 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004802 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004803 }
4804 return result;
4805}
4806
4807
4808// kNotEscaped is generated by the following:
4809//
4810// #!/bin/perl
4811// for (my $i = 0; $i < 256; $i++) {
4812// print "\n" if $i % 16 == 0;
4813// my $c = chr($i);
4814// my $escaped = 1;
4815// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
4816// print $escaped ? "0, " : "1, ";
4817// }
4818
4819
4820static bool IsNotEscaped(uint16_t character) {
4821 // Only for 8 bit characters, the rest are always escaped (in a different way)
4822 ASSERT(character < 256);
4823 static const char kNotEscaped[256] = {
4824 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4825 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4826 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
4827 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
4828 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4829 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
4830 0, 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, 0,
4832 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4833 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4834 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4835 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4836 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4837 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4838 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4839 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4840 };
4841 return kNotEscaped[character] != 0;
4842}
4843
4844
4845static Object* Runtime_URIEscape(Arguments args) {
4846 const char hex_chars[] = "0123456789ABCDEF";
4847 NoHandleAllocation ha;
4848 ASSERT(args.length() == 1);
4849 CONVERT_CHECKED(String, source, args[0]);
4850
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004851 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004852
4853 int escaped_length = 0;
4854 int length = source->length();
4855 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004856 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004857 buffer->Reset(source);
4858 while (buffer->has_more()) {
4859 uint16_t character = buffer->GetNext();
4860 if (character >= 256) {
4861 escaped_length += 6;
4862 } else if (IsNotEscaped(character)) {
4863 escaped_length++;
4864 } else {
4865 escaped_length += 3;
4866 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004867 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004868 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004869 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004870 Top::context()->mark_out_of_memory();
4871 return Failure::OutOfMemoryException();
4872 }
4873 }
4874 }
4875 // No length change implies no change. Return original string if no change.
4876 if (escaped_length == length) {
4877 return source;
4878 }
4879 Object* o = Heap::AllocateRawAsciiString(escaped_length);
4880 if (o->IsFailure()) return o;
4881 String* destination = String::cast(o);
4882 int dest_position = 0;
4883
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004884 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004885 buffer->Rewind();
4886 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00004887 uint16_t chr = buffer->GetNext();
4888 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004889 destination->Set(dest_position, '%');
4890 destination->Set(dest_position+1, 'u');
4891 destination->Set(dest_position+2, hex_chars[chr >> 12]);
4892 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
4893 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
4894 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004895 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00004896 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004897 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004898 dest_position++;
4899 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004900 destination->Set(dest_position, '%');
4901 destination->Set(dest_position+1, hex_chars[chr >> 4]);
4902 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004903 dest_position += 3;
4904 }
4905 }
4906 return destination;
4907}
4908
4909
4910static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
4911 static const signed char kHexValue['g'] = {
4912 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4913 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4914 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4915 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
4916 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4917 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4918 -1, 10, 11, 12, 13, 14, 15 };
4919
4920 if (character1 > 'f') return -1;
4921 int hi = kHexValue[character1];
4922 if (hi == -1) return -1;
4923 if (character2 > 'f') return -1;
4924 int lo = kHexValue[character2];
4925 if (lo == -1) return -1;
4926 return (hi << 4) + lo;
4927}
4928
4929
ager@chromium.org870a0b62008-11-04 11:43:05 +00004930static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004931 int i,
4932 int length,
4933 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004934 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00004935 int32_t hi = 0;
4936 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004937 if (character == '%' &&
4938 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004939 source->Get(i + 1) == 'u' &&
4940 (hi = TwoDigitHex(source->Get(i + 2),
4941 source->Get(i + 3))) != -1 &&
4942 (lo = TwoDigitHex(source->Get(i + 4),
4943 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004944 *step = 6;
4945 return (hi << 8) + lo;
4946 } else if (character == '%' &&
4947 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004948 (lo = TwoDigitHex(source->Get(i + 1),
4949 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004950 *step = 3;
4951 return lo;
4952 } else {
4953 *step = 1;
4954 return character;
4955 }
4956}
4957
4958
4959static Object* Runtime_URIUnescape(Arguments args) {
4960 NoHandleAllocation ha;
4961 ASSERT(args.length() == 1);
4962 CONVERT_CHECKED(String, source, args[0]);
4963
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004964 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004965
4966 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004967 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004968
4969 int unescaped_length = 0;
4970 for (int i = 0; i < length; unescaped_length++) {
4971 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004972 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004973 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004974 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004975 i += step;
4976 }
4977
4978 // No length change implies no change. Return original string if no change.
4979 if (unescaped_length == length)
4980 return source;
4981
4982 Object* o = ascii ?
4983 Heap::AllocateRawAsciiString(unescaped_length) :
4984 Heap::AllocateRawTwoByteString(unescaped_length);
4985 if (o->IsFailure()) return o;
4986 String* destination = String::cast(o);
4987
4988 int dest_position = 0;
4989 for (int i = 0; i < length; dest_position++) {
4990 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004991 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004992 i += step;
4993 }
4994 return destination;
4995}
4996
4997
4998static Object* Runtime_StringParseInt(Arguments args) {
4999 NoHandleAllocation ha;
5000
5001 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005002 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005003
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005004 s->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005005
lrn@chromium.org25156de2010-04-06 13:10:27 +00005006 RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
5007 double value = StringToInt(s, radix);
5008 return Heap::NumberFromDouble(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005009 return Heap::nan_value();
5010}
5011
5012
5013static Object* Runtime_StringParseFloat(Arguments args) {
5014 NoHandleAllocation ha;
5015 CONVERT_CHECKED(String, str, args[0]);
5016
5017 // ECMA-262 section 15.1.2.3, empty string is NaN
5018 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
5019
5020 // Create a number object from the value.
5021 return Heap::NumberFromDouble(value);
5022}
5023
5024
5025static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
5026static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
5027
5028
5029template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005030static Object* ConvertCaseHelper(String* s,
5031 int length,
5032 int input_string_length,
5033 unibrow::Mapping<Converter, 128>* mapping) {
5034 // We try this twice, once with the assumption that the result is no longer
5035 // than the input and, if that assumption breaks, again with the exact
5036 // length. This may not be pretty, but it is nicer than what was here before
5037 // and I hereby claim my vaffel-is.
5038 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005039 // Allocate the resulting string.
5040 //
5041 // NOTE: This assumes that the upper/lower case of an ascii
5042 // character is also ascii. This is currently the case, but it
5043 // might break in the future if we implement more context and locale
5044 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00005045 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005046 ? Heap::AllocateRawAsciiString(length)
5047 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005048 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005049 String* result = String::cast(o);
5050 bool has_changed_character = false;
5051
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005052 // Convert all characters to upper case, assuming that they will fit
5053 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005054 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005055 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005056 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005057 // We can assume that the string is not empty
5058 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005059 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00005060 bool has_next = buffer->has_more();
5061 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005062 int char_length = mapping->get(current, next, chars);
5063 if (char_length == 0) {
5064 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005065 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005066 i++;
5067 } else if (char_length == 1) {
5068 // Common case: converting the letter resulted in one character.
5069 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005070 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005071 has_changed_character = true;
5072 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005073 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005074 // We've assumed that the result would be as long as the
5075 // input but here is a character that converts to several
5076 // characters. No matter, we calculate the exact length
5077 // of the result and try the whole thing again.
5078 //
5079 // Note that this leaves room for optimization. We could just
5080 // memcpy what we already have to the result string. Also,
5081 // the result string is the last object allocated we could
5082 // "realloc" it and probably, in the vast majority of cases,
5083 // extend the existing string to be able to hold the full
5084 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00005085 int next_length = 0;
5086 if (has_next) {
5087 next_length = mapping->get(next, 0, chars);
5088 if (next_length == 0) next_length = 1;
5089 }
5090 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005091 while (buffer->has_more()) {
5092 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00005093 // NOTE: we use 0 as the next character here because, while
5094 // the next character may affect what a character converts to,
5095 // it does not in any case affect the length of what it convert
5096 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005097 int char_length = mapping->get(current, 0, chars);
5098 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00005099 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005100 if (current_length > Smi::kMaxValue) {
5101 Top::context()->mark_out_of_memory();
5102 return Failure::OutOfMemoryException();
5103 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005104 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005105 // Try again with the real length.
5106 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005107 } else {
5108 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005109 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005110 i++;
5111 }
5112 has_changed_character = true;
5113 }
5114 current = next;
5115 }
5116 if (has_changed_character) {
5117 return result;
5118 } else {
5119 // If we didn't actually change anything in doing the conversion
5120 // we simple return the result and let the converted string
5121 // become garbage; there is no reason to keep two identical strings
5122 // alive.
5123 return s;
5124 }
5125}
5126
5127
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005128namespace {
5129
5130struct ToLowerTraits {
5131 typedef unibrow::ToLowercase UnibrowConverter;
5132
5133 static bool ConvertAscii(char* dst, char* src, int length) {
5134 bool changed = false;
5135 for (int i = 0; i < length; ++i) {
5136 char c = src[i];
5137 if ('A' <= c && c <= 'Z') {
5138 c += ('a' - 'A');
5139 changed = true;
5140 }
5141 dst[i] = c;
5142 }
5143 return changed;
5144 }
5145};
5146
5147
5148struct ToUpperTraits {
5149 typedef unibrow::ToUppercase UnibrowConverter;
5150
5151 static bool ConvertAscii(char* dst, char* src, int length) {
5152 bool changed = false;
5153 for (int i = 0; i < length; ++i) {
5154 char c = src[i];
5155 if ('a' <= c && c <= 'z') {
5156 c -= ('a' - 'A');
5157 changed = true;
5158 }
5159 dst[i] = c;
5160 }
5161 return changed;
5162 }
5163};
5164
5165} // namespace
5166
5167
5168template <typename ConvertTraits>
5169static Object* ConvertCase(
5170 Arguments args,
5171 unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005172 NoHandleAllocation ha;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005173 CONVERT_CHECKED(String, s, args[0]);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005174 s = s->TryFlattenGetString();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005175
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005176 const int length = s->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005177 // Assume that the string is not empty; we need this assumption later
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005178 if (length == 0) return s;
5179
5180 // Simpler handling of ascii strings.
5181 //
5182 // NOTE: This assumes that the upper/lower case of an ascii
5183 // character is also ascii. This is currently the case, but it
5184 // might break in the future if we implement more context and locale
5185 // dependent upper/lower conversions.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005186 if (s->IsSeqAsciiString()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005187 Object* o = Heap::AllocateRawAsciiString(length);
5188 if (o->IsFailure()) return o;
5189 SeqAsciiString* result = SeqAsciiString::cast(o);
5190 bool has_changed_character = ConvertTraits::ConvertAscii(
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005191 result->GetChars(), SeqAsciiString::cast(s)->GetChars(), length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005192 return has_changed_character ? result : s;
5193 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005194
5195 Object* answer = ConvertCaseHelper(s, length, length, mapping);
5196 if (answer->IsSmi()) {
5197 // Retry with correct length.
5198 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
5199 }
5200 return answer; // This may be a failure.
5201}
5202
5203
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005204static Object* Runtime_StringToLowerCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005205 return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005206}
5207
5208
5209static Object* Runtime_StringToUpperCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005210 return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005211}
5212
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005213
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005214static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
5215 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
5216}
5217
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005218
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005219static Object* Runtime_StringTrim(Arguments args) {
5220 NoHandleAllocation ha;
5221 ASSERT(args.length() == 3);
5222
5223 CONVERT_CHECKED(String, s, args[0]);
5224 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
5225 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
5226
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005227 s->TryFlatten();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005228 int length = s->length();
5229
5230 int left = 0;
5231 if (trimLeft) {
5232 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
5233 left++;
5234 }
5235 }
5236
5237 int right = length;
5238 if (trimRight) {
5239 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
5240 right--;
5241 }
5242 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005243 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005244}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005245
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005246
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005247template <typename schar, typename pchar>
5248void FindStringIndices(Vector<const schar> subject,
5249 Vector<const pchar> pattern,
5250 ZoneList<int>* indices,
5251 unsigned int limit) {
5252 ASSERT(limit > 0);
5253 // Collect indices of pattern in subject, and the end-of-string index.
5254 // Stop after finding at most limit values.
5255 StringSearchStrategy strategy =
5256 InitializeStringSearch(pattern, sizeof(schar) == 1);
5257 switch (strategy) {
5258 case SEARCH_FAIL: return;
5259 case SEARCH_SHORT: {
5260 int pattern_length = pattern.length();
5261 int index = 0;
5262 while (limit > 0) {
5263 index = SimpleIndexOf(subject, pattern, index);
5264 if (index < 0) return;
5265 indices->Add(index);
5266 index += pattern_length;
5267 limit--;
5268 }
5269 return;
5270 }
5271 case SEARCH_LONG: {
5272 int pattern_length = pattern.length();
5273 int index = 0;
5274 while (limit > 0) {
5275 index = ComplexIndexOf(subject, pattern, index);
5276 if (index < 0) return;
5277 indices->Add(index);
5278 index += pattern_length;
5279 limit--;
5280 }
5281 return;
5282 }
5283 default:
5284 UNREACHABLE();
5285 return;
5286 }
5287}
5288
5289template <typename schar>
5290inline void FindCharIndices(Vector<const schar> subject,
5291 const schar pattern_char,
5292 ZoneList<int>* indices,
5293 unsigned int limit) {
5294 // Collect indices of pattern_char in subject, and the end-of-string index.
5295 // Stop after finding at most limit values.
5296 int index = 0;
5297 while (limit > 0) {
5298 index = SingleCharIndexOf(subject, pattern_char, index);
5299 if (index < 0) return;
5300 indices->Add(index);
5301 index++;
5302 limit--;
5303 }
5304}
5305
5306
5307static Object* Runtime_StringSplit(Arguments args) {
5308 ASSERT(args.length() == 3);
5309 HandleScope handle_scope;
5310 CONVERT_ARG_CHECKED(String, subject, 0);
5311 CONVERT_ARG_CHECKED(String, pattern, 1);
5312 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
5313
5314 int subject_length = subject->length();
5315 int pattern_length = pattern->length();
5316 RUNTIME_ASSERT(pattern_length > 0);
5317
5318 // The limit can be very large (0xffffffffu), but since the pattern
5319 // isn't empty, we can never create more parts than ~half the length
5320 // of the subject.
5321
5322 if (!subject->IsFlat()) FlattenString(subject);
5323
5324 static const int kMaxInitialListCapacity = 16;
5325
5326 ZoneScope scope(DELETE_ON_EXIT);
5327
5328 // Find (up to limit) indices of separator and end-of-string in subject
5329 int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
5330 ZoneList<int> indices(initial_capacity);
5331 if (pattern_length == 1) {
5332 // Special case, go directly to fast single-character split.
5333 AssertNoAllocation nogc;
5334 uc16 pattern_char = pattern->Get(0);
5335 if (subject->IsTwoByteRepresentation()) {
5336 FindCharIndices(subject->ToUC16Vector(), pattern_char,
5337 &indices,
5338 limit);
5339 } else if (pattern_char <= String::kMaxAsciiCharCode) {
5340 FindCharIndices(subject->ToAsciiVector(),
5341 static_cast<char>(pattern_char),
5342 &indices,
5343 limit);
5344 }
5345 } else {
5346 if (!pattern->IsFlat()) FlattenString(pattern);
5347 AssertNoAllocation nogc;
5348 if (subject->IsAsciiRepresentation()) {
5349 Vector<const char> subject_vector = subject->ToAsciiVector();
5350 if (pattern->IsAsciiRepresentation()) {
5351 FindStringIndices(subject_vector,
5352 pattern->ToAsciiVector(),
5353 &indices,
5354 limit);
5355 } else {
5356 FindStringIndices(subject_vector,
5357 pattern->ToUC16Vector(),
5358 &indices,
5359 limit);
5360 }
5361 } else {
5362 Vector<const uc16> subject_vector = subject->ToUC16Vector();
5363 if (pattern->IsAsciiRepresentation()) {
5364 FindStringIndices(subject_vector,
5365 pattern->ToAsciiVector(),
5366 &indices,
5367 limit);
5368 } else {
5369 FindStringIndices(subject_vector,
5370 pattern->ToUC16Vector(),
5371 &indices,
5372 limit);
5373 }
5374 }
5375 }
5376 if (static_cast<uint32_t>(indices.length()) < limit) {
5377 indices.Add(subject_length);
5378 }
5379 // The list indices now contains the end of each part to create.
5380
5381
5382 // Create JSArray of substrings separated by separator.
5383 int part_count = indices.length();
5384
5385 Handle<JSArray> result = Factory::NewJSArray(part_count);
5386 result->set_length(Smi::FromInt(part_count));
5387
5388 ASSERT(result->HasFastElements());
5389
5390 if (part_count == 1 && indices.at(0) == subject_length) {
5391 FixedArray::cast(result->elements())->set(0, *subject);
5392 return *result;
5393 }
5394
5395 Handle<FixedArray> elements(FixedArray::cast(result->elements()));
5396 int part_start = 0;
5397 for (int i = 0; i < part_count; i++) {
5398 HandleScope local_loop_handle;
5399 int part_end = indices.at(i);
5400 Handle<String> substring =
5401 Factory::NewSubString(subject, part_start, part_end);
5402 elements->set(i, *substring);
5403 part_start = part_end + pattern_length;
5404 }
5405
5406 return *result;
5407}
5408
5409
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005410// Copies ascii characters to the given fixed array looking up
5411// one-char strings in the cache. Gives up on the first char that is
5412// not in the cache and fills the remainder with smi zeros. Returns
5413// the length of the successfully copied prefix.
5414static int CopyCachedAsciiCharsToArray(const char* chars,
5415 FixedArray* elements,
5416 int length) {
5417 AssertNoAllocation nogc;
5418 FixedArray* ascii_cache = Heap::single_character_string_cache();
5419 Object* undefined = Heap::undefined_value();
5420 int i;
5421 for (i = 0; i < length; ++i) {
5422 Object* value = ascii_cache->get(chars[i]);
5423 if (value == undefined) break;
5424 ASSERT(!Heap::InNewSpace(value));
5425 elements->set(i, value, SKIP_WRITE_BARRIER);
5426 }
5427 if (i < length) {
5428 ASSERT(Smi::FromInt(0) == 0);
5429 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
5430 }
5431#ifdef DEBUG
5432 for (int j = 0; j < length; ++j) {
5433 Object* element = elements->get(j);
5434 ASSERT(element == Smi::FromInt(0) ||
5435 (element->IsString() && String::cast(element)->LooksValid()));
5436 }
5437#endif
5438 return i;
5439}
5440
5441
5442// Converts a String to JSArray.
5443// For example, "foo" => ["f", "o", "o"].
5444static Object* Runtime_StringToArray(Arguments args) {
5445 HandleScope scope;
5446 ASSERT(args.length() == 1);
5447 CONVERT_ARG_CHECKED(String, s, 0);
5448
5449 s->TryFlatten();
5450 const int length = s->length();
5451
5452 Handle<FixedArray> elements;
5453 if (s->IsFlat() && s->IsAsciiRepresentation()) {
5454 Object* obj = Heap::AllocateUninitializedFixedArray(length);
5455 if (obj->IsFailure()) return obj;
5456 elements = Handle<FixedArray>(FixedArray::cast(obj));
5457
5458 Vector<const char> chars = s->ToAsciiVector();
5459 // Note, this will initialize all elements (not only the prefix)
5460 // to prevent GC from seeing partially initialized array.
5461 int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
5462 *elements,
5463 length);
5464
5465 for (int i = num_copied_from_cache; i < length; ++i) {
5466 elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
5467 }
5468 } else {
5469 elements = Factory::NewFixedArray(length);
5470 for (int i = 0; i < length; ++i) {
5471 elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
5472 }
5473 }
5474
5475#ifdef DEBUG
5476 for (int i = 0; i < length; ++i) {
5477 ASSERT(String::cast(elements->get(i))->length() == 1);
5478 }
5479#endif
5480
5481 return *Factory::NewJSArrayWithElements(elements);
5482}
5483
5484
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00005485bool Runtime::IsUpperCaseChar(uint16_t ch) {
5486 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
5487 int char_length = to_upper_mapping.get(ch, 0, chars);
5488 return char_length == 0;
5489}
5490
5491
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005492static Object* Runtime_NumberToString(Arguments args) {
5493 NoHandleAllocation ha;
5494 ASSERT(args.length() == 1);
5495
5496 Object* number = args[0];
5497 RUNTIME_ASSERT(number->IsNumber());
5498
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005499 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005500}
5501
5502
ager@chromium.org357bf652010-04-12 11:30:10 +00005503static Object* Runtime_NumberToStringSkipCache(Arguments args) {
5504 NoHandleAllocation ha;
5505 ASSERT(args.length() == 1);
5506
5507 Object* number = args[0];
5508 RUNTIME_ASSERT(number->IsNumber());
5509
5510 return Heap::NumberToString(number, false);
5511}
5512
5513
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005514static Object* Runtime_NumberToInteger(Arguments args) {
5515 NoHandleAllocation ha;
5516 ASSERT(args.length() == 1);
5517
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005518 CONVERT_DOUBLE_CHECKED(number, args[0]);
5519
5520 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5521 if (number > 0 && number <= Smi::kMaxValue) {
5522 return Smi::FromInt(static_cast<int>(number));
5523 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005524 return Heap::NumberFromDouble(DoubleToInteger(number));
5525}
5526
5527
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005528static Object* Runtime_NumberToIntegerMapMinusZero(Arguments args) {
5529 NoHandleAllocation ha;
5530 ASSERT(args.length() == 1);
5531
5532 CONVERT_DOUBLE_CHECKED(number, args[0]);
5533
5534 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5535 if (number > 0 && number <= Smi::kMaxValue) {
5536 return Smi::FromInt(static_cast<int>(number));
5537 }
5538
5539 double double_value = DoubleToInteger(number);
5540 // Map both -0 and +0 to +0.
5541 if (double_value == 0) double_value = 0;
5542
5543 return Heap::NumberFromDouble(double_value);
5544}
5545
5546
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005547static Object* Runtime_NumberToJSUint32(Arguments args) {
5548 NoHandleAllocation ha;
5549 ASSERT(args.length() == 1);
5550
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005551 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005552 return Heap::NumberFromUint32(number);
5553}
5554
5555
5556static Object* Runtime_NumberToJSInt32(Arguments args) {
5557 NoHandleAllocation ha;
5558 ASSERT(args.length() == 1);
5559
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005560 CONVERT_DOUBLE_CHECKED(number, args[0]);
5561
5562 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5563 if (number > 0 && number <= Smi::kMaxValue) {
5564 return Smi::FromInt(static_cast<int>(number));
5565 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005566 return Heap::NumberFromInt32(DoubleToInt32(number));
5567}
5568
5569
ager@chromium.org870a0b62008-11-04 11:43:05 +00005570// Converts a Number to a Smi, if possible. Returns NaN if the number is not
5571// a small integer.
5572static Object* Runtime_NumberToSmi(Arguments args) {
5573 NoHandleAllocation ha;
5574 ASSERT(args.length() == 1);
5575
5576 Object* obj = args[0];
5577 if (obj->IsSmi()) {
5578 return obj;
5579 }
5580 if (obj->IsHeapNumber()) {
5581 double value = HeapNumber::cast(obj)->value();
5582 int int_value = FastD2I(value);
5583 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
5584 return Smi::FromInt(int_value);
5585 }
5586 }
5587 return Heap::nan_value();
5588}
5589
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005590
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005591static Object* Runtime_NumberAdd(Arguments args) {
5592 NoHandleAllocation ha;
5593 ASSERT(args.length() == 2);
5594
5595 CONVERT_DOUBLE_CHECKED(x, args[0]);
5596 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005597 return Heap::NumberFromDouble(x + y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005598}
5599
5600
5601static Object* Runtime_NumberSub(Arguments args) {
5602 NoHandleAllocation ha;
5603 ASSERT(args.length() == 2);
5604
5605 CONVERT_DOUBLE_CHECKED(x, args[0]);
5606 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005607 return Heap::NumberFromDouble(x - y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005608}
5609
5610
5611static Object* Runtime_NumberMul(Arguments args) {
5612 NoHandleAllocation ha;
5613 ASSERT(args.length() == 2);
5614
5615 CONVERT_DOUBLE_CHECKED(x, args[0]);
5616 CONVERT_DOUBLE_CHECKED(y, args[1]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005617 return Heap::NumberFromDouble(x * y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005618}
5619
5620
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005621static Object* Runtime_NumberUnaryMinus(Arguments args) {
5622 NoHandleAllocation ha;
5623 ASSERT(args.length() == 1);
5624
5625 CONVERT_DOUBLE_CHECKED(x, args[0]);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005626 return Heap::NumberFromDouble(-x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005627}
5628
5629
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00005630static Object* Runtime_NumberAlloc(Arguments args) {
5631 NoHandleAllocation ha;
5632 ASSERT(args.length() == 0);
5633
5634 return Heap::NumberFromDouble(9876543210.0);
5635}
5636
5637
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005638static Object* Runtime_NumberDiv(Arguments args) {
5639 NoHandleAllocation ha;
5640 ASSERT(args.length() == 2);
5641
5642 CONVERT_DOUBLE_CHECKED(x, args[0]);
5643 CONVERT_DOUBLE_CHECKED(y, args[1]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005644 return Heap::NumberFromDouble(x / y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005645}
5646
5647
5648static Object* Runtime_NumberMod(Arguments args) {
5649 NoHandleAllocation ha;
5650 ASSERT(args.length() == 2);
5651
5652 CONVERT_DOUBLE_CHECKED(x, args[0]);
5653 CONVERT_DOUBLE_CHECKED(y, args[1]);
5654
ager@chromium.org3811b432009-10-28 14:53:37 +00005655 x = modulo(x, y);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005656 // NumberFromDouble may return a Smi instead of a Number object
5657 return Heap::NumberFromDouble(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005658}
5659
5660
5661static Object* Runtime_StringAdd(Arguments args) {
5662 NoHandleAllocation ha;
5663 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005664 CONVERT_CHECKED(String, str1, args[0]);
5665 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005666 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00005667 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005668}
5669
5670
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005671template <typename sinkchar>
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005672static inline void StringBuilderConcatHelper(String* special,
5673 sinkchar* sink,
5674 FixedArray* fixed_array,
5675 int array_length) {
5676 int position = 0;
5677 for (int i = 0; i < array_length; i++) {
5678 Object* element = fixed_array->get(i);
5679 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005680 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005681 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005682 int pos;
5683 int len;
5684 if (encoded_slice > 0) {
5685 // Position and length encoded in one smi.
5686 pos = StringBuilderSubstringPosition::decode(encoded_slice);
5687 len = StringBuilderSubstringLength::decode(encoded_slice);
5688 } else {
5689 // Position and length encoded in two smis.
5690 Object* obj = fixed_array->get(++i);
5691 ASSERT(obj->IsSmi());
5692 pos = Smi::cast(obj)->value();
5693 len = -encoded_slice;
5694 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00005695 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00005696 sink + position,
5697 pos,
5698 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005699 position += len;
5700 } else {
5701 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005702 int element_length = string->length();
5703 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005704 position += element_length;
5705 }
5706 }
5707}
5708
5709
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005710static Object* Runtime_StringBuilderConcat(Arguments args) {
5711 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005712 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005713 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005714 if (!args[1]->IsSmi()) {
5715 Top::context()->mark_out_of_memory();
5716 return Failure::OutOfMemoryException();
5717 }
5718 int array_length = Smi::cast(args[1])->value();
5719 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005720
5721 // This assumption is used by the slice encoding in one or two smis.
5722 ASSERT(Smi::kMaxValue >= String::kMaxLength);
5723
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005724 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005725 if (!array->HasFastElements()) {
5726 return Top::Throw(Heap::illegal_argument_symbol());
5727 }
5728 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005729 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005730 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005731 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005732
5733 if (array_length == 0) {
5734 return Heap::empty_string();
5735 } else if (array_length == 1) {
5736 Object* first = fixed_array->get(0);
5737 if (first->IsString()) return first;
5738 }
5739
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005740 bool ascii = special->HasOnlyAsciiChars();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005741 int position = 0;
5742 for (int i = 0; i < array_length; i++) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005743 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005744 Object* elt = fixed_array->get(i);
5745 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005746 // Smi encoding of position and length.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005747 int smi_value = Smi::cast(elt)->value();
5748 int pos;
5749 int len;
5750 if (smi_value > 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005751 // Position and length encoded in one smi.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005752 pos = StringBuilderSubstringPosition::decode(smi_value);
5753 len = StringBuilderSubstringLength::decode(smi_value);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005754 } else {
5755 // Position and length encoded in two smis.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005756 len = -smi_value;
5757 // Get the position and check that it is a positive smi.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005758 i++;
5759 if (i >= array_length) {
5760 return Top::Throw(Heap::illegal_argument_symbol());
5761 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005762 Object* next_smi = fixed_array->get(i);
5763 if (!next_smi->IsSmi()) {
5764 return Top::Throw(Heap::illegal_argument_symbol());
5765 }
5766 pos = Smi::cast(next_smi)->value();
5767 if (pos < 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005768 return Top::Throw(Heap::illegal_argument_symbol());
5769 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005770 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005771 ASSERT(pos >= 0);
5772 ASSERT(len >= 0);
5773 if (pos > special_length || len > special_length - pos) {
5774 return Top::Throw(Heap::illegal_argument_symbol());
5775 }
5776 increment = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005777 } else if (elt->IsString()) {
5778 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005779 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005780 increment = element_length;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005781 if (ascii && !element->HasOnlyAsciiChars()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005782 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005783 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005784 } else {
5785 return Top::Throw(Heap::illegal_argument_symbol());
5786 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005787 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005788 Top::context()->mark_out_of_memory();
5789 return Failure::OutOfMemoryException();
5790 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005791 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005792 }
5793
5794 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005795 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005796
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005797 if (ascii) {
5798 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005799 if (object->IsFailure()) return object;
5800 SeqAsciiString* answer = SeqAsciiString::cast(object);
5801 StringBuilderConcatHelper(special,
5802 answer->GetChars(),
5803 fixed_array,
5804 array_length);
5805 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005806 } else {
5807 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005808 if (object->IsFailure()) return object;
5809 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
5810 StringBuilderConcatHelper(special,
5811 answer->GetChars(),
5812 fixed_array,
5813 array_length);
5814 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005815 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005816}
5817
5818
5819static Object* Runtime_NumberOr(Arguments args) {
5820 NoHandleAllocation ha;
5821 ASSERT(args.length() == 2);
5822
5823 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5824 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5825 return Heap::NumberFromInt32(x | y);
5826}
5827
5828
5829static Object* Runtime_NumberAnd(Arguments args) {
5830 NoHandleAllocation ha;
5831 ASSERT(args.length() == 2);
5832
5833 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5834 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5835 return Heap::NumberFromInt32(x & y);
5836}
5837
5838
5839static Object* Runtime_NumberXor(Arguments args) {
5840 NoHandleAllocation ha;
5841 ASSERT(args.length() == 2);
5842
5843 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5844 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5845 return Heap::NumberFromInt32(x ^ y);
5846}
5847
5848
5849static Object* Runtime_NumberNot(Arguments args) {
5850 NoHandleAllocation ha;
5851 ASSERT(args.length() == 1);
5852
5853 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5854 return Heap::NumberFromInt32(~x);
5855}
5856
5857
5858static Object* Runtime_NumberShl(Arguments args) {
5859 NoHandleAllocation ha;
5860 ASSERT(args.length() == 2);
5861
5862 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5863 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5864 return Heap::NumberFromInt32(x << (y & 0x1f));
5865}
5866
5867
5868static Object* Runtime_NumberShr(Arguments args) {
5869 NoHandleAllocation ha;
5870 ASSERT(args.length() == 2);
5871
5872 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
5873 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5874 return Heap::NumberFromUint32(x >> (y & 0x1f));
5875}
5876
5877
5878static Object* Runtime_NumberSar(Arguments args) {
5879 NoHandleAllocation ha;
5880 ASSERT(args.length() == 2);
5881
5882 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5883 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5884 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
5885}
5886
5887
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005888static Object* Runtime_NumberEquals(Arguments args) {
5889 NoHandleAllocation ha;
5890 ASSERT(args.length() == 2);
5891
5892 CONVERT_DOUBLE_CHECKED(x, args[0]);
5893 CONVERT_DOUBLE_CHECKED(y, args[1]);
5894 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
5895 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
5896 if (x == y) return Smi::FromInt(EQUAL);
5897 Object* result;
5898 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
5899 result = Smi::FromInt(EQUAL);
5900 } else {
5901 result = Smi::FromInt(NOT_EQUAL);
5902 }
5903 return result;
5904}
5905
5906
5907static Object* Runtime_StringEquals(Arguments args) {
5908 NoHandleAllocation ha;
5909 ASSERT(args.length() == 2);
5910
5911 CONVERT_CHECKED(String, x, args[0]);
5912 CONVERT_CHECKED(String, y, args[1]);
5913
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005914 bool not_equal = !x->Equals(y);
5915 // This is slightly convoluted because the value that signifies
5916 // equality is 0 and inequality is 1 so we have to negate the result
5917 // from String::Equals.
5918 ASSERT(not_equal == 0 || not_equal == 1);
5919 STATIC_CHECK(EQUAL == 0);
5920 STATIC_CHECK(NOT_EQUAL == 1);
5921 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005922}
5923
5924
5925static Object* Runtime_NumberCompare(Arguments args) {
5926 NoHandleAllocation ha;
5927 ASSERT(args.length() == 3);
5928
5929 CONVERT_DOUBLE_CHECKED(x, args[0]);
5930 CONVERT_DOUBLE_CHECKED(y, args[1]);
5931 if (isnan(x) || isnan(y)) return args[2];
5932 if (x == y) return Smi::FromInt(EQUAL);
5933 if (isless(x, y)) return Smi::FromInt(LESS);
5934 return Smi::FromInt(GREATER);
5935}
5936
5937
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005938// Compare two Smis as if they were converted to strings and then
5939// compared lexicographically.
5940static Object* Runtime_SmiLexicographicCompare(Arguments args) {
5941 NoHandleAllocation ha;
5942 ASSERT(args.length() == 2);
5943
5944 // Arrays for the individual characters of the two Smis. Smis are
5945 // 31 bit integers and 10 decimal digits are therefore enough.
5946 static int x_elms[10];
5947 static int y_elms[10];
5948
5949 // Extract the integer values from the Smis.
5950 CONVERT_CHECKED(Smi, x, args[0]);
5951 CONVERT_CHECKED(Smi, y, args[1]);
5952 int x_value = x->value();
5953 int y_value = y->value();
5954
5955 // If the integers are equal so are the string representations.
5956 if (x_value == y_value) return Smi::FromInt(EQUAL);
5957
5958 // If one of the integers are zero the normal integer order is the
5959 // same as the lexicographic order of the string representations.
5960 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
5961
ager@chromium.org32912102009-01-16 10:38:43 +00005962 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005963 // smallest because the char code of '-' is less than the char code
5964 // of any digit. Otherwise, we make both values positive.
5965 if (x_value < 0 || y_value < 0) {
5966 if (y_value >= 0) return Smi::FromInt(LESS);
5967 if (x_value >= 0) return Smi::FromInt(GREATER);
5968 x_value = -x_value;
5969 y_value = -y_value;
5970 }
5971
5972 // Convert the integers to arrays of their decimal digits.
5973 int x_index = 0;
5974 int y_index = 0;
5975 while (x_value > 0) {
5976 x_elms[x_index++] = x_value % 10;
5977 x_value /= 10;
5978 }
5979 while (y_value > 0) {
5980 y_elms[y_index++] = y_value % 10;
5981 y_value /= 10;
5982 }
5983
5984 // Loop through the arrays of decimal digits finding the first place
5985 // where they differ.
5986 while (--x_index >= 0 && --y_index >= 0) {
5987 int diff = x_elms[x_index] - y_elms[y_index];
5988 if (diff != 0) return Smi::FromInt(diff);
5989 }
5990
5991 // If one array is a suffix of the other array, the longest array is
5992 // the representation of the largest of the Smis in the
5993 // lexicographic ordering.
5994 return Smi::FromInt(x_index - y_index);
5995}
5996
5997
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005998static Object* StringInputBufferCompare(String* x, String* y) {
5999 static StringInputBuffer bufx;
6000 static StringInputBuffer bufy;
6001 bufx.Reset(x);
6002 bufy.Reset(y);
6003 while (bufx.has_more() && bufy.has_more()) {
6004 int d = bufx.GetNext() - bufy.GetNext();
6005 if (d < 0) return Smi::FromInt(LESS);
6006 else if (d > 0) return Smi::FromInt(GREATER);
6007 }
6008
6009 // x is (non-trivial) prefix of y:
6010 if (bufy.has_more()) return Smi::FromInt(LESS);
6011 // y is prefix of x:
6012 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
6013}
6014
6015
6016static Object* FlatStringCompare(String* x, String* y) {
6017 ASSERT(x->IsFlat());
6018 ASSERT(y->IsFlat());
6019 Object* equal_prefix_result = Smi::FromInt(EQUAL);
6020 int prefix_length = x->length();
6021 if (y->length() < prefix_length) {
6022 prefix_length = y->length();
6023 equal_prefix_result = Smi::FromInt(GREATER);
6024 } else if (y->length() > prefix_length) {
6025 equal_prefix_result = Smi::FromInt(LESS);
6026 }
6027 int r;
6028 if (x->IsAsciiRepresentation()) {
6029 Vector<const char> x_chars = x->ToAsciiVector();
6030 if (y->IsAsciiRepresentation()) {
6031 Vector<const char> y_chars = y->ToAsciiVector();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006032 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006033 } else {
6034 Vector<const uc16> y_chars = y->ToUC16Vector();
6035 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
6036 }
6037 } else {
6038 Vector<const uc16> x_chars = x->ToUC16Vector();
6039 if (y->IsAsciiRepresentation()) {
6040 Vector<const char> y_chars = y->ToAsciiVector();
6041 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
6042 } else {
6043 Vector<const uc16> y_chars = y->ToUC16Vector();
6044 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
6045 }
6046 }
6047 Object* result;
6048 if (r == 0) {
6049 result = equal_prefix_result;
6050 } else {
6051 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
6052 }
6053 ASSERT(result == StringInputBufferCompare(x, y));
6054 return result;
6055}
6056
6057
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006058static Object* Runtime_StringCompare(Arguments args) {
6059 NoHandleAllocation ha;
6060 ASSERT(args.length() == 2);
6061
6062 CONVERT_CHECKED(String, x, args[0]);
6063 CONVERT_CHECKED(String, y, args[1]);
6064
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006065 Counters::string_compare_runtime.Increment();
6066
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006067 // A few fast case tests before we flatten.
6068 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006069 if (y->length() == 0) {
6070 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006071 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006072 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006073 return Smi::FromInt(LESS);
6074 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006075
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006076 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006077 if (d < 0) return Smi::FromInt(LESS);
6078 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006079
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006080 Object* obj = Heap::PrepareForCompare(x);
6081 if (obj->IsFailure()) return obj;
6082 obj = Heap::PrepareForCompare(y);
6083 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006084
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006085 return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
6086 : StringInputBufferCompare(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006087}
6088
6089
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006090static Object* Runtime_Math_acos(Arguments args) {
6091 NoHandleAllocation ha;
6092 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006093 Counters::math_acos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006094
6095 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006096 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006097}
6098
6099
6100static Object* Runtime_Math_asin(Arguments args) {
6101 NoHandleAllocation ha;
6102 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006103 Counters::math_asin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006104
6105 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006106 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006107}
6108
6109
6110static Object* Runtime_Math_atan(Arguments args) {
6111 NoHandleAllocation ha;
6112 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006113 Counters::math_atan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006114
6115 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006116 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006117}
6118
6119
6120static Object* Runtime_Math_atan2(Arguments args) {
6121 NoHandleAllocation ha;
6122 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006123 Counters::math_atan2.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006124
6125 CONVERT_DOUBLE_CHECKED(x, args[0]);
6126 CONVERT_DOUBLE_CHECKED(y, args[1]);
6127 double result;
6128 if (isinf(x) && isinf(y)) {
6129 // Make sure that the result in case of two infinite arguments
6130 // is a multiple of Pi / 4. The sign of the result is determined
6131 // by the first argument (x) and the sign of the second argument
6132 // determines the multiplier: one or three.
6133 static double kPiDividedBy4 = 0.78539816339744830962;
6134 int multiplier = (x < 0) ? -1 : 1;
6135 if (y < 0) multiplier *= 3;
6136 result = multiplier * kPiDividedBy4;
6137 } else {
6138 result = atan2(x, y);
6139 }
6140 return Heap::AllocateHeapNumber(result);
6141}
6142
6143
6144static Object* Runtime_Math_ceil(Arguments args) {
6145 NoHandleAllocation ha;
6146 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006147 Counters::math_ceil.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006148
6149 CONVERT_DOUBLE_CHECKED(x, args[0]);
6150 return Heap::NumberFromDouble(ceiling(x));
6151}
6152
6153
6154static Object* Runtime_Math_cos(Arguments args) {
6155 NoHandleAllocation ha;
6156 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006157 Counters::math_cos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006158
6159 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006160 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006161}
6162
6163
6164static Object* Runtime_Math_exp(Arguments args) {
6165 NoHandleAllocation ha;
6166 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006167 Counters::math_exp.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006168
6169 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006170 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006171}
6172
6173
6174static Object* Runtime_Math_floor(Arguments args) {
6175 NoHandleAllocation ha;
6176 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006177 Counters::math_floor.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006178
6179 CONVERT_DOUBLE_CHECKED(x, args[0]);
6180 return Heap::NumberFromDouble(floor(x));
6181}
6182
6183
6184static Object* Runtime_Math_log(Arguments args) {
6185 NoHandleAllocation ha;
6186 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006187 Counters::math_log.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006188
6189 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006190 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006191}
6192
6193
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006194// Helper function to compute x^y, where y is known to be an
6195// integer. Uses binary decomposition to limit the number of
6196// multiplications; see the discussion in "Hacker's Delight" by Henry
6197// S. Warren, Jr., figure 11-6, page 213.
6198static double powi(double x, int y) {
6199 ASSERT(y != kMinInt);
6200 unsigned n = (y < 0) ? -y : y;
6201 double m = x;
6202 double p = 1;
6203 while (true) {
6204 if ((n & 1) != 0) p *= m;
6205 n >>= 1;
6206 if (n == 0) {
6207 if (y < 0) {
6208 // Unfortunately, we have to be careful when p has reached
6209 // infinity in the computation, because sometimes the higher
6210 // internal precision in the pow() implementation would have
6211 // given us a finite p. This happens very rarely.
6212 double result = 1.0 / p;
6213 return (result == 0 && isinf(p))
6214 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
6215 : result;
6216 } else {
6217 return p;
6218 }
6219 }
6220 m *= m;
6221 }
6222}
6223
6224
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006225static Object* Runtime_Math_pow(Arguments args) {
6226 NoHandleAllocation ha;
6227 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006228 Counters::math_pow.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006229
6230 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006231
6232 // If the second argument is a smi, it is much faster to call the
6233 // custom powi() function than the generic pow().
6234 if (args[1]->IsSmi()) {
6235 int y = Smi::cast(args[1])->value();
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00006236 return Heap::NumberFromDouble(powi(x, y));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006237 }
6238
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006239 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00006240
6241 if (!isinf(x)) {
6242 if (y == 0.5) {
6243 // It's not uncommon to use Math.pow(x, 0.5) to compute the
6244 // square root of a number. To speed up such computations, we
6245 // explictly check for this case and use the sqrt() function
6246 // which is faster than pow().
6247 return Heap::AllocateHeapNumber(sqrt(x));
6248 } else if (y == -0.5) {
6249 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
6250 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
6251 }
6252 }
6253
6254 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006255 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006256 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6257 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006258 } else {
6259 return Heap::AllocateHeapNumber(pow(x, y));
6260 }
6261}
6262
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006263// Fast version of Math.pow if we know that y is not an integer and
6264// y is not -0.5 or 0.5. Used as slowcase from codegen.
6265static Object* Runtime_Math_pow_cfunction(Arguments args) {
6266 NoHandleAllocation ha;
6267 ASSERT(args.length() == 2);
6268 CONVERT_DOUBLE_CHECKED(x, args[0]);
6269 CONVERT_DOUBLE_CHECKED(y, args[1]);
6270 if (y == 0) {
6271 return Smi::FromInt(1);
6272 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6273 return Heap::nan_value();
6274 } else {
6275 return Heap::AllocateHeapNumber(pow(x, y));
6276 }
6277}
6278
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006279
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006280static Object* Runtime_RoundNumber(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006281 NoHandleAllocation ha;
6282 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006283 Counters::math_round.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006284
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006285 if (!args[0]->IsHeapNumber()) {
6286 // Must be smi. Return the argument unchanged for all the other types
6287 // to make fuzz-natives test happy.
6288 return args[0];
6289 }
6290
6291 HeapNumber* number = reinterpret_cast<HeapNumber*>(args[0]);
6292
6293 double value = number->value();
6294 int exponent = number->get_exponent();
6295 int sign = number->get_sign();
6296
6297 // We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
6298 // should be rounded to 2^30, which is not smi.
6299 if (!sign && exponent <= kSmiValueSize - 3) {
6300 return Smi::FromInt(static_cast<int>(value + 0.5));
6301 }
6302
6303 // If the magnitude is big enough, there's no place for fraction part. If we
6304 // try to add 0.5 to this number, 1.0 will be added instead.
6305 if (exponent >= 52) {
6306 return number;
6307 }
6308
6309 if (sign && value >= -0.5) return Heap::minus_zero_value();
6310
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006311 // Do not call NumberFromDouble() to avoid extra checks.
6312 return Heap::AllocateHeapNumber(floor(value + 0.5));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006313}
6314
6315
6316static Object* Runtime_Math_sin(Arguments args) {
6317 NoHandleAllocation ha;
6318 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006319 Counters::math_sin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006320
6321 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006322 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006323}
6324
6325
6326static Object* Runtime_Math_sqrt(Arguments args) {
6327 NoHandleAllocation ha;
6328 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006329 Counters::math_sqrt.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006330
6331 CONVERT_DOUBLE_CHECKED(x, args[0]);
6332 return Heap::AllocateHeapNumber(sqrt(x));
6333}
6334
6335
6336static Object* Runtime_Math_tan(Arguments args) {
6337 NoHandleAllocation ha;
6338 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006339 Counters::math_tan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006340
6341 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006342 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006343}
6344
6345
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006346static int MakeDay(int year, int month, int day) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006347 static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
6348 181, 212, 243, 273, 304, 334};
6349 static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
6350 182, 213, 244, 274, 305, 335};
6351
6352 year += month / 12;
6353 month %= 12;
6354 if (month < 0) {
6355 year--;
6356 month += 12;
6357 }
6358
6359 ASSERT(month >= 0);
6360 ASSERT(month < 12);
6361
6362 // year_delta is an arbitrary number such that:
6363 // a) year_delta = -1 (mod 400)
6364 // b) year + year_delta > 0 for years in the range defined by
6365 // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
6366 // Jan 1 1970. This is required so that we don't run into integer
6367 // division of negative numbers.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006368 // c) there shouldn't be an overflow for 32-bit integers in the following
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006369 // operations.
6370 static const int year_delta = 399999;
6371 static const int base_day = 365 * (1970 + year_delta) +
6372 (1970 + year_delta) / 4 -
6373 (1970 + year_delta) / 100 +
6374 (1970 + year_delta) / 400;
6375
6376 int year1 = year + year_delta;
6377 int day_from_year = 365 * year1 +
6378 year1 / 4 -
6379 year1 / 100 +
6380 year1 / 400 -
6381 base_day;
6382
6383 if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006384 return day_from_year + day_from_month[month] + day - 1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006385 }
6386
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006387 return day_from_year + day_from_month_leap[month] + day - 1;
6388}
6389
6390
6391static Object* Runtime_DateMakeDay(Arguments args) {
6392 NoHandleAllocation ha;
6393 ASSERT(args.length() == 3);
6394
6395 CONVERT_SMI_CHECKED(year, args[0]);
6396 CONVERT_SMI_CHECKED(month, args[1]);
6397 CONVERT_SMI_CHECKED(date, args[2]);
6398
6399 return Smi::FromInt(MakeDay(year, month, date));
6400}
6401
6402
6403static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
6404static const int kDaysIn4Years = 4 * 365 + 1;
6405static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
6406static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
6407static const int kDays1970to2000 = 30 * 365 + 7;
6408static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
6409 kDays1970to2000;
6410static const int kYearsOffset = 400000;
6411
6412static const char kDayInYear[] = {
6413 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6414 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6415 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,
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, 29, 30, 31,
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,
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, 31,
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,
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, 31,
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,
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, 31,
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,
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, 31,
6437
6438 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6439 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
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,
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, 29, 30, 31,
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,
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, 31,
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,
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, 31,
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,
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, 31,
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,
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, 31,
6462
6463 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6464 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
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,
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, 30, 31,
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,
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, 31,
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,
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, 31,
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,
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, 31,
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,
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, 31,
6487
6488 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6489 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
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,
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, 29, 30, 31,
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,
6496 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6497 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6498 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6499 22, 23, 24, 25, 26, 27, 28, 29, 30,
6500 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6501 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6502 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6503 22, 23, 24, 25, 26, 27, 28, 29, 30, 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,
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, 31,
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,
6510 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6511 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
6512
6513static const char kMonthInYear[] = {
6514 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,
6515 0, 0, 0, 0, 0, 0,
6516 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,
6517 1, 1, 1,
6518 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,
6519 2, 2, 2, 2, 2, 2,
6520 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,
6521 3, 3, 3, 3, 3,
6522 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,
6523 4, 4, 4, 4, 4, 4,
6524 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,
6525 5, 5, 5, 5, 5,
6526 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,
6527 6, 6, 6, 6, 6, 6,
6528 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,
6529 7, 7, 7, 7, 7, 7,
6530 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,
6531 8, 8, 8, 8, 8,
6532 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,
6533 9, 9, 9, 9, 9, 9,
6534 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6535 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6536 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6537 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6538
6539 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,
6540 0, 0, 0, 0, 0, 0,
6541 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,
6542 1, 1, 1,
6543 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,
6544 2, 2, 2, 2, 2, 2,
6545 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,
6546 3, 3, 3, 3, 3,
6547 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,
6548 4, 4, 4, 4, 4, 4,
6549 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,
6550 5, 5, 5, 5, 5,
6551 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,
6552 6, 6, 6, 6, 6, 6,
6553 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,
6554 7, 7, 7, 7, 7, 7,
6555 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,
6556 8, 8, 8, 8, 8,
6557 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,
6558 9, 9, 9, 9, 9, 9,
6559 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6560 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6561 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6562 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6563
6564 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,
6565 0, 0, 0, 0, 0, 0,
6566 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,
6567 1, 1, 1, 1,
6568 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,
6569 2, 2, 2, 2, 2, 2,
6570 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,
6571 3, 3, 3, 3, 3,
6572 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,
6573 4, 4, 4, 4, 4, 4,
6574 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,
6575 5, 5, 5, 5, 5,
6576 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,
6577 6, 6, 6, 6, 6, 6,
6578 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,
6579 7, 7, 7, 7, 7, 7,
6580 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,
6581 8, 8, 8, 8, 8,
6582 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,
6583 9, 9, 9, 9, 9, 9,
6584 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6585 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6586 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6587 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6588
6589 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,
6590 0, 0, 0, 0, 0, 0,
6591 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,
6592 1, 1, 1,
6593 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,
6594 2, 2, 2, 2, 2, 2,
6595 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,
6596 3, 3, 3, 3, 3,
6597 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,
6598 4, 4, 4, 4, 4, 4,
6599 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,
6600 5, 5, 5, 5, 5,
6601 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,
6602 6, 6, 6, 6, 6, 6,
6603 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,
6604 7, 7, 7, 7, 7, 7,
6605 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,
6606 8, 8, 8, 8, 8,
6607 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,
6608 9, 9, 9, 9, 9, 9,
6609 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6610 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6611 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6612 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
6613
6614
6615// This function works for dates from 1970 to 2099.
6616static inline void DateYMDFromTimeAfter1970(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006617 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006618#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006619 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006620#endif
6621
6622 year = 1970 + (4 * date + 2) / kDaysIn4Years;
6623 date %= kDaysIn4Years;
6624
6625 month = kMonthInYear[date];
6626 day = kDayInYear[date];
6627
6628 ASSERT(MakeDay(year, month, day) == save_date);
6629}
6630
6631
6632static inline void DateYMDFromTimeSlow(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006633 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006634#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006635 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006636#endif
6637
6638 date += kDaysOffset;
6639 year = 400 * (date / kDaysIn400Years) - kYearsOffset;
6640 date %= kDaysIn400Years;
6641
6642 ASSERT(MakeDay(year, 0, 1) + date == save_date);
6643
6644 date--;
6645 int yd1 = date / kDaysIn100Years;
6646 date %= kDaysIn100Years;
6647 year += 100 * yd1;
6648
6649 date++;
6650 int yd2 = date / kDaysIn4Years;
6651 date %= kDaysIn4Years;
6652 year += 4 * yd2;
6653
6654 date--;
6655 int yd3 = date / 365;
6656 date %= 365;
6657 year += yd3;
6658
6659 bool is_leap = (!yd1 || yd2) && !yd3;
6660
6661 ASSERT(date >= -1);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006662 ASSERT(is_leap || (date >= 0));
6663 ASSERT((date < 365) || (is_leap && (date < 366)));
6664 ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0))));
6665 ASSERT(is_leap || ((MakeDay(year, 0, 1) + date) == save_date));
6666 ASSERT(!is_leap || ((MakeDay(year, 0, 1) + date + 1) == save_date));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006667
6668 if (is_leap) {
6669 day = kDayInYear[2*365 + 1 + date];
6670 month = kMonthInYear[2*365 + 1 + date];
6671 } else {
6672 day = kDayInYear[date];
6673 month = kMonthInYear[date];
6674 }
6675
6676 ASSERT(MakeDay(year, month, day) == save_date);
6677}
6678
6679
6680static inline void DateYMDFromTime(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006681 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006682 if (date >= 0 && date < 32 * kDaysIn4Years) {
6683 DateYMDFromTimeAfter1970(date, year, month, day);
6684 } else {
6685 DateYMDFromTimeSlow(date, year, month, day);
6686 }
6687}
6688
6689
6690static Object* Runtime_DateYMDFromTime(Arguments args) {
6691 NoHandleAllocation ha;
6692 ASSERT(args.length() == 2);
6693
6694 CONVERT_DOUBLE_CHECKED(t, args[0]);
6695 CONVERT_CHECKED(JSArray, res_array, args[1]);
6696
6697 int year, month, day;
6698 DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
6699
6700 res_array->SetElement(0, Smi::FromInt(year));
6701 res_array->SetElement(1, Smi::FromInt(month));
6702 res_array->SetElement(2, Smi::FromInt(day));
6703
6704 return Heap::undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006705}
6706
6707
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006708static Object* Runtime_NewArgumentsFast(Arguments args) {
6709 NoHandleAllocation ha;
6710 ASSERT(args.length() == 3);
6711
6712 JSFunction* callee = JSFunction::cast(args[0]);
6713 Object** parameters = reinterpret_cast<Object**>(args[1]);
6714 const int length = Smi::cast(args[2])->value();
6715
6716 Object* result = Heap::AllocateArgumentsObject(callee, length);
6717 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006718 // Allocate the elements if needed.
6719 if (length > 0) {
6720 // Allocate the fixed array.
6721 Object* obj = Heap::AllocateRawFixedArray(length);
6722 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006723
6724 AssertNoAllocation no_gc;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006725 FixedArray* array = reinterpret_cast<FixedArray*>(obj);
6726 array->set_map(Heap::fixed_array_map());
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006727 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006728
6729 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006730 for (int i = 0; i < length; i++) {
6731 array->set(i, *--parameters, mode);
6732 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006733 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006734 }
6735 return result;
6736}
6737
6738
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006739static Object* Runtime_NewClosure(Arguments args) {
6740 HandleScope scope;
6741 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00006742 CONVERT_ARG_CHECKED(Context, context, 0);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006743 CONVERT_ARG_CHECKED(SharedFunctionInfo, shared, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006744
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00006745 PretenureFlag pretenure = (context->global_context() == *context)
6746 ? TENURED // Allocate global closures in old space.
6747 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006748 Handle<JSFunction> result =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006749 Factory::NewFunctionFromSharedFunctionInfo(shared, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006750 return *result;
6751}
6752
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006753static Object* Runtime_NewObjectFromBound(Arguments args) {
6754 HandleScope scope;
6755 ASSERT(args.length() == 2);
6756 CONVERT_ARG_CHECKED(JSFunction, function, 0);
6757 CONVERT_ARG_CHECKED(JSArray, params, 1);
6758
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006759 RUNTIME_ASSERT(params->HasFastElements());
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006760 FixedArray* fixed = FixedArray::cast(params->elements());
6761
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006762 int fixed_length = Smi::cast(params->length())->value();
6763 SmartPointer<Object**> param_data(NewArray<Object**>(fixed_length));
6764 for (int i = 0; i < fixed_length; i++) {
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006765 Handle<Object> val = Handle<Object>(fixed->get(i));
6766 param_data[i] = val.location();
6767 }
6768
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006769 bool exception = false;
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006770 Handle<Object> result = Execution::New(
whesse@chromium.orge90029b2010-08-02 11:52:17 +00006771 function, fixed_length, *param_data, &exception);
6772 if (exception) {
6773 return Failure::Exception();
6774 }
6775 ASSERT(!result.is_null());
whesse@chromium.orgba5a61b2010-07-26 11:44:40 +00006776 return *result;
6777}
6778
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006779
ager@chromium.org5c838252010-02-19 08:53:10 +00006780static Code* ComputeConstructStub(Handle<JSFunction> function) {
6781 Handle<Object> prototype = Factory::null_value();
6782 if (function->has_instance_prototype()) {
6783 prototype = Handle<Object>(function->instance_prototype());
6784 }
6785 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006786 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00006787 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006788 if (code->IsFailure()) {
6789 return Builtins::builtin(Builtins::JSConstructStubGeneric);
6790 }
6791 return Code::cast(code);
6792 }
6793
ager@chromium.org5c838252010-02-19 08:53:10 +00006794 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006795}
6796
6797
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006798static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006799 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006800 ASSERT(args.length() == 1);
6801
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006802 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006803
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006804 // If the constructor isn't a proper function we throw a type error.
6805 if (!constructor->IsJSFunction()) {
6806 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6807 Handle<Object> type_error =
6808 Factory::NewTypeError("not_constructor", arguments);
6809 return Top::Throw(*type_error);
6810 }
6811
6812 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006813
6814 // If function should not have prototype, construction is not allowed. In this
6815 // case generated code bailouts here, since function has no initial_map.
6816 if (!function->should_have_prototype()) {
6817 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6818 Handle<Object> type_error =
6819 Factory::NewTypeError("not_constructor", arguments);
6820 return Top::Throw(*type_error);
6821 }
6822
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006823#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006824 // Handle stepping into constructors if step into is active.
6825 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006826 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006827 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006828#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006829
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006830 if (function->has_initial_map()) {
6831 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006832 // The 'Function' function ignores the receiver object when
6833 // called using 'new' and creates a new JSFunction object that
6834 // is returned. The receiver object is only used for error
6835 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006836 // JSFunction. Factory::NewJSObject() should not be used to
6837 // allocate JSFunctions since it does not properly initialize
6838 // the shared part of the function. Since the receiver is
6839 // ignored anyway, we use the global object as the receiver
6840 // instead of a new JSFunction object. This way, errors are
6841 // reported the same way whether or not 'Function' is called
6842 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006843 return Top::context()->global();
6844 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006845 }
6846
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006847 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006848 Handle<SharedFunctionInfo> shared(function->shared());
6849 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006850
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006851 bool first_allocation = !function->has_initial_map();
6852 Handle<JSObject> result = Factory::NewJSObject(function);
6853 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006854 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00006855 ComputeConstructStub(Handle<JSFunction>(function)));
6856 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006857 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006858
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006859 Counters::constructed_objects.Increment();
6860 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006861
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006862 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006863}
6864
6865
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006866static Object* Runtime_LazyCompile(Arguments args) {
6867 HandleScope scope;
6868 ASSERT(args.length() == 1);
6869
6870 Handle<JSFunction> function = args.at<JSFunction>(0);
6871#ifdef DEBUG
6872 if (FLAG_trace_lazy) {
6873 PrintF("[lazy: ");
6874 function->shared()->name()->Print();
6875 PrintF("]\n");
6876 }
6877#endif
6878
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006879 // Compile the target function. Here we compile using CompileLazyInLoop in
6880 // order to get the optimized version. This helps code like delta-blue
6881 // that calls performance-critical routines through constructors. A
6882 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
6883 // direct call. Since the in-loop tracking takes place through CallICs
6884 // this means that things called through constructors are never known to
6885 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006886 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006887 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006888 return Failure::Exception();
6889 }
6890
6891 return function->code();
6892}
6893
6894
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006895static Object* Runtime_GetFunctionDelegate(Arguments args) {
6896 HandleScope scope;
6897 ASSERT(args.length() == 1);
6898 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6899 return *Execution::GetFunctionDelegate(args.at<Object>(0));
6900}
6901
6902
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00006903static Object* Runtime_GetConstructorDelegate(Arguments args) {
6904 HandleScope scope;
6905 ASSERT(args.length() == 1);
6906 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6907 return *Execution::GetConstructorDelegate(args.at<Object>(0));
6908}
6909
6910
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006911static Object* Runtime_NewContext(Arguments args) {
6912 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00006913 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006914
kasper.lund7276f142008-07-30 08:49:36 +00006915 CONVERT_CHECKED(JSFunction, function, args[0]);
ager@chromium.orgb5737492010-07-15 09:29:43 +00006916 int length = function->shared()->scope_info()->NumberOfContextSlots();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006917 Object* result = Heap::AllocateFunctionContext(length, function);
6918 if (result->IsFailure()) return result;
6919
6920 Top::set_context(Context::cast(result));
6921
kasper.lund7276f142008-07-30 08:49:36 +00006922 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006923}
6924
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006925static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006926 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006927 Object* js_object = object;
6928 if (!js_object->IsJSObject()) {
6929 js_object = js_object->ToObject();
6930 if (js_object->IsFailure()) {
6931 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006932 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006933 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006934 Handle<Object> result =
6935 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
6936 return Top::Throw(*result);
6937 }
6938 }
6939
6940 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006941 Heap::AllocateWithContext(Top::context(),
6942 JSObject::cast(js_object),
6943 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006944 if (result->IsFailure()) return result;
6945
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006946 Context* context = Context::cast(result);
6947 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006948
kasper.lund7276f142008-07-30 08:49:36 +00006949 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006950}
6951
6952
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006953static Object* Runtime_PushContext(Arguments args) {
6954 NoHandleAllocation ha;
6955 ASSERT(args.length() == 1);
6956 return PushContextHelper(args[0], false);
6957}
6958
6959
6960static Object* Runtime_PushCatchContext(Arguments args) {
6961 NoHandleAllocation ha;
6962 ASSERT(args.length() == 1);
6963 return PushContextHelper(args[0], true);
6964}
6965
6966
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006967static Object* Runtime_LookupContext(Arguments args) {
6968 HandleScope scope;
6969 ASSERT(args.length() == 2);
6970
6971 CONVERT_ARG_CHECKED(Context, context, 0);
6972 CONVERT_ARG_CHECKED(String, name, 1);
6973
6974 int index;
6975 PropertyAttributes attributes;
6976 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006977 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006978 context->Lookup(name, flags, &index, &attributes);
6979
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006980 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006981 ASSERT(holder->IsJSObject());
6982 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006983 }
6984
6985 // No intermediate context found. Use global object by default.
6986 return Top::context()->global();
6987}
6988
6989
ager@chromium.orga1645e22009-09-09 19:27:10 +00006990// A mechanism to return a pair of Object pointers in registers (if possible).
6991// How this is achieved is calling convention-dependent.
6992// All currently supported x86 compiles uses calling conventions that are cdecl
6993// variants where a 64-bit value is returned in two 32-bit registers
6994// (edx:eax on ia32, r1:r0 on ARM).
6995// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
6996// In Win64 calling convention, a struct of two pointers is returned in memory,
6997// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006998#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006999struct ObjectPair {
7000 Object* x;
7001 Object* y;
7002};
ager@chromium.orga1645e22009-09-09 19:27:10 +00007003
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007004static inline ObjectPair MakePair(Object* x, Object* y) {
7005 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00007006 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
7007 // In Win64 they are assigned to a hidden first argument.
7008 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007009}
7010#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007011typedef uint64_t ObjectPair;
7012static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007013 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007014 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007015}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007016#endif
7017
7018
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007019static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007020 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
7021 USE(attributes);
7022 return x->IsTheHole() ? Heap::undefined_value() : x;
7023}
7024
7025
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007026static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
7027 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007028 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007029 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007030 JSFunction* context_extension_function =
7031 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007032 // If the holder isn't a context extension object, we just return it
7033 // as the receiver. This allows arguments objects to be used as
7034 // receivers, but only if they are put in the context scope chain
7035 // explicitly via a with-statement.
7036 Object* constructor = holder->map()->constructor();
7037 if (constructor != context_extension_function) return holder;
7038 // Fall back to using the global object as the receiver if the
7039 // property turns out to be a local variable allocated in a context
7040 // extension object - introduced via eval.
7041 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007042}
7043
7044
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007045static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007046 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00007047 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007048
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007049 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00007050 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007051 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007052 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007053 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007054
7055 int index;
7056 PropertyAttributes attributes;
7057 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007058 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007059 context->Lookup(name, flags, &index, &attributes);
7060
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007061 // If the index is non-negative, the slot has been found in a local
7062 // variable or a parameter. Read it from the context object or the
7063 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007064 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007065 // If the "property" we were looking for is a local variable or an
7066 // argument in a context, the receiver is the global object; see
7067 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
7068 JSObject* receiver = Top::context()->global()->global_receiver();
7069 Object* value = (holder->IsContext())
7070 ? Context::cast(*holder)->get(index)
7071 : JSObject::cast(*holder)->GetElement(index);
7072 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007073 }
7074
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007075 // If the holder is found, we read the property from it.
7076 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007077 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007078 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007079 JSObject* receiver;
7080 if (object->IsGlobalObject()) {
7081 receiver = GlobalObject::cast(object)->global_receiver();
7082 } else if (context->is_exception_holder(*holder)) {
7083 receiver = Top::context()->global()->global_receiver();
7084 } else {
7085 receiver = ComputeReceiverForNonGlobal(object);
7086 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007087 // No need to unhole the value here. This is taken care of by the
7088 // GetProperty function.
7089 Object* value = object->GetProperty(*name);
7090 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007091 }
7092
7093 if (throw_error) {
7094 // The property doesn't exist - throw exception.
7095 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007096 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007097 return MakePair(Top::Throw(*reference_error), NULL);
7098 } else {
7099 // The property doesn't exist - return undefined
7100 return MakePair(Heap::undefined_value(), Heap::undefined_value());
7101 }
7102}
7103
7104
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007105static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007106 return LoadContextSlotHelper(args, true);
7107}
7108
7109
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007110static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007111 return LoadContextSlotHelper(args, false);
7112}
7113
7114
7115static Object* Runtime_StoreContextSlot(Arguments args) {
7116 HandleScope scope;
7117 ASSERT(args.length() == 3);
7118
7119 Handle<Object> value(args[0]);
7120 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007121 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007122
7123 int index;
7124 PropertyAttributes attributes;
7125 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007126 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007127 context->Lookup(name, flags, &index, &attributes);
7128
7129 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007130 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007131 // Ignore if read_only variable.
7132 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007133 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007134 }
7135 } else {
7136 ASSERT((attributes & READ_ONLY) == 0);
7137 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007138 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007139 USE(result);
7140 ASSERT(!result->IsFailure());
7141 }
7142 return *value;
7143 }
7144
7145 // Slow case: The property is not in a FixedArray context.
7146 // It is either in an JSObject extension context or it was not found.
7147 Handle<JSObject> context_ext;
7148
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007149 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007150 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007151 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007152 } else {
7153 // The property was not found. It needs to be stored in the global context.
7154 ASSERT(attributes == ABSENT);
7155 attributes = NONE;
7156 context_ext = Handle<JSObject>(Top::context()->global());
7157 }
7158
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007159 // Set the property, but ignore if read_only variable on the context
7160 // extension object itself.
7161 if ((attributes & READ_ONLY) == 0 ||
7162 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007163 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
7164 if (set.is_null()) {
7165 // Failure::Exception is converted to a null handle in the
7166 // handle-based methods such as SetProperty. We therefore need
7167 // to convert null handles back to exceptions.
7168 ASSERT(Top::has_pending_exception());
7169 return Failure::Exception();
7170 }
7171 }
7172 return *value;
7173}
7174
7175
7176static Object* Runtime_Throw(Arguments args) {
7177 HandleScope scope;
7178 ASSERT(args.length() == 1);
7179
7180 return Top::Throw(args[0]);
7181}
7182
7183
7184static Object* Runtime_ReThrow(Arguments args) {
7185 HandleScope scope;
7186 ASSERT(args.length() == 1);
7187
7188 return Top::ReThrow(args[0]);
7189}
7190
7191
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007192static Object* Runtime_PromoteScheduledException(Arguments args) {
7193 ASSERT_EQ(0, args.length());
7194 return Top::PromoteScheduledException();
7195}
7196
7197
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007198static Object* Runtime_ThrowReferenceError(Arguments args) {
7199 HandleScope scope;
7200 ASSERT(args.length() == 1);
7201
7202 Handle<Object> name(args[0]);
7203 Handle<Object> reference_error =
7204 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
7205 return Top::Throw(*reference_error);
7206}
7207
7208
7209static Object* Runtime_StackOverflow(Arguments args) {
7210 NoHandleAllocation na;
7211 return Top::StackOverflow();
7212}
7213
7214
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007215static Object* Runtime_StackGuard(Arguments args) {
7216 ASSERT(args.length() == 1);
7217
7218 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007219 if (StackGuard::IsStackOverflow()) {
7220 return Runtime_StackOverflow(args);
7221 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007222
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007223 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007224}
7225
7226
7227// NOTE: These PrintXXX functions are defined for all builds (not just
7228// DEBUG builds) because we may want to be able to trace function
7229// calls in all modes.
7230static void PrintString(String* str) {
7231 // not uncommon to have empty strings
7232 if (str->length() > 0) {
7233 SmartPointer<char> s =
7234 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
7235 PrintF("%s", *s);
7236 }
7237}
7238
7239
7240static void PrintObject(Object* obj) {
7241 if (obj->IsSmi()) {
7242 PrintF("%d", Smi::cast(obj)->value());
7243 } else if (obj->IsString() || obj->IsSymbol()) {
7244 PrintString(String::cast(obj));
7245 } else if (obj->IsNumber()) {
7246 PrintF("%g", obj->Number());
7247 } else if (obj->IsFailure()) {
7248 PrintF("<failure>");
7249 } else if (obj->IsUndefined()) {
7250 PrintF("<undefined>");
7251 } else if (obj->IsNull()) {
7252 PrintF("<null>");
7253 } else if (obj->IsTrue()) {
7254 PrintF("<true>");
7255 } else if (obj->IsFalse()) {
7256 PrintF("<false>");
7257 } else {
7258 PrintF("%p", obj);
7259 }
7260}
7261
7262
7263static int StackSize() {
7264 int n = 0;
7265 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
7266 return n;
7267}
7268
7269
7270static void PrintTransition(Object* result) {
7271 // indentation
7272 { const int nmax = 80;
7273 int n = StackSize();
7274 if (n <= nmax)
7275 PrintF("%4d:%*s", n, n, "");
7276 else
7277 PrintF("%4d:%*s", n, nmax, "...");
7278 }
7279
7280 if (result == NULL) {
7281 // constructor calls
7282 JavaScriptFrameIterator it;
7283 JavaScriptFrame* frame = it.frame();
7284 if (frame->IsConstructor()) PrintF("new ");
7285 // function name
7286 Object* fun = frame->function();
7287 if (fun->IsJSFunction()) {
7288 PrintObject(JSFunction::cast(fun)->shared()->name());
7289 } else {
7290 PrintObject(fun);
7291 }
7292 // function arguments
7293 // (we are intentionally only printing the actually
7294 // supplied parameters, not all parameters required)
7295 PrintF("(this=");
7296 PrintObject(frame->receiver());
7297 const int length = frame->GetProvidedParametersCount();
7298 for (int i = 0; i < length; i++) {
7299 PrintF(", ");
7300 PrintObject(frame->GetParameter(i));
7301 }
7302 PrintF(") {\n");
7303
7304 } else {
7305 // function result
7306 PrintF("} -> ");
7307 PrintObject(result);
7308 PrintF("\n");
7309 }
7310}
7311
7312
7313static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007314 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007315 NoHandleAllocation ha;
7316 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007317 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007318}
7319
7320
7321static Object* Runtime_TraceExit(Arguments args) {
7322 NoHandleAllocation ha;
7323 PrintTransition(args[0]);
7324 return args[0]; // return TOS
7325}
7326
7327
7328static Object* Runtime_DebugPrint(Arguments args) {
7329 NoHandleAllocation ha;
7330 ASSERT(args.length() == 1);
7331
7332#ifdef DEBUG
7333 if (args[0]->IsString()) {
7334 // If we have a string, assume it's a code "marker"
7335 // and print some interesting cpu debugging info.
7336 JavaScriptFrameIterator it;
7337 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007338 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
7339 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007340 } else {
7341 PrintF("DebugPrint: ");
7342 }
7343 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007344 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007345 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007346 HeapObject::cast(args[0])->map()->Print();
7347 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007348#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007349 // ShortPrint is available in release mode. Print is not.
7350 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007351#endif
7352 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00007353 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007354
7355 return args[0]; // return TOS
7356}
7357
7358
7359static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007360 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007361 NoHandleAllocation ha;
7362 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007363 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007364}
7365
7366
mads.s.ager31e71382008-08-13 09:32:07 +00007367static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007368 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007369 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007370
7371 // According to ECMA-262, section 15.9.1, page 117, the precision of
7372 // the number in a Date object representing a particular instant in
7373 // time is milliseconds. Therefore, we floor the result of getting
7374 // the OS time.
7375 double millis = floor(OS::TimeCurrentMillis());
7376 return Heap::NumberFromDouble(millis);
7377}
7378
7379
7380static Object* Runtime_DateParseString(Arguments args) {
7381 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007382 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007383
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007384 CONVERT_ARG_CHECKED(String, str, 0);
7385 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007386
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007387 CONVERT_ARG_CHECKED(JSArray, output, 1);
7388 RUNTIME_ASSERT(output->HasFastElements());
7389
7390 AssertNoAllocation no_allocation;
7391
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007392 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007393 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
7394 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007395 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007396 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007397 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007398 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007399 result = DateParser::Parse(str->ToUC16Vector(), output_array);
7400 }
7401
7402 if (result) {
7403 return *output;
7404 } else {
7405 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007406 }
7407}
7408
7409
7410static Object* Runtime_DateLocalTimezone(Arguments args) {
7411 NoHandleAllocation ha;
7412 ASSERT(args.length() == 1);
7413
7414 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007415 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007416 return Heap::AllocateStringFromUtf8(CStrVector(zone));
7417}
7418
7419
7420static Object* Runtime_DateLocalTimeOffset(Arguments args) {
7421 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007422 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007423
7424 return Heap::NumberFromDouble(OS::LocalTimeOffset());
7425}
7426
7427
7428static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
7429 NoHandleAllocation ha;
7430 ASSERT(args.length() == 1);
7431
7432 CONVERT_DOUBLE_CHECKED(x, args[0]);
7433 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
7434}
7435
7436
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007437static Object* Runtime_GlobalReceiver(Arguments args) {
7438 ASSERT(args.length() == 1);
7439 Object* global = args[0];
7440 if (!global->IsJSGlobalObject()) return Heap::null_value();
7441 return JSGlobalObject::cast(global)->global_receiver();
7442}
7443
7444
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007445static Object* Runtime_CompileString(Arguments args) {
7446 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007447 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007448 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007449 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007450
ager@chromium.org381abbb2009-02-25 13:23:22 +00007451 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007452 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007453 Compiler::ValidationState validate = (is_json->IsTrue())
7454 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007455 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
7456 context,
7457 true,
7458 validate);
7459 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007460 Handle<JSFunction> fun =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007461 Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007462 return *fun;
7463}
7464
7465
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007466static ObjectPair CompileGlobalEval(Handle<String> source,
7467 Handle<Object> receiver) {
7468 // Deal with a normal eval call with a string argument. Compile it
7469 // and return the compiled function bound in the local context.
7470 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
7471 source,
7472 Handle<Context>(Top::context()),
7473 Top::context()->IsGlobalContext(),
7474 Compiler::DONT_VALIDATE_JSON);
7475 if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
7476 Handle<JSFunction> compiled = Factory::NewFunctionFromSharedFunctionInfo(
7477 shared,
7478 Handle<Context>(Top::context()),
7479 NOT_TENURED);
7480 return MakePair(*compiled, *receiver);
7481}
7482
7483
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007484static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
7485 ASSERT(args.length() == 3);
7486 if (!args[0]->IsJSFunction()) {
7487 return MakePair(Top::ThrowIllegalOperation(), NULL);
7488 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007489
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007490 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007491 Handle<JSFunction> callee = args.at<JSFunction>(0);
7492 Handle<Object> receiver; // Will be overwritten.
7493
7494 // Compute the calling context.
7495 Handle<Context> context = Handle<Context>(Top::context());
7496#ifdef DEBUG
7497 // Make sure Top::context() agrees with the old code that traversed
7498 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007499 StackFrameLocator locator;
7500 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007501 ASSERT(Context::cast(frame->context()) == *context);
7502#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007503
7504 // Find where the 'eval' symbol is bound. It is unaliased only if
7505 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007506 int index = -1;
7507 PropertyAttributes attributes = ABSENT;
7508 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007509 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
7510 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007511 // Stop search when eval is found or when the global context is
7512 // reached.
7513 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007514 if (context->is_function_context()) {
7515 context = Handle<Context>(Context::cast(context->closure()->context()));
7516 } else {
7517 context = Handle<Context>(context->previous());
7518 }
7519 }
7520
iposva@chromium.org245aa852009-02-10 00:49:54 +00007521 // If eval could not be resolved, it has been deleted and we need to
7522 // throw a reference error.
7523 if (attributes == ABSENT) {
7524 Handle<Object> name = Factory::eval_symbol();
7525 Handle<Object> reference_error =
7526 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007527 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007528 }
7529
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007530 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007531 // 'eval' is not bound in the global context. Just call the function
7532 // with the given arguments. This is not necessarily the global eval.
7533 if (receiver->IsContext()) {
7534 context = Handle<Context>::cast(receiver);
7535 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007536 } else if (receiver->IsJSContextExtensionObject()) {
7537 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007538 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007539 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007540 }
7541
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007542 // 'eval' is bound in the global context, but it may have been overwritten.
7543 // Compare it to the builtin 'GlobalEval' function to make sure.
7544 if (*callee != Top::global_context()->global_eval_fun() ||
7545 !args[1]->IsString()) {
7546 return MakePair(*callee, Top::context()->global()->global_receiver());
7547 }
7548
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007549 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
7550}
7551
7552
7553static ObjectPair Runtime_ResolvePossiblyDirectEvalNoLookup(Arguments args) {
7554 ASSERT(args.length() == 3);
7555 if (!args[0]->IsJSFunction()) {
7556 return MakePair(Top::ThrowIllegalOperation(), NULL);
7557 }
7558
7559 HandleScope scope;
7560 Handle<JSFunction> callee = args.at<JSFunction>(0);
7561
7562 // 'eval' is bound in the global context, but it may have been overwritten.
7563 // Compare it to the builtin 'GlobalEval' function to make sure.
7564 if (*callee != Top::global_context()->global_eval_fun() ||
7565 !args[1]->IsString()) {
7566 return MakePair(*callee, Top::context()->global()->global_receiver());
7567 }
7568
7569 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007570}
7571
7572
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007573static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
7574 // This utility adjusts the property attributes for newly created Function
7575 // object ("new Function(...)") by changing the map.
7576 // All it does is changing the prototype property to enumerable
7577 // as specified in ECMA262, 15.3.5.2.
7578 HandleScope scope;
7579 ASSERT(args.length() == 1);
7580 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7581 ASSERT(func->map()->instance_type() ==
7582 Top::function_instance_map()->instance_type());
7583 ASSERT(func->map()->instance_size() ==
7584 Top::function_instance_map()->instance_size());
7585 func->set_map(*Top::function_instance_map());
7586 return *func;
7587}
7588
7589
lrn@chromium.orgc4e51ac2010-08-09 09:47:21 +00007590static Object* Runtime_AllocateInNewSpace(Arguments args) {
7591 // Allocate a block of memory in NewSpace (filled with a filler).
7592 // Use as fallback for allocation in generated code when NewSpace
7593 // is full.
7594 ASSERT(args.length() == 1);
7595 CONVERT_ARG_CHECKED(Smi, size_smi, 0);
7596 int size = size_smi->value();
7597 RUNTIME_ASSERT(IsAligned(size, kPointerSize));
7598 RUNTIME_ASSERT(size > 0);
7599 static const int kMinFreeNewSpaceAfterGC =
7600 Heap::InitialSemiSpaceSize() * 3/4;
7601 RUNTIME_ASSERT(size <= kMinFreeNewSpaceAfterGC);
7602 Object* allocation = Heap::new_space()->AllocateRaw(size);
7603 if (!allocation->IsFailure()) {
7604 Heap::CreateFillerObjectAt(HeapObject::cast(allocation)->address(), size);
7605 }
7606 return allocation;
7607}
7608
7609
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007610// Push an array unto an array of arrays if it is not already in the
7611// array. Returns true if the element was pushed on the stack and
7612// false otherwise.
7613static Object* Runtime_PushIfAbsent(Arguments args) {
7614 ASSERT(args.length() == 2);
7615 CONVERT_CHECKED(JSArray, array, args[0]);
7616 CONVERT_CHECKED(JSArray, element, args[1]);
7617 RUNTIME_ASSERT(array->HasFastElements());
7618 int length = Smi::cast(array->length())->value();
7619 FixedArray* elements = FixedArray::cast(array->elements());
7620 for (int i = 0; i < length; i++) {
7621 if (elements->get(i) == element) return Heap::false_value();
7622 }
7623 Object* obj = array->SetFastElement(length, element);
7624 if (obj->IsFailure()) return obj;
7625 return Heap::true_value();
7626}
7627
7628
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007629/**
7630 * A simple visitor visits every element of Array's.
7631 * The backend storage can be a fixed array for fast elements case,
7632 * or a dictionary for sparse array. Since Dictionary is a subtype
7633 * of FixedArray, the class can be used by both fast and slow cases.
7634 * The second parameter of the constructor, fast_elements, specifies
7635 * whether the storage is a FixedArray or Dictionary.
7636 *
7637 * An index limit is used to deal with the situation that a result array
7638 * length overflows 32-bit non-negative integer.
7639 */
7640class ArrayConcatVisitor {
7641 public:
7642 ArrayConcatVisitor(Handle<FixedArray> storage,
7643 uint32_t index_limit,
7644 bool fast_elements) :
7645 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007646 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007647
7648 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007649 if (i >= index_limit_ - index_offset_) return;
7650 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007651
7652 if (fast_elements_) {
7653 ASSERT(index < static_cast<uint32_t>(storage_->length()));
7654 storage_->set(index, *elm);
7655
7656 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007657 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
7658 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007659 Factory::DictionaryAtNumberPut(dict, index, elm);
7660 if (!result.is_identical_to(dict))
7661 storage_ = result;
7662 }
7663 }
7664
7665 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007666 if (index_limit_ - index_offset_ < delta) {
7667 index_offset_ = index_limit_;
7668 } else {
7669 index_offset_ += delta;
7670 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007671 }
7672
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007673 Handle<FixedArray> storage() { return storage_; }
7674
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007675 private:
7676 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007677 // Limit on the accepted indices. Elements with indices larger than the
7678 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007679 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007680 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007681 uint32_t index_offset_;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007682 const bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007683};
7684
7685
ager@chromium.org3811b432009-10-28 14:53:37 +00007686template<class ExternalArrayClass, class ElementType>
7687static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
7688 bool elements_are_ints,
7689 bool elements_are_guaranteed_smis,
7690 uint32_t range,
7691 ArrayConcatVisitor* visitor) {
7692 Handle<ExternalArrayClass> array(
7693 ExternalArrayClass::cast(receiver->elements()));
7694 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
7695
7696 if (visitor != NULL) {
7697 if (elements_are_ints) {
7698 if (elements_are_guaranteed_smis) {
7699 for (uint32_t j = 0; j < len; j++) {
7700 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
7701 visitor->visit(j, e);
7702 }
7703 } else {
7704 for (uint32_t j = 0; j < len; j++) {
7705 int64_t val = static_cast<int64_t>(array->get(j));
7706 if (Smi::IsValid(static_cast<intptr_t>(val))) {
7707 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
7708 visitor->visit(j, e);
7709 } else {
7710 Handle<Object> e(
7711 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
7712 visitor->visit(j, e);
7713 }
7714 }
7715 }
7716 } else {
7717 for (uint32_t j = 0; j < len; j++) {
7718 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
7719 visitor->visit(j, e);
7720 }
7721 }
7722 }
7723
7724 return len;
7725}
7726
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007727/**
7728 * A helper function that visits elements of a JSObject. Only elements
7729 * whose index between 0 and range (exclusive) are visited.
7730 *
7731 * If the third parameter, visitor, is not NULL, the visitor is called
7732 * with parameters, 'visitor_index_offset + element index' and the element.
7733 *
7734 * It returns the number of visisted elements.
7735 */
7736static uint32_t IterateElements(Handle<JSObject> receiver,
7737 uint32_t range,
7738 ArrayConcatVisitor* visitor) {
7739 uint32_t num_of_elements = 0;
7740
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007741 switch (receiver->GetElementsKind()) {
7742 case JSObject::FAST_ELEMENTS: {
7743 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
7744 uint32_t len = elements->length();
7745 if (range < len) {
7746 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007747 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007748
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007749 for (uint32_t j = 0; j < len; j++) {
7750 Handle<Object> e(elements->get(j));
7751 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007752 num_of_elements++;
7753 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007754 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007755 }
7756 }
7757 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007758 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007759 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007760 case JSObject::PIXEL_ELEMENTS: {
7761 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
7762 uint32_t len = pixels->length();
7763 if (range < len) {
7764 len = range;
7765 }
7766
7767 for (uint32_t j = 0; j < len; j++) {
7768 num_of_elements++;
7769 if (visitor != NULL) {
7770 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
7771 visitor->visit(j, e);
7772 }
7773 }
7774 break;
7775 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007776 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
7777 num_of_elements =
7778 IterateExternalArrayElements<ExternalByteArray, int8_t>(
7779 receiver, true, true, range, visitor);
7780 break;
7781 }
7782 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7783 num_of_elements =
7784 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
7785 receiver, true, true, range, visitor);
7786 break;
7787 }
7788 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
7789 num_of_elements =
7790 IterateExternalArrayElements<ExternalShortArray, int16_t>(
7791 receiver, true, true, range, visitor);
7792 break;
7793 }
7794 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7795 num_of_elements =
7796 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
7797 receiver, true, true, range, visitor);
7798 break;
7799 }
7800 case JSObject::EXTERNAL_INT_ELEMENTS: {
7801 num_of_elements =
7802 IterateExternalArrayElements<ExternalIntArray, int32_t>(
7803 receiver, true, false, range, visitor);
7804 break;
7805 }
7806 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7807 num_of_elements =
7808 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
7809 receiver, true, false, range, visitor);
7810 break;
7811 }
7812 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
7813 num_of_elements =
7814 IterateExternalArrayElements<ExternalFloatArray, float>(
7815 receiver, false, false, range, visitor);
7816 break;
7817 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007818 case JSObject::DICTIONARY_ELEMENTS: {
7819 Handle<NumberDictionary> dict(receiver->element_dictionary());
7820 uint32_t capacity = dict->Capacity();
7821 for (uint32_t j = 0; j < capacity; j++) {
7822 Handle<Object> k(dict->KeyAt(j));
7823 if (dict->IsKey(*k)) {
7824 ASSERT(k->IsNumber());
7825 uint32_t index = static_cast<uint32_t>(k->Number());
7826 if (index < range) {
7827 num_of_elements++;
7828 if (visitor) {
7829 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
7830 }
7831 }
7832 }
7833 }
7834 break;
7835 }
7836 default:
7837 UNREACHABLE();
7838 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007839 }
7840
7841 return num_of_elements;
7842}
7843
7844
7845/**
7846 * A helper function that visits elements of an Array object, and elements
7847 * on its prototypes.
7848 *
7849 * Elements on prototypes are visited first, and only elements whose indices
7850 * less than Array length are visited.
7851 *
7852 * If a ArrayConcatVisitor object is given, the visitor is called with
7853 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007854 *
7855 * The returned number of elements is an upper bound on the actual number
7856 * of elements added. If the same element occurs in more than one object
7857 * in the array's prototype chain, it will be counted more than once, but
7858 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007859 */
7860static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
7861 ArrayConcatVisitor* visitor) {
7862 uint32_t range = static_cast<uint32_t>(array->length()->Number());
7863 Handle<Object> obj = array;
7864
7865 static const int kEstimatedPrototypes = 3;
7866 List< Handle<JSObject> > objects(kEstimatedPrototypes);
7867
7868 // Visit prototype first. If an element on the prototype is shadowed by
7869 // the inheritor using the same index, the ArrayConcatVisitor visits
7870 // the prototype element before the shadowing element.
7871 // The visitor can simply overwrite the old value by new value using
7872 // the same index. This follows Array::concat semantics.
7873 while (!obj->IsNull()) {
7874 objects.Add(Handle<JSObject>::cast(obj));
7875 obj = Handle<Object>(obj->GetPrototype());
7876 }
7877
7878 uint32_t nof_elements = 0;
7879 for (int i = objects.length() - 1; i >= 0; i--) {
7880 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007881 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007882 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007883
7884 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
7885 nof_elements = JSObject::kMaxElementCount;
7886 } else {
7887 nof_elements += encountered_elements;
7888 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007889 }
7890
7891 return nof_elements;
7892}
7893
7894
7895/**
7896 * A helper function of Runtime_ArrayConcat.
7897 *
7898 * The first argument is an Array of arrays and objects. It is the
7899 * same as the arguments array of Array::concat JS function.
7900 *
7901 * If an argument is an Array object, the function visits array
7902 * elements. If an argument is not an Array object, the function
7903 * visits the object as if it is an one-element array.
7904 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007905 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007906 * non-negative number is used as new length. For example, if one
7907 * array length is 2^32 - 1, second array length is 1, the
7908 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007909 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
7910 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007911 */
7912static uint32_t IterateArguments(Handle<JSArray> arguments,
7913 ArrayConcatVisitor* visitor) {
7914 uint32_t visited_elements = 0;
7915 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7916
7917 for (uint32_t i = 0; i < num_of_args; i++) {
7918 Handle<Object> obj(arguments->GetElement(i));
7919 if (obj->IsJSArray()) {
7920 Handle<JSArray> array = Handle<JSArray>::cast(obj);
7921 uint32_t len = static_cast<uint32_t>(array->length()->Number());
7922 uint32_t nof_elements =
7923 IterateArrayAndPrototypeElements(array, visitor);
7924 // Total elements of array and its prototype chain can be more than
7925 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007926 // the array length number of elements. We use the length as an estimate
7927 // for the actual number of elements added.
7928 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
7929 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
7930 visited_elements = JSArray::kMaxElementCount;
7931 } else {
7932 visited_elements += added_elements;
7933 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007934 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007935 } else {
7936 if (visitor) {
7937 visitor->visit(0, obj);
7938 visitor->increase_index_offset(1);
7939 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007940 if (visited_elements < JSArray::kMaxElementCount) {
7941 visited_elements++;
7942 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007943 }
7944 }
7945 return visited_elements;
7946}
7947
7948
7949/**
7950 * Array::concat implementation.
7951 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007952 * TODO(lrn): Fix non-compliance for very large concatenations and update to
7953 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007954 */
7955static Object* Runtime_ArrayConcat(Arguments args) {
7956 ASSERT(args.length() == 1);
7957 HandleScope handle_scope;
7958
7959 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
7960 Handle<JSArray> arguments(arg_arrays);
7961
7962 // Pass 1: estimate the number of elements of the result
7963 // (it could be more than real numbers if prototype has elements).
7964 uint32_t result_length = 0;
7965 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7966
7967 { AssertNoAllocation nogc;
7968 for (uint32_t i = 0; i < num_of_args; i++) {
7969 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007970 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007971 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007972 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007973 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
7974 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007975 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007976 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007977 if (JSObject::kMaxElementCount - result_length < length_estimate) {
7978 result_length = JSObject::kMaxElementCount;
7979 break;
7980 }
7981 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007982 }
7983 }
7984
7985 // Allocate an empty array, will set length and content later.
7986 Handle<JSArray> result = Factory::NewJSArray(0);
7987
7988 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
7989 // If estimated number of elements is more than half of length, a
7990 // fixed array (fast case) is more time and space-efficient than a
7991 // dictionary.
7992 bool fast_case = (estimate_nof_elements * 2) >= result_length;
7993
7994 Handle<FixedArray> storage;
7995 if (fast_case) {
7996 // The backing storage array must have non-existing elements to
7997 // preserve holes across concat operations.
7998 storage = Factory::NewFixedArrayWithHoles(result_length);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007999 result->set_map(*Factory::GetFastElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008000 } else {
8001 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
8002 uint32_t at_least_space_for = estimate_nof_elements +
8003 (estimate_nof_elements >> 2);
8004 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008005 Factory::NewNumberDictionary(at_least_space_for));
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00008006 result->set_map(*Factory::GetSlowElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008007 }
8008
8009 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
8010
8011 ArrayConcatVisitor visitor(storage, result_length, fast_case);
8012
8013 IterateArguments(arguments, &visitor);
8014
8015 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00008016 // Please note the storage might have changed in the visitor.
8017 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008018
8019 return *result;
8020}
8021
8022
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008023// This will not allocate (flatten the string), but it may run
8024// very slowly for very deeply nested ConsStrings. For debugging use only.
8025static Object* Runtime_GlobalPrint(Arguments args) {
8026 NoHandleAllocation ha;
8027 ASSERT(args.length() == 1);
8028
8029 CONVERT_CHECKED(String, string, args[0]);
8030 StringInputBuffer buffer(string);
8031 while (buffer.has_more()) {
8032 uint16_t character = buffer.GetNext();
8033 PrintF("%c", character);
8034 }
8035 return string;
8036}
8037
ager@chromium.org5ec48922009-05-05 07:25:34 +00008038// Moves all own elements of an object, that are below a limit, to positions
8039// starting at zero. All undefined values are placed after non-undefined values,
8040// and are followed by non-existing element. Does not change the length
8041// property.
8042// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008043static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00008044 ASSERT(args.length() == 2);
8045 CONVERT_CHECKED(JSObject, object, args[0]);
8046 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
8047 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008048}
8049
8050
8051// Move contents of argument 0 (an array) to argument 1 (an array)
8052static Object* Runtime_MoveArrayContents(Arguments args) {
8053 ASSERT(args.length() == 2);
8054 CONVERT_CHECKED(JSArray, from, args[0]);
8055 CONVERT_CHECKED(JSArray, to, args[1]);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00008056 HeapObject* new_elements = from->elements();
8057 Object* new_map;
8058 if (new_elements->map() == Heap::fixed_array_map()) {
8059 new_map = to->map()->GetFastElementsMap();
8060 } else {
8061 new_map = to->map()->GetSlowElementsMap();
8062 }
8063 if (new_map->IsFailure()) return new_map;
8064 to->set_map(Map::cast(new_map));
8065 to->set_elements(new_elements);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008066 to->set_length(from->length());
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00008067 Object* obj = from->ResetElements();
8068 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008069 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008070 return to;
8071}
8072
8073
8074// How many elements does this array have?
8075static Object* Runtime_EstimateNumberOfElements(Arguments args) {
8076 ASSERT(args.length() == 1);
8077 CONVERT_CHECKED(JSArray, array, args[0]);
8078 HeapObject* elements = array->elements();
8079 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008080 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008081 } else {
8082 return array->length();
8083 }
8084}
8085
8086
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008087static Object* Runtime_SwapElements(Arguments args) {
8088 HandleScope handle_scope;
8089
8090 ASSERT_EQ(3, args.length());
8091
ager@chromium.orgac091b72010-05-05 07:34:42 +00008092 CONVERT_ARG_CHECKED(JSObject, object, 0);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008093 Handle<Object> key1 = args.at<Object>(1);
8094 Handle<Object> key2 = args.at<Object>(2);
8095
8096 uint32_t index1, index2;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008097 if (!key1->ToArrayIndex(&index1)
8098 || !key2->ToArrayIndex(&index2)) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00008099 return Top::ThrowIllegalOperation();
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008100 }
8101
ager@chromium.orgac091b72010-05-05 07:34:42 +00008102 Handle<JSObject> jsobject = Handle<JSObject>::cast(object);
8103 Handle<Object> tmp1 = GetElement(jsobject, index1);
8104 Handle<Object> tmp2 = GetElement(jsobject, index2);
8105
8106 SetElement(jsobject, index1, tmp2);
8107 SetElement(jsobject, index2, tmp1);
8108
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008109 return Heap::undefined_value();
8110}
8111
8112
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008113// Returns an array that tells you where in the [0, length) interval an array
8114// might have elements. Can either return keys or intervals. Keys can have
8115// gaps in (undefined). Intervals can also span over some undefined keys.
8116static Object* Runtime_GetArrayKeys(Arguments args) {
8117 ASSERT(args.length() == 2);
8118 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00008119 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008120 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008121 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008122 // Create an array and get all the keys into it, then remove all the
8123 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008124 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008125 int keys_length = keys->length();
8126 for (int i = 0; i < keys_length; i++) {
8127 Object* key = keys->get(i);
8128 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008129 if (!key->ToArrayIndex(&index) || index >= length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008130 // Zap invalid keys.
8131 keys->set_undefined(i);
8132 }
8133 }
8134 return *Factory::NewJSArrayWithElements(keys);
8135 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008136 ASSERT(array->HasFastElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008137 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
8138 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008139 single_interval->set(0, Smi::FromInt(-1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008140 uint32_t actual_length =
8141 static_cast<uint32_t>(FixedArray::cast(array->elements())->length());
ager@chromium.org5ec48922009-05-05 07:25:34 +00008142 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008143 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00008144 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008145 single_interval->set(1, *length_object);
8146 return *Factory::NewJSArrayWithElements(single_interval);
8147 }
8148}
8149
8150
8151// DefineAccessor takes an optional final argument which is the
8152// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
8153// to the way accessors are implemented, it is set for both the getter
8154// and setter on the first call to DefineAccessor and ignored on
8155// subsequent calls.
8156static Object* Runtime_DefineAccessor(Arguments args) {
8157 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
8158 // Compute attributes.
8159 PropertyAttributes attributes = NONE;
8160 if (args.length() == 5) {
8161 CONVERT_CHECKED(Smi, attrs, args[4]);
8162 int value = attrs->value();
8163 // Only attribute bits should be set.
8164 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
8165 attributes = static_cast<PropertyAttributes>(value);
8166 }
8167
8168 CONVERT_CHECKED(JSObject, obj, args[0]);
8169 CONVERT_CHECKED(String, name, args[1]);
8170 CONVERT_CHECKED(Smi, flag, args[2]);
8171 CONVERT_CHECKED(JSFunction, fun, args[3]);
8172 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
8173}
8174
8175
8176static Object* Runtime_LookupAccessor(Arguments args) {
8177 ASSERT(args.length() == 3);
8178 CONVERT_CHECKED(JSObject, obj, args[0]);
8179 CONVERT_CHECKED(String, name, args[1]);
8180 CONVERT_CHECKED(Smi, flag, args[2]);
8181 return obj->LookupAccessor(name, flag->value() == 0);
8182}
8183
8184
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008185#ifdef ENABLE_DEBUGGER_SUPPORT
8186static Object* Runtime_DebugBreak(Arguments args) {
8187 ASSERT(args.length() == 0);
8188 return Execution::DebugBreakHelper();
8189}
8190
8191
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008192// Helper functions for wrapping and unwrapping stack frame ids.
8193static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008194 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008195 return Smi::FromInt(id >> 2);
8196}
8197
8198
8199static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
8200 return static_cast<StackFrame::Id>(wrapped->value() << 2);
8201}
8202
8203
8204// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00008205// args[0]: debug event listener function to set or null or undefined for
8206// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008207// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00008208static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008209 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00008210 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
8211 args[0]->IsUndefined() ||
8212 args[0]->IsNull());
8213 Handle<Object> callback = args.at<Object>(0);
8214 Handle<Object> data = args.at<Object>(1);
8215 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008216
8217 return Heap::undefined_value();
8218}
8219
8220
8221static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00008222 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008223 StackGuard::DebugBreak();
8224 return Heap::undefined_value();
8225}
8226
8227
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008228static Object* DebugLookupResultValue(Object* receiver, String* name,
8229 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00008230 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008231 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008232 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008233 case NORMAL:
8234 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008235 if (value->IsTheHole()) {
8236 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008237 }
8238 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008239 case FIELD:
8240 value =
8241 JSObject::cast(
8242 result->holder())->FastPropertyAt(result->GetFieldIndex());
8243 if (value->IsTheHole()) {
8244 return Heap::undefined_value();
8245 }
8246 return value;
8247 case CONSTANT_FUNCTION:
8248 return result->GetConstantFunction();
8249 case CALLBACKS: {
8250 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008251 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008252 value = receiver->GetPropertyWithCallback(
8253 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00008254 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008255 value = Top::pending_exception();
8256 Top::clear_pending_exception();
8257 if (caught_exception != NULL) {
8258 *caught_exception = true;
8259 }
8260 }
8261 return value;
8262 } else {
8263 return Heap::undefined_value();
8264 }
8265 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008266 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008267 case MAP_TRANSITION:
8268 case CONSTANT_TRANSITION:
8269 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008270 return Heap::undefined_value();
8271 default:
8272 UNREACHABLE();
8273 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008274 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008275 return Heap::undefined_value();
8276}
8277
8278
ager@chromium.org32912102009-01-16 10:38:43 +00008279// Get debugger related details for an object property.
8280// args[0]: object holding property
8281// args[1]: name of the property
8282//
8283// The array returned contains the following information:
8284// 0: Property value
8285// 1: Property details
8286// 2: Property value is exception
8287// 3: Getter function if defined
8288// 4: Setter function if defined
8289// Items 2-4 are only filled if the property has either a getter or a setter
8290// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00008291static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008292 HandleScope scope;
8293
8294 ASSERT(args.length() == 2);
8295
8296 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8297 CONVERT_ARG_CHECKED(String, name, 1);
8298
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008299 // Make sure to set the current context to the context before the debugger was
8300 // entered (if the debugger is entered). The reason for switching context here
8301 // is that for some property lookups (accessors and interceptors) callbacks
8302 // into the embedding application can occour, and the embedding application
8303 // could have the assumption that its own global context is the current
8304 // context and not some internal debugger context.
8305 SaveContext save;
8306 if (Debug::InDebugger()) {
8307 Top::set_context(*Debug::debugger_entry()->GetContext());
8308 }
8309
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008310 // Skip the global proxy as it has no properties and always delegates to the
8311 // real global object.
8312 if (obj->IsJSGlobalProxy()) {
8313 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
8314 }
8315
8316
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008317 // Check if the name is trivially convertible to an index and get the element
8318 // if so.
8319 uint32_t index;
8320 if (name->AsArrayIndex(&index)) {
8321 Handle<FixedArray> details = Factory::NewFixedArray(2);
8322 details->set(0, Runtime::GetElementOrCharAt(obj, index));
8323 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
8324 return *Factory::NewJSArrayWithElements(details);
8325 }
8326
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008327 // Find the number of objects making up this.
8328 int length = LocalPrototypeChainLength(*obj);
8329
8330 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008331 Handle<JSObject> jsproto = obj;
8332 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008333 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008334 jsproto->LocalLookup(*name, &result);
8335 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008336 // LookupResult is not GC safe as it holds raw object pointers.
8337 // GC can happen later in this code so put the required fields into
8338 // local variables using handles when required for later use.
8339 PropertyType result_type = result.type();
8340 Handle<Object> result_callback_obj;
8341 if (result_type == CALLBACKS) {
8342 result_callback_obj = Handle<Object>(result.GetCallbackObject());
8343 }
8344 Smi* property_details = result.GetPropertyDetails().AsSmi();
8345 // DebugLookupResultValue can cause GC so details from LookupResult needs
8346 // to be copied to handles before this.
8347 bool caught_exception = false;
8348 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
8349 &caught_exception);
8350 if (raw_value->IsFailure()) return raw_value;
8351 Handle<Object> value(raw_value);
8352
8353 // If the callback object is a fixed array then it contains JavaScript
8354 // getter and/or setter.
8355 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
8356 result_callback_obj->IsFixedArray();
8357 Handle<FixedArray> details =
8358 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
8359 details->set(0, *value);
8360 details->set(1, property_details);
8361 if (hasJavaScriptAccessors) {
8362 details->set(2,
8363 caught_exception ? Heap::true_value()
8364 : Heap::false_value());
8365 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
8366 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
8367 }
8368
8369 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008370 }
8371 if (i < length - 1) {
8372 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
8373 }
8374 }
8375
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008376 return Heap::undefined_value();
8377}
8378
8379
8380static Object* Runtime_DebugGetProperty(Arguments args) {
8381 HandleScope scope;
8382
8383 ASSERT(args.length() == 2);
8384
8385 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8386 CONVERT_ARG_CHECKED(String, name, 1);
8387
8388 LookupResult result;
8389 obj->Lookup(*name, &result);
8390 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008391 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008392 }
8393 return Heap::undefined_value();
8394}
8395
8396
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008397// Return the property type calculated from the property details.
8398// args[0]: smi with property details.
8399static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
8400 ASSERT(args.length() == 1);
8401 CONVERT_CHECKED(Smi, details, args[0]);
8402 PropertyType type = PropertyDetails(details).type();
8403 return Smi::FromInt(static_cast<int>(type));
8404}
8405
8406
8407// Return the property attribute calculated from the property details.
8408// args[0]: smi with property details.
8409static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
8410 ASSERT(args.length() == 1);
8411 CONVERT_CHECKED(Smi, details, args[0]);
8412 PropertyAttributes attributes = PropertyDetails(details).attributes();
8413 return Smi::FromInt(static_cast<int>(attributes));
8414}
8415
8416
8417// Return the property insertion index calculated from the property details.
8418// args[0]: smi with property details.
8419static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
8420 ASSERT(args.length() == 1);
8421 CONVERT_CHECKED(Smi, details, args[0]);
8422 int index = PropertyDetails(details).index();
8423 return Smi::FromInt(index);
8424}
8425
8426
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008427// Return property value from named interceptor.
8428// args[0]: object
8429// args[1]: property name
8430static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
8431 HandleScope scope;
8432 ASSERT(args.length() == 2);
8433 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8434 RUNTIME_ASSERT(obj->HasNamedInterceptor());
8435 CONVERT_ARG_CHECKED(String, name, 1);
8436
8437 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008438 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008439}
8440
8441
8442// Return element value from indexed interceptor.
8443// args[0]: object
8444// args[1]: index
8445static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
8446 HandleScope scope;
8447 ASSERT(args.length() == 2);
8448 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8449 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
8450 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
8451
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008452 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008453}
8454
8455
8456static Object* Runtime_CheckExecutionState(Arguments args) {
8457 ASSERT(args.length() >= 1);
8458 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00008459 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008460 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008461 return Top::Throw(Heap::illegal_execution_state_symbol());
8462 }
8463
8464 return Heap::true_value();
8465}
8466
8467
8468static Object* Runtime_GetFrameCount(Arguments args) {
8469 HandleScope scope;
8470 ASSERT(args.length() == 1);
8471
8472 // Check arguments.
8473 Object* result = Runtime_CheckExecutionState(args);
8474 if (result->IsFailure()) return result;
8475
8476 // Count all frames which are relevant to debugging stack trace.
8477 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008478 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008479 if (id == StackFrame::NO_ID) {
8480 // If there is no JavaScript stack frame count is 0.
8481 return Smi::FromInt(0);
8482 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008483 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
8484 return Smi::FromInt(n);
8485}
8486
8487
8488static const int kFrameDetailsFrameIdIndex = 0;
8489static const int kFrameDetailsReceiverIndex = 1;
8490static const int kFrameDetailsFunctionIndex = 2;
8491static const int kFrameDetailsArgumentCountIndex = 3;
8492static const int kFrameDetailsLocalCountIndex = 4;
8493static const int kFrameDetailsSourcePositionIndex = 5;
8494static const int kFrameDetailsConstructCallIndex = 6;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008495static const int kFrameDetailsAtReturnIndex = 7;
8496static const int kFrameDetailsDebuggerFrameIndex = 8;
8497static const int kFrameDetailsFirstDynamicIndex = 9;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008498
8499// Return an array with frame details
8500// args[0]: number: break id
8501// args[1]: number: frame index
8502//
8503// The array returned contains the following information:
8504// 0: Frame id
8505// 1: Receiver
8506// 2: Function
8507// 3: Argument count
8508// 4: Local count
8509// 5: Source position
8510// 6: Constructor call
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008511// 7: Is at return
8512// 8: Debugger frame
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008513// Arguments name, value
8514// Locals name, value
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008515// Return value if any
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008516static Object* Runtime_GetFrameDetails(Arguments args) {
8517 HandleScope scope;
8518 ASSERT(args.length() == 2);
8519
8520 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008521 Object* check = Runtime_CheckExecutionState(args);
8522 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008523 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8524
8525 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008526 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008527 if (id == StackFrame::NO_ID) {
8528 // If there are no JavaScript stack frames return undefined.
8529 return Heap::undefined_value();
8530 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008531 int count = 0;
8532 JavaScriptFrameIterator it(id);
8533 for (; !it.done(); it.Advance()) {
8534 if (count == index) break;
8535 count++;
8536 }
8537 if (it.done()) return Heap::undefined_value();
8538
8539 // Traverse the saved contexts chain to find the active context for the
8540 // selected frame.
8541 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008542 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008543 save = save->prev();
8544 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008545 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008546
8547 // Get the frame id.
8548 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
8549
8550 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008551 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008552
8553 // Check for constructor frame.
8554 bool constructor = it.frame()->IsConstructor();
8555
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008556 // Get scope info and read from it for local variable information.
8557 Handle<JSFunction> function(JSFunction::cast(it.frame()->function()));
ager@chromium.orgb5737492010-07-15 09:29:43 +00008558 Handle<SerializedScopeInfo> scope_info(function->shared()->scope_info());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008559 ScopeInfo<> info(*scope_info);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008560
8561 // Get the context.
8562 Handle<Context> context(Context::cast(it.frame()->context()));
8563
8564 // Get the locals names and values into a temporary array.
8565 //
8566 // TODO(1240907): Hide compiler-introduced stack variables
8567 // (e.g. .result)? For users of the debugger, they will probably be
8568 // confusing.
8569 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
8570 for (int i = 0; i < info.NumberOfLocals(); i++) {
8571 // Name of the local.
8572 locals->set(i * 2, *info.LocalName(i));
8573
8574 // Fetch the value of the local - either from the stack or from a
8575 // heap-allocated context.
8576 if (i < info.number_of_stack_slots()) {
8577 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
8578 } else {
8579 Handle<String> name = info.LocalName(i);
8580 // Traverse the context chain to the function context as all local
8581 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008582 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008583 context = Handle<Context>(context->previous());
8584 }
8585 ASSERT(context->is_function_context());
8586 locals->set(i * 2 + 1,
ager@chromium.orgb5737492010-07-15 09:29:43 +00008587 context->get(scope_info->ContextSlotIndex(*name, NULL)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008588 }
8589 }
8590
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008591 // Check whether this frame is positioned at return.
8592 int at_return = (index == 0) ? Debug::IsBreakAtReturn(it.frame()) : false;
8593
8594 // If positioned just before return find the value to be returned and add it
8595 // to the frame information.
8596 Handle<Object> return_value = Factory::undefined_value();
8597 if (at_return) {
8598 StackFrameIterator it2;
8599 Address internal_frame_sp = NULL;
8600 while (!it2.done()) {
8601 if (it2.frame()->is_internal()) {
8602 internal_frame_sp = it2.frame()->sp();
8603 } else {
8604 if (it2.frame()->is_java_script()) {
8605 if (it2.frame()->id() == it.frame()->id()) {
8606 // The internal frame just before the JavaScript frame contains the
8607 // value to return on top. A debug break at return will create an
8608 // internal frame to store the return value (eax/rax/r0) before
8609 // entering the debug break exit frame.
8610 if (internal_frame_sp != NULL) {
8611 return_value =
8612 Handle<Object>(Memory::Object_at(internal_frame_sp));
8613 break;
8614 }
8615 }
8616 }
8617
8618 // Indicate that the previous frame was not an internal frame.
8619 internal_frame_sp = NULL;
8620 }
8621 it2.Advance();
8622 }
8623 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008624
8625 // Now advance to the arguments adapter frame (if any). It contains all
8626 // the provided parameters whereas the function frame always have the number
8627 // of arguments matching the functions parameters. The rest of the
8628 // information (except for what is collected above) is the same.
8629 it.AdvanceToArgumentsFrame();
8630
8631 // Find the number of arguments to fill. At least fill the number of
8632 // parameters for the function and fill more if more parameters are provided.
8633 int argument_count = info.number_of_parameters();
8634 if (argument_count < it.frame()->GetProvidedParametersCount()) {
8635 argument_count = it.frame()->GetProvidedParametersCount();
8636 }
8637
8638 // Calculate the size of the result.
8639 int details_size = kFrameDetailsFirstDynamicIndex +
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008640 2 * (argument_count + info.NumberOfLocals()) +
8641 (at_return ? 1 : 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008642 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8643
8644 // Add the frame id.
8645 details->set(kFrameDetailsFrameIdIndex, *frame_id);
8646
8647 // Add the function (same as in function frame).
8648 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
8649
8650 // Add the arguments count.
8651 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
8652
8653 // Add the locals count
8654 details->set(kFrameDetailsLocalCountIndex,
8655 Smi::FromInt(info.NumberOfLocals()));
8656
8657 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00008658 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008659 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
8660 } else {
8661 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
8662 }
8663
8664 // Add the constructor information.
8665 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
8666
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008667 // Add the at return information.
8668 details->set(kFrameDetailsAtReturnIndex, Heap::ToBoolean(at_return));
8669
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008670 // Add information on whether this frame is invoked in the debugger context.
8671 details->set(kFrameDetailsDebuggerFrameIndex,
8672 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
8673
8674 // Fill the dynamic part.
8675 int details_index = kFrameDetailsFirstDynamicIndex;
8676
8677 // Add arguments name and value.
8678 for (int i = 0; i < argument_count; i++) {
8679 // Name of the argument.
8680 if (i < info.number_of_parameters()) {
8681 details->set(details_index++, *info.parameter_name(i));
8682 } else {
8683 details->set(details_index++, Heap::undefined_value());
8684 }
8685
8686 // Parameter value.
8687 if (i < it.frame()->GetProvidedParametersCount()) {
8688 details->set(details_index++, it.frame()->GetParameter(i));
8689 } else {
8690 details->set(details_index++, Heap::undefined_value());
8691 }
8692 }
8693
8694 // Add locals name and value from the temporary copy from the function frame.
8695 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
8696 details->set(details_index++, locals->get(i));
8697 }
8698
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008699 // Add the value being returned.
8700 if (at_return) {
8701 details->set(details_index++, *return_value);
8702 }
8703
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008704 // Add the receiver (same as in function frame).
8705 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
8706 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
8707 Handle<Object> receiver(it.frame()->receiver());
8708 if (!receiver->IsJSObject()) {
8709 // If the receiver is NOT a JSObject we have hit an optimization
8710 // where a value object is not converted into a wrapped JS objects.
8711 // To hide this optimization from the debugger, we wrap the receiver
8712 // by creating correct wrapper object based on the calling frame's
8713 // global context.
8714 it.Advance();
8715 Handle<Context> calling_frames_global_context(
8716 Context::cast(Context::cast(it.frame()->context())->global_context()));
8717 receiver = Factory::ToObject(receiver, calling_frames_global_context);
8718 }
8719 details->set(kFrameDetailsReceiverIndex, *receiver);
8720
8721 ASSERT_EQ(details_size, details_index);
8722 return *Factory::NewJSArrayWithElements(details);
8723}
8724
8725
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008726// Copy all the context locals into an object used to materialize a scope.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008727static void CopyContextLocalsToScopeObject(
8728 Handle<SerializedScopeInfo> serialized_scope_info,
8729 ScopeInfo<>& scope_info,
8730 Handle<Context> context,
8731 Handle<JSObject> scope_object) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008732 // Fill all context locals to the context extension.
8733 for (int i = Context::MIN_CONTEXT_SLOTS;
8734 i < scope_info.number_of_context_slots();
8735 i++) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00008736 int context_index = serialized_scope_info->ContextSlotIndex(
8737 *scope_info.context_slot_name(i), NULL);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008738
8739 // Don't include the arguments shadow (.arguments) context variable.
8740 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
8741 SetProperty(scope_object,
8742 scope_info.context_slot_name(i),
8743 Handle<Object>(context->get(context_index)), NONE);
8744 }
8745 }
8746}
8747
8748
8749// Create a plain JSObject which materializes the local scope for the specified
8750// frame.
8751static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
8752 Handle<JSFunction> function(JSFunction::cast(frame->function()));
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008753 Handle<SharedFunctionInfo> shared(function->shared());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008754 Handle<SerializedScopeInfo> serialized_scope_info(shared->scope_info());
8755 ScopeInfo<> scope_info(*serialized_scope_info);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008756
8757 // Allocate and initialize a JSObject with all the arguments, stack locals
8758 // heap locals and extension properties of the debugged function.
8759 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
8760
8761 // First fill all parameters.
8762 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8763 SetProperty(local_scope,
8764 scope_info.parameter_name(i),
8765 Handle<Object>(frame->GetParameter(i)), NONE);
8766 }
8767
8768 // Second fill all stack locals.
8769 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
8770 SetProperty(local_scope,
8771 scope_info.stack_slot_name(i),
8772 Handle<Object>(frame->GetExpression(i)), NONE);
8773 }
8774
8775 // Third fill all context locals.
8776 Handle<Context> frame_context(Context::cast(frame->context()));
8777 Handle<Context> function_context(frame_context->fcontext());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008778 CopyContextLocalsToScopeObject(serialized_scope_info, scope_info,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008779 function_context, local_scope);
8780
8781 // Finally copy any properties from the function context extension. This will
8782 // be variables introduced by eval.
8783 if (function_context->closure() == *function) {
8784 if (function_context->has_extension() &&
8785 !function_context->IsGlobalContext()) {
8786 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008787 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008788 for (int i = 0; i < keys->length(); i++) {
8789 // Names of variables introduced by eval are strings.
8790 ASSERT(keys->get(i)->IsString());
8791 Handle<String> key(String::cast(keys->get(i)));
8792 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
8793 }
8794 }
8795 }
8796 return local_scope;
8797}
8798
8799
8800// Create a plain JSObject which materializes the closure content for the
8801// context.
8802static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
8803 ASSERT(context->is_function_context());
8804
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008805 Handle<SharedFunctionInfo> shared(context->closure()->shared());
ager@chromium.orgb5737492010-07-15 09:29:43 +00008806 Handle<SerializedScopeInfo> serialized_scope_info(shared->scope_info());
8807 ScopeInfo<> scope_info(*serialized_scope_info);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008808
8809 // Allocate and initialize a JSObject with all the content of theis function
8810 // closure.
8811 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
8812
8813 // Check whether the arguments shadow object exists.
8814 int arguments_shadow_index =
ager@chromium.orgb5737492010-07-15 09:29:43 +00008815 shared->scope_info()->ContextSlotIndex(Heap::arguments_shadow_symbol(),
8816 NULL);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008817 if (arguments_shadow_index >= 0) {
8818 // In this case all the arguments are available in the arguments shadow
8819 // object.
8820 Handle<JSObject> arguments_shadow(
8821 JSObject::cast(context->get(arguments_shadow_index)));
8822 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8823 SetProperty(closure_scope,
8824 scope_info.parameter_name(i),
8825 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
8826 }
8827 }
8828
8829 // Fill all context locals to the context extension.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008830 CopyContextLocalsToScopeObject(serialized_scope_info, scope_info,
8831 context, closure_scope);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008832
8833 // Finally copy any properties from the function context extension. This will
8834 // be variables introduced by eval.
8835 if (context->has_extension()) {
8836 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008837 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008838 for (int i = 0; i < keys->length(); i++) {
8839 // Names of variables introduced by eval are strings.
8840 ASSERT(keys->get(i)->IsString());
8841 Handle<String> key(String::cast(keys->get(i)));
8842 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
8843 }
8844 }
8845
8846 return closure_scope;
8847}
8848
8849
8850// Iterate over the actual scopes visible from a stack frame. All scopes are
8851// backed by an actual context except the local scope, which is inserted
8852// "artifically" in the context chain.
8853class ScopeIterator {
8854 public:
8855 enum ScopeType {
8856 ScopeTypeGlobal = 0,
8857 ScopeTypeLocal,
8858 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00008859 ScopeTypeClosure,
8860 // Every catch block contains an implicit with block (its parameter is
8861 // a JSContextExtensionObject) that extends current scope with a variable
8862 // holding exception object. Such with blocks are treated as scopes of their
8863 // own type.
8864 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008865 };
8866
8867 explicit ScopeIterator(JavaScriptFrame* frame)
8868 : frame_(frame),
8869 function_(JSFunction::cast(frame->function())),
8870 context_(Context::cast(frame->context())),
8871 local_done_(false),
8872 at_local_(false) {
8873
8874 // Check whether the first scope is actually a local scope.
8875 if (context_->IsGlobalContext()) {
8876 // If there is a stack slot for .result then this local scope has been
8877 // created for evaluating top level code and it is not a real local scope.
8878 // Checking for the existence of .result seems fragile, but the scope info
8879 // saved with the code object does not otherwise have that information.
ager@chromium.orgb5737492010-07-15 09:29:43 +00008880 int index = function_->shared()->scope_info()->
8881 StackSlotIndex(Heap::result_symbol());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008882 at_local_ = index < 0;
8883 } else if (context_->is_function_context()) {
8884 at_local_ = true;
8885 }
8886 }
8887
8888 // More scopes?
8889 bool Done() { return context_.is_null(); }
8890
8891 // Move to the next scope.
8892 void Next() {
8893 // If at a local scope mark the local scope as passed.
8894 if (at_local_) {
8895 at_local_ = false;
8896 local_done_ = true;
8897
8898 // If the current context is not associated with the local scope the
8899 // current context is the next real scope, so don't move to the next
8900 // context in this case.
8901 if (context_->closure() != *function_) {
8902 return;
8903 }
8904 }
8905
8906 // The global scope is always the last in the chain.
8907 if (context_->IsGlobalContext()) {
8908 context_ = Handle<Context>();
8909 return;
8910 }
8911
8912 // Move to the next context.
8913 if (context_->is_function_context()) {
8914 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
8915 } else {
8916 context_ = Handle<Context>(context_->previous());
8917 }
8918
8919 // If passing the local scope indicate that the current scope is now the
8920 // local scope.
8921 if (!local_done_ &&
8922 (context_->IsGlobalContext() || (context_->is_function_context()))) {
8923 at_local_ = true;
8924 }
8925 }
8926
8927 // Return the type of the current scope.
8928 int Type() {
8929 if (at_local_) {
8930 return ScopeTypeLocal;
8931 }
8932 if (context_->IsGlobalContext()) {
8933 ASSERT(context_->global()->IsGlobalObject());
8934 return ScopeTypeGlobal;
8935 }
8936 if (context_->is_function_context()) {
8937 return ScopeTypeClosure;
8938 }
8939 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00008940 // Current scope is either an explicit with statement or a with statement
8941 // implicitely generated for a catch block.
8942 // If the extension object here is a JSContextExtensionObject then
8943 // current with statement is one frome a catch block otherwise it's a
8944 // regular with statement.
8945 if (context_->extension()->IsJSContextExtensionObject()) {
8946 return ScopeTypeCatch;
8947 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008948 return ScopeTypeWith;
8949 }
8950
8951 // Return the JavaScript object with the content of the current scope.
8952 Handle<JSObject> ScopeObject() {
8953 switch (Type()) {
8954 case ScopeIterator::ScopeTypeGlobal:
8955 return Handle<JSObject>(CurrentContext()->global());
8956 break;
8957 case ScopeIterator::ScopeTypeLocal:
8958 // Materialize the content of the local scope into a JSObject.
8959 return MaterializeLocalScope(frame_);
8960 break;
8961 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00008962 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008963 // Return the with object.
8964 return Handle<JSObject>(CurrentContext()->extension());
8965 break;
8966 case ScopeIterator::ScopeTypeClosure:
8967 // Materialize the content of the closure scope into a JSObject.
8968 return MaterializeClosure(CurrentContext());
8969 break;
8970 }
8971 UNREACHABLE();
8972 return Handle<JSObject>();
8973 }
8974
8975 // Return the context for this scope. For the local context there might not
8976 // be an actual context.
8977 Handle<Context> CurrentContext() {
8978 if (at_local_ && context_->closure() != *function_) {
8979 return Handle<Context>();
8980 }
8981 return context_;
8982 }
8983
8984#ifdef DEBUG
8985 // Debug print of the content of the current scope.
8986 void DebugPrint() {
8987 switch (Type()) {
8988 case ScopeIterator::ScopeTypeGlobal:
8989 PrintF("Global:\n");
8990 CurrentContext()->Print();
8991 break;
8992
8993 case ScopeIterator::ScopeTypeLocal: {
8994 PrintF("Local:\n");
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00008995 ScopeInfo<> scope_info(function_->shared()->scope_info());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008996 scope_info.Print();
8997 if (!CurrentContext().is_null()) {
8998 CurrentContext()->Print();
8999 if (CurrentContext()->has_extension()) {
9000 Handle<JSObject> extension =
9001 Handle<JSObject>(CurrentContext()->extension());
9002 if (extension->IsJSContextExtensionObject()) {
9003 extension->Print();
9004 }
9005 }
9006 }
9007 break;
9008 }
9009
9010 case ScopeIterator::ScopeTypeWith: {
9011 PrintF("With:\n");
9012 Handle<JSObject> extension =
9013 Handle<JSObject>(CurrentContext()->extension());
9014 extension->Print();
9015 break;
9016 }
9017
ager@chromium.orga1645e22009-09-09 19:27:10 +00009018 case ScopeIterator::ScopeTypeCatch: {
9019 PrintF("Catch:\n");
9020 Handle<JSObject> extension =
9021 Handle<JSObject>(CurrentContext()->extension());
9022 extension->Print();
9023 break;
9024 }
9025
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009026 case ScopeIterator::ScopeTypeClosure: {
9027 PrintF("Closure:\n");
9028 CurrentContext()->Print();
9029 if (CurrentContext()->has_extension()) {
9030 Handle<JSObject> extension =
9031 Handle<JSObject>(CurrentContext()->extension());
9032 if (extension->IsJSContextExtensionObject()) {
9033 extension->Print();
9034 }
9035 }
9036 break;
9037 }
9038
9039 default:
9040 UNREACHABLE();
9041 }
9042 PrintF("\n");
9043 }
9044#endif
9045
9046 private:
9047 JavaScriptFrame* frame_;
9048 Handle<JSFunction> function_;
9049 Handle<Context> context_;
9050 bool local_done_;
9051 bool at_local_;
9052
9053 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
9054};
9055
9056
9057static Object* Runtime_GetScopeCount(Arguments args) {
9058 HandleScope scope;
9059 ASSERT(args.length() == 2);
9060
9061 // Check arguments.
9062 Object* check = Runtime_CheckExecutionState(args);
9063 if (check->IsFailure()) return check;
9064 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9065
9066 // Get the frame where the debugging is performed.
9067 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9068 JavaScriptFrameIterator it(id);
9069 JavaScriptFrame* frame = it.frame();
9070
9071 // Count the visible scopes.
9072 int n = 0;
9073 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
9074 n++;
9075 }
9076
9077 return Smi::FromInt(n);
9078}
9079
9080
9081static const int kScopeDetailsTypeIndex = 0;
9082static const int kScopeDetailsObjectIndex = 1;
9083static const int kScopeDetailsSize = 2;
9084
9085// Return an array with scope details
9086// args[0]: number: break id
9087// args[1]: number: frame index
9088// args[2]: number: scope index
9089//
9090// The array returned contains the following information:
9091// 0: Scope type
9092// 1: Scope object
9093static Object* Runtime_GetScopeDetails(Arguments args) {
9094 HandleScope scope;
9095 ASSERT(args.length() == 3);
9096
9097 // Check arguments.
9098 Object* check = Runtime_CheckExecutionState(args);
9099 if (check->IsFailure()) return check;
9100 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9101 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
9102
9103 // Get the frame where the debugging is performed.
9104 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9105 JavaScriptFrameIterator frame_it(id);
9106 JavaScriptFrame* frame = frame_it.frame();
9107
9108 // Find the requested scope.
9109 int n = 0;
9110 ScopeIterator it(frame);
9111 for (; !it.Done() && n < index; it.Next()) {
9112 n++;
9113 }
9114 if (it.Done()) {
9115 return Heap::undefined_value();
9116 }
9117
9118 // Calculate the size of the result.
9119 int details_size = kScopeDetailsSize;
9120 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
9121
9122 // Fill in scope details.
9123 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
9124 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
9125
9126 return *Factory::NewJSArrayWithElements(details);
9127}
9128
9129
9130static Object* Runtime_DebugPrintScopes(Arguments args) {
9131 HandleScope scope;
9132 ASSERT(args.length() == 0);
9133
9134#ifdef DEBUG
9135 // Print the scopes for the top frame.
9136 StackFrameLocator locator;
9137 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
9138 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
9139 it.DebugPrint();
9140 }
9141#endif
9142 return Heap::undefined_value();
9143}
9144
9145
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009146static Object* Runtime_GetCFrames(Arguments args) {
9147 HandleScope scope;
9148 ASSERT(args.length() == 1);
9149 Object* result = Runtime_CheckExecutionState(args);
9150 if (result->IsFailure()) return result;
9151
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00009152#if V8_HOST_ARCH_64_BIT
9153 UNIMPLEMENTED();
9154 return Heap::undefined_value();
9155#else
9156
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009157 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009158 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
9159 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009160 if (frames_count == OS::kStackWalkError) {
9161 return Heap::undefined_value();
9162 }
9163
9164 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
9165 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
9166 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
9167 for (int i = 0; i < frames_count; i++) {
9168 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
9169 frame_value->SetProperty(
9170 *address_str,
9171 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
9172 NONE);
9173
9174 // Get the stack walk text for this frame.
9175 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009176 int frame_text_length = StrLength(frames[i].text);
9177 if (frame_text_length > 0) {
9178 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009179 frame_text = Factory::NewStringFromAscii(str);
9180 }
9181
9182 if (!frame_text.is_null()) {
9183 frame_value->SetProperty(*text_str, *frame_text, NONE);
9184 }
9185
9186 frames_array->set(i, *frame_value);
9187 }
9188 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00009189#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009190}
9191
9192
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00009193static Object* Runtime_GetThreadCount(Arguments args) {
9194 HandleScope scope;
9195 ASSERT(args.length() == 1);
9196
9197 // Check arguments.
9198 Object* result = Runtime_CheckExecutionState(args);
9199 if (result->IsFailure()) return result;
9200
9201 // Count all archived V8 threads.
9202 int n = 0;
9203 for (ThreadState* thread = ThreadState::FirstInUse();
9204 thread != NULL;
9205 thread = thread->Next()) {
9206 n++;
9207 }
9208
9209 // Total number of threads is current thread and archived threads.
9210 return Smi::FromInt(n + 1);
9211}
9212
9213
9214static const int kThreadDetailsCurrentThreadIndex = 0;
9215static const int kThreadDetailsThreadIdIndex = 1;
9216static const int kThreadDetailsSize = 2;
9217
9218// Return an array with thread details
9219// args[0]: number: break id
9220// args[1]: number: thread index
9221//
9222// The array returned contains the following information:
9223// 0: Is current thread?
9224// 1: Thread id
9225static Object* Runtime_GetThreadDetails(Arguments args) {
9226 HandleScope scope;
9227 ASSERT(args.length() == 2);
9228
9229 // Check arguments.
9230 Object* check = Runtime_CheckExecutionState(args);
9231 if (check->IsFailure()) return check;
9232 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
9233
9234 // Allocate array for result.
9235 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
9236
9237 // Thread index 0 is current thread.
9238 if (index == 0) {
9239 // Fill the details.
9240 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
9241 details->set(kThreadDetailsThreadIdIndex,
9242 Smi::FromInt(ThreadManager::CurrentId()));
9243 } else {
9244 // Find the thread with the requested index.
9245 int n = 1;
9246 ThreadState* thread = ThreadState::FirstInUse();
9247 while (index != n && thread != NULL) {
9248 thread = thread->Next();
9249 n++;
9250 }
9251 if (thread == NULL) {
9252 return Heap::undefined_value();
9253 }
9254
9255 // Fill the details.
9256 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
9257 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
9258 }
9259
9260 // Convert to JS array and return.
9261 return *Factory::NewJSArrayWithElements(details);
9262}
9263
9264
whesse@chromium.orge90029b2010-08-02 11:52:17 +00009265// Sets the disable break state
9266// args[0]: disable break state
9267static Object* Runtime_SetDisableBreak(Arguments args) {
9268 HandleScope scope;
9269 ASSERT(args.length() == 1);
9270 CONVERT_BOOLEAN_CHECKED(disable_break, args[0]);
9271 Debug::set_disable_break(disable_break);
9272 return Heap::undefined_value();
9273}
9274
9275
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009276static Object* Runtime_GetBreakLocations(Arguments args) {
9277 HandleScope scope;
9278 ASSERT(args.length() == 1);
9279
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009280 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9281 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009282 // Find the number of break points
9283 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
9284 if (break_locations->IsUndefined()) return Heap::undefined_value();
9285 // Return array as JS array
9286 return *Factory::NewJSArrayWithElements(
9287 Handle<FixedArray>::cast(break_locations));
9288}
9289
9290
9291// Set a break point in a function
9292// args[0]: function
9293// args[1]: number: break source position (within the function source)
9294// args[2]: number: break point object
9295static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
9296 HandleScope scope;
9297 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009298 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9299 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009300 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9301 RUNTIME_ASSERT(source_position >= 0);
9302 Handle<Object> break_point_object_arg = args.at<Object>(2);
9303
9304 // Set break point.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009305 Debug::SetBreakPoint(shared, break_point_object_arg, &source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009306
lrn@chromium.org32d961d2010-06-30 09:09:34 +00009307 return Smi::FromInt(source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009308}
9309
9310
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009311Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
9312 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009313 // Iterate the heap looking for SharedFunctionInfo generated from the
9314 // script. The inner most SharedFunctionInfo containing the source position
9315 // for the requested break point is found.
9316 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
9317 // which is found is not compiled it is compiled and the heap is iterated
9318 // again as the compilation might create inner functions from the newly
9319 // compiled function and the actual requested break point might be in one of
9320 // these functions.
9321 bool done = false;
9322 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00009323 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009324 Handle<SharedFunctionInfo> target;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009325 while (!done) {
9326 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009327 for (HeapObject* obj = iterator.next();
9328 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009329 if (obj->IsSharedFunctionInfo()) {
9330 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
9331 if (shared->script() == *script) {
9332 // If the SharedFunctionInfo found has the requested script data and
9333 // contains the source position it is a candidate.
9334 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00009335 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009336 start_position = shared->start_position();
9337 }
9338 if (start_position <= position &&
9339 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00009340 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009341 // candidate this is the new candidate.
9342 if (target.is_null()) {
9343 target_start_position = start_position;
9344 target = shared;
9345 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00009346 if (target_start_position == start_position &&
9347 shared->end_position() == target->end_position()) {
9348 // If a top-level function contain only one function
9349 // declartion the source for the top-level and the function is
9350 // the same. In that case prefer the non top-level function.
9351 if (!shared->is_toplevel()) {
9352 target_start_position = start_position;
9353 target = shared;
9354 }
9355 } else if (target_start_position <= start_position &&
9356 shared->end_position() <= target->end_position()) {
9357 // This containment check includes equality as a function inside
9358 // a top-level function can share either start or end position
9359 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009360 target_start_position = start_position;
9361 target = shared;
9362 }
9363 }
9364 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009365 }
9366 }
9367 }
9368
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009369 if (target.is_null()) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009370 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009371 }
9372
9373 // If the candidate found is compiled we are done. NOTE: when lazy
9374 // compilation of inner functions is introduced some additional checking
9375 // needs to be done here to compile inner functions.
9376 done = target->is_compiled();
9377 if (!done) {
9378 // If the candidate is not compiled compile it to reveal any inner
9379 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009380 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009381 }
9382 }
9383
9384 return *target;
9385}
9386
9387
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009388// Changes the state of a break point in a script and returns source position
9389// where break point was set. NOTE: Regarding performance see the NOTE for
9390// GetScriptFromScriptData.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009391// args[0]: script to set break point in
9392// args[1]: number: break source position (within the script source)
9393// args[2]: number: break point object
9394static Object* Runtime_SetScriptBreakPoint(Arguments args) {
9395 HandleScope scope;
9396 ASSERT(args.length() == 3);
9397 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
9398 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9399 RUNTIME_ASSERT(source_position >= 0);
9400 Handle<Object> break_point_object_arg = args.at<Object>(2);
9401
9402 // Get the script from the script wrapper.
9403 RUNTIME_ASSERT(wrapper->value()->IsScript());
9404 Handle<Script> script(Script::cast(wrapper->value()));
9405
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009406 Object* result = Runtime::FindSharedFunctionInfoInScript(
9407 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009408 if (!result->IsUndefined()) {
9409 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
9410 // Find position within function. The script position might be before the
9411 // source position of the first function.
9412 int position;
9413 if (shared->start_position() > source_position) {
9414 position = 0;
9415 } else {
9416 position = source_position - shared->start_position();
9417 }
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009418 Debug::SetBreakPoint(shared, break_point_object_arg, &position);
9419 position += shared->start_position();
9420 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009421 }
9422 return Heap::undefined_value();
9423}
9424
9425
9426// Clear a break point
9427// args[0]: number: break point object
9428static Object* Runtime_ClearBreakPoint(Arguments args) {
9429 HandleScope scope;
9430 ASSERT(args.length() == 1);
9431 Handle<Object> break_point_object_arg = args.at<Object>(0);
9432
9433 // Clear break point.
9434 Debug::ClearBreakPoint(break_point_object_arg);
9435
9436 return Heap::undefined_value();
9437}
9438
9439
9440// Change the state of break on exceptions
9441// args[0]: boolean indicating uncaught exceptions
9442// args[1]: boolean indicating on/off
9443static Object* Runtime_ChangeBreakOnException(Arguments args) {
9444 HandleScope scope;
9445 ASSERT(args.length() == 2);
9446 ASSERT(args[0]->IsNumber());
9447 ASSERT(args[1]->IsBoolean());
9448
9449 // Update break point state
9450 ExceptionBreakType type =
9451 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
9452 bool enable = args[1]->ToBoolean()->IsTrue();
9453 Debug::ChangeBreakOnException(type, enable);
9454 return Heap::undefined_value();
9455}
9456
9457
9458// Prepare for stepping
9459// args[0]: break id for checking execution state
9460// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00009461// args[2]: number of times to perform the step, for step out it is the number
9462// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009463static Object* Runtime_PrepareStep(Arguments args) {
9464 HandleScope scope;
9465 ASSERT(args.length() == 3);
9466 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009467 Object* check = Runtime_CheckExecutionState(args);
9468 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009469 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
9470 return Top::Throw(Heap::illegal_argument_symbol());
9471 }
9472
9473 // Get the step action and check validity.
9474 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
9475 if (step_action != StepIn &&
9476 step_action != StepNext &&
9477 step_action != StepOut &&
9478 step_action != StepInMin &&
9479 step_action != StepMin) {
9480 return Top::Throw(Heap::illegal_argument_symbol());
9481 }
9482
9483 // Get the number of steps.
9484 int step_count = NumberToInt32(args[2]);
9485 if (step_count < 1) {
9486 return Top::Throw(Heap::illegal_argument_symbol());
9487 }
9488
ager@chromium.orga1645e22009-09-09 19:27:10 +00009489 // Clear all current stepping setup.
9490 Debug::ClearStepping();
9491
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009492 // Prepare step.
9493 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
9494 return Heap::undefined_value();
9495}
9496
9497
9498// Clear all stepping set by PrepareStep.
9499static Object* Runtime_ClearStepping(Arguments args) {
9500 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009501 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009502 Debug::ClearStepping();
9503 return Heap::undefined_value();
9504}
9505
9506
9507// Creates a copy of the with context chain. The copy of the context chain is
9508// is linked to the function context supplied.
9509static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
9510 Handle<Context> function_context) {
9511 // At the bottom of the chain. Return the function context to link to.
9512 if (context_chain->is_function_context()) {
9513 return function_context;
9514 }
9515
9516 // Recursively copy the with contexts.
9517 Handle<Context> previous(context_chain->previous());
9518 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
9519 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009520 CopyWithContextChain(function_context, previous),
9521 extension,
9522 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009523}
9524
9525
9526// Helper function to find or create the arguments object for
9527// Runtime_DebugEvaluate.
9528static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
9529 Handle<JSFunction> function,
ager@chromium.orgb5737492010-07-15 09:29:43 +00009530 Handle<SerializedScopeInfo> scope_info,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009531 const ScopeInfo<>* sinfo,
9532 Handle<Context> function_context) {
9533 // Try to find the value of 'arguments' to pass as parameter. If it is not
9534 // found (that is the debugged function does not reference 'arguments' and
9535 // does not support eval) then create an 'arguments' object.
9536 int index;
9537 if (sinfo->number_of_stack_slots() > 0) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00009538 index = scope_info->StackSlotIndex(Heap::arguments_symbol());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009539 if (index != -1) {
9540 return Handle<Object>(frame->GetExpression(index));
9541 }
9542 }
9543
9544 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
ager@chromium.orgb5737492010-07-15 09:29:43 +00009545 index = scope_info->ContextSlotIndex(Heap::arguments_symbol(), NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009546 if (index != -1) {
9547 return Handle<Object>(function_context->get(index));
9548 }
9549 }
9550
9551 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009552 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
9553 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009554
9555 AssertNoAllocation no_gc;
9556 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009557 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009558 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009559 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009560 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009561 return arguments;
9562}
9563
9564
9565// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00009566// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009567// extension part has all the parameters and locals of the function on the
9568// stack frame. A function which calls eval with the code to evaluate is then
9569// compiled in this context and called in this context. As this context
9570// replaces the context of the function on the stack frame a new (empty)
9571// function is created as well to be used as the closure for the context.
9572// This function and the context acts as replacements for the function on the
9573// stack frame presenting the same view of the values of parameters and
9574// local variables as if the piece of JavaScript was evaluated at the point
9575// where the function on the stack frame is currently stopped.
9576static Object* Runtime_DebugEvaluate(Arguments args) {
9577 HandleScope scope;
9578
9579 // Check the execution state and decode arguments frame and source to be
9580 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009581 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009582 Object* check_result = Runtime_CheckExecutionState(args);
9583 if (check_result->IsFailure()) return check_result;
9584 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9585 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009586 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
9587
9588 // Handle the processing of break.
9589 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009590
9591 // Get the frame where the debugging is performed.
9592 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9593 JavaScriptFrameIterator it(id);
9594 JavaScriptFrame* frame = it.frame();
9595 Handle<JSFunction> function(JSFunction::cast(frame->function()));
ager@chromium.orgb5737492010-07-15 09:29:43 +00009596 Handle<SerializedScopeInfo> scope_info(function->shared()->scope_info());
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009597 ScopeInfo<> sinfo(*scope_info);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009598
9599 // Traverse the saved contexts chain to find the active context for the
9600 // selected frame.
9601 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009602 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009603 save = save->prev();
9604 }
9605 ASSERT(save != NULL);
9606 SaveContext savex;
9607 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009608
9609 // Create the (empty) function replacing the function on the stack frame for
9610 // the purpose of evaluating in the context created below. It is important
9611 // that this function does not describe any parameters and local variables
9612 // in the context. If it does then this will cause problems with the lookup
9613 // in Context::Lookup, where context slots for parameters and local variables
9614 // are looked at before the extension object.
9615 Handle<JSFunction> go_between =
9616 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
9617 go_between->set_context(function->context());
9618#ifdef DEBUG
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009619 ScopeInfo<> go_between_sinfo(go_between->shared()->scope_info());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009620 ASSERT(go_between_sinfo.number_of_parameters() == 0);
9621 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
9622#endif
9623
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009624 // Materialize the content of the local scope into a JSObject.
9625 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009626
9627 // Allocate a new context for the debug evaluation and set the extension
9628 // object build.
9629 Handle<Context> context =
9630 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009631 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009632 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009633 Handle<Context> frame_context(Context::cast(frame->context()));
9634 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009635 context = CopyWithContextChain(frame_context, context);
9636
9637 // Wrap the evaluation statement in a new function compiled in the newly
9638 // created context. The function has one parameter which has to be called
9639 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00009640 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009641 // function(arguments,__source__) {return eval(__source__);}
9642 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00009643 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009644 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009645 Handle<String> function_source =
9646 Factory::NewStringFromAscii(Vector<const char>(source_str,
9647 source_str_length));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009648 Handle<SharedFunctionInfo> shared =
ager@chromium.org381abbb2009-02-25 13:23:22 +00009649 Compiler::CompileEval(function_source,
9650 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00009651 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00009652 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009653 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009654 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009655 Factory::NewFunctionFromSharedFunctionInfo(shared, context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009656
9657 // Invoke the result of the compilation to get the evaluation function.
9658 bool has_pending_exception;
9659 Handle<Object> receiver(frame->receiver());
9660 Handle<Object> evaluation_function =
9661 Execution::Call(compiled_function, receiver, 0, NULL,
9662 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009663 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009664
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00009665 Handle<Object> arguments = GetArgumentsObject(frame, function, scope_info,
9666 &sinfo, function_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009667
9668 // Invoke the evaluation function and return the result.
9669 const int argc = 2;
9670 Object** argv[argc] = { arguments.location(),
9671 Handle<Object>::cast(source).location() };
9672 Handle<Object> result =
9673 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
9674 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009675 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009676
9677 // Skip the global proxy as it has no properties and always delegates to the
9678 // real global object.
9679 if (result->IsJSGlobalProxy()) {
9680 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
9681 }
9682
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009683 return *result;
9684}
9685
9686
9687static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
9688 HandleScope scope;
9689
9690 // Check the execution state and decode arguments frame and source to be
9691 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009692 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009693 Object* check_result = Runtime_CheckExecutionState(args);
9694 if (check_result->IsFailure()) return check_result;
9695 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009696 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
9697
9698 // Handle the processing of break.
9699 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009700
9701 // Enter the top context from before the debugger was invoked.
9702 SaveContext save;
9703 SaveContext* top = &save;
9704 while (top != NULL && *top->context() == *Debug::debug_context()) {
9705 top = top->prev();
9706 }
9707 if (top != NULL) {
9708 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009709 }
9710
9711 // Get the global context now set to the top context from before the
9712 // debugger was invoked.
9713 Handle<Context> context = Top::global_context();
9714
9715 // Compile the source to be evaluated.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009716 Handle<SharedFunctionInfo> shared =
9717 Compiler::CompileEval(source,
9718 context,
9719 true,
9720 Compiler::DONT_VALIDATE_JSON);
9721 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009722 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009723 Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
9724 context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009725
9726 // Invoke the result of the compilation to get the evaluation function.
9727 bool has_pending_exception;
9728 Handle<Object> receiver = Top::global();
9729 Handle<Object> result =
9730 Execution::Call(compiled_function, receiver, 0, NULL,
9731 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009732 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009733 return *result;
9734}
9735
9736
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009737static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
9738 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009739 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009740
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009741 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009742 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009743
9744 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009745 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00009746 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
9747 // Get the script wrapper in a local handle before calling GetScriptWrapper,
9748 // because using
9749 // instances->set(i, *GetScriptWrapper(script))
9750 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
9751 // already have deferenced the instances handle.
9752 Handle<JSValue> wrapper = GetScriptWrapper(script);
9753 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009754 }
9755
9756 // Return result as a JS array.
9757 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
9758 Handle<JSArray>::cast(result)->SetContent(*instances);
9759 return *result;
9760}
9761
9762
9763// Helper function used by Runtime_DebugReferencedBy below.
9764static int DebugReferencedBy(JSObject* target,
9765 Object* instance_filter, int max_references,
9766 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009767 JSFunction* arguments_function) {
9768 NoHandleAllocation ha;
9769 AssertNoAllocation no_alloc;
9770
9771 // Iterate the heap.
9772 int count = 0;
9773 JSObject* last = NULL;
9774 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009775 HeapObject* heap_obj = NULL;
9776 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009777 (max_references == 0 || count < max_references)) {
9778 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009779 if (heap_obj->IsJSObject()) {
9780 // Skip context extension objects and argument arrays as these are
9781 // checked in the context of functions using them.
9782 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00009783 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009784 obj->map()->constructor() == arguments_function) {
9785 continue;
9786 }
9787
9788 // Check if the JS object has a reference to the object looked for.
9789 if (obj->ReferencesObject(target)) {
9790 // Check instance filter if supplied. This is normally used to avoid
9791 // references from mirror objects (see Runtime_IsInPrototypeChain).
9792 if (!instance_filter->IsUndefined()) {
9793 Object* V = obj;
9794 while (true) {
9795 Object* prototype = V->GetPrototype();
9796 if (prototype->IsNull()) {
9797 break;
9798 }
9799 if (instance_filter == prototype) {
9800 obj = NULL; // Don't add this object.
9801 break;
9802 }
9803 V = prototype;
9804 }
9805 }
9806
9807 if (obj != NULL) {
9808 // Valid reference found add to instance array if supplied an update
9809 // count.
9810 if (instances != NULL && count < instances_size) {
9811 instances->set(count, obj);
9812 }
9813 last = obj;
9814 count++;
9815 }
9816 }
9817 }
9818 }
9819
9820 // Check for circular reference only. This can happen when the object is only
9821 // referenced from mirrors and has a circular reference in which case the
9822 // object is not really alive and would have been garbage collected if not
9823 // referenced from the mirror.
9824 if (count == 1 && last == target) {
9825 count = 0;
9826 }
9827
9828 // Return the number of referencing objects found.
9829 return count;
9830}
9831
9832
9833// Scan the heap for objects with direct references to an object
9834// args[0]: the object to find references to
9835// args[1]: constructor function for instances to exclude (Mirror)
9836// args[2]: the the maximum number of objects to return
9837static Object* Runtime_DebugReferencedBy(Arguments args) {
9838 ASSERT(args.length() == 3);
9839
9840 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009841 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009842
9843 // Check parameters.
9844 CONVERT_CHECKED(JSObject, target, args[0]);
9845 Object* instance_filter = args[1];
9846 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
9847 instance_filter->IsJSObject());
9848 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
9849 RUNTIME_ASSERT(max_references >= 0);
9850
9851 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009852 JSObject* arguments_boilerplate =
9853 Top::context()->global_context()->arguments_boilerplate();
9854 JSFunction* arguments_function =
9855 JSFunction::cast(arguments_boilerplate->map()->constructor());
9856
9857 // Get the number of referencing objects.
9858 int count;
9859 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009860 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009861
9862 // Allocate an array to hold the result.
9863 Object* object = Heap::AllocateFixedArray(count);
9864 if (object->IsFailure()) return object;
9865 FixedArray* instances = FixedArray::cast(object);
9866
9867 // Fill the referencing objects.
9868 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009869 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009870
9871 // Return result as JS array.
9872 Object* result =
9873 Heap::AllocateJSObject(
9874 Top::context()->global_context()->array_function());
9875 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9876 return result;
9877}
9878
9879
9880// Helper function used by Runtime_DebugConstructedBy below.
9881static int DebugConstructedBy(JSFunction* constructor, int max_references,
9882 FixedArray* instances, int instances_size) {
9883 AssertNoAllocation no_alloc;
9884
9885 // Iterate the heap.
9886 int count = 0;
9887 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009888 HeapObject* heap_obj = NULL;
9889 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009890 (max_references == 0 || count < max_references)) {
9891 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009892 if (heap_obj->IsJSObject()) {
9893 JSObject* obj = JSObject::cast(heap_obj);
9894 if (obj->map()->constructor() == constructor) {
9895 // Valid reference found add to instance array if supplied an update
9896 // count.
9897 if (instances != NULL && count < instances_size) {
9898 instances->set(count, obj);
9899 }
9900 count++;
9901 }
9902 }
9903 }
9904
9905 // Return the number of referencing objects found.
9906 return count;
9907}
9908
9909
9910// Scan the heap for objects constructed by a specific function.
9911// args[0]: the constructor to find instances of
9912// args[1]: the the maximum number of objects to return
9913static Object* Runtime_DebugConstructedBy(Arguments args) {
9914 ASSERT(args.length() == 2);
9915
9916 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009917 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009918
9919 // Check parameters.
9920 CONVERT_CHECKED(JSFunction, constructor, args[0]);
9921 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
9922 RUNTIME_ASSERT(max_references >= 0);
9923
9924 // Get the number of referencing objects.
9925 int count;
9926 count = DebugConstructedBy(constructor, max_references, NULL, 0);
9927
9928 // Allocate an array to hold the result.
9929 Object* object = Heap::AllocateFixedArray(count);
9930 if (object->IsFailure()) return object;
9931 FixedArray* instances = FixedArray::cast(object);
9932
9933 // Fill the referencing objects.
9934 count = DebugConstructedBy(constructor, max_references, instances, count);
9935
9936 // Return result as JS array.
9937 Object* result =
9938 Heap::AllocateJSObject(
9939 Top::context()->global_context()->array_function());
9940 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9941 return result;
9942}
9943
9944
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009945// Find the effective prototype object as returned by __proto__.
9946// args[0]: the object to find the prototype for.
9947static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009948 ASSERT(args.length() == 1);
9949
9950 CONVERT_CHECKED(JSObject, obj, args[0]);
9951
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009952 // Use the __proto__ accessor.
9953 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009954}
9955
9956
9957static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009958 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009959 CPU::DebugBreak();
9960 return Heap::undefined_value();
9961}
9962
9963
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009964static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009965#ifdef DEBUG
9966 HandleScope scope;
9967 ASSERT(args.length() == 1);
9968 // Get the function and make sure it is compiled.
9969 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009970 Handle<SharedFunctionInfo> shared(func->shared());
9971 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009972 return Failure::Exception();
9973 }
9974 func->code()->PrintLn();
9975#endif // DEBUG
9976 return Heap::undefined_value();
9977}
ager@chromium.org9085a012009-05-11 19:22:57 +00009978
9979
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009980static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
9981#ifdef DEBUG
9982 HandleScope scope;
9983 ASSERT(args.length() == 1);
9984 // Get the function and make sure it is compiled.
9985 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009986 Handle<SharedFunctionInfo> shared(func->shared());
9987 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009988 return Failure::Exception();
9989 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009990 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009991#endif // DEBUG
9992 return Heap::undefined_value();
9993}
9994
9995
ager@chromium.org9085a012009-05-11 19:22:57 +00009996static Object* Runtime_FunctionGetInferredName(Arguments args) {
9997 NoHandleAllocation ha;
9998 ASSERT(args.length() == 1);
9999
10000 CONVERT_CHECKED(JSFunction, f, args[0]);
10001 return f->shared()->inferred_name();
10002}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010003
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010004
10005static int FindSharedFunctionInfosForScript(Script* script,
10006 FixedArray* buffer) {
10007 AssertNoAllocation no_allocations;
10008
10009 int counter = 0;
10010 int buffer_size = buffer->length();
10011 HeapIterator iterator;
10012 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
10013 ASSERT(obj != NULL);
10014 if (!obj->IsSharedFunctionInfo()) {
10015 continue;
10016 }
10017 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
10018 if (shared->script() != script) {
10019 continue;
10020 }
10021 if (counter < buffer_size) {
10022 buffer->set(counter, shared);
10023 }
10024 counter++;
10025 }
10026 return counter;
10027}
10028
10029// For a script finds all SharedFunctionInfo's in the heap that points
10030// to this script. Returns JSArray of SharedFunctionInfo wrapped
10031// in OpaqueReferences.
10032static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
10033 Arguments args) {
10034 ASSERT(args.length() == 1);
10035 HandleScope scope;
10036 CONVERT_CHECKED(JSValue, script_value, args[0]);
10037
10038 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
10039
10040 const int kBufferSize = 32;
10041
10042 Handle<FixedArray> array;
10043 array = Factory::NewFixedArray(kBufferSize);
10044 int number = FindSharedFunctionInfosForScript(*script, *array);
10045 if (number > kBufferSize) {
10046 array = Factory::NewFixedArray(number);
10047 FindSharedFunctionInfosForScript(*script, *array);
10048 }
10049
10050 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
10051 result->set_length(Smi::FromInt(number));
10052
10053 LiveEdit::WrapSharedFunctionInfos(result);
10054
10055 return *result;
10056}
10057
10058// For a script calculates compilation information about all its functions.
10059// The script source is explicitly specified by the second argument.
10060// The source of the actual script is not used, however it is important that
10061// all generated code keeps references to this particular instance of script.
10062// Returns a JSArray of compilation infos. The array is ordered so that
10063// each function with all its descendant is always stored in a continues range
10064// with the function itself going first. The root function is a script function.
10065static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
10066 ASSERT(args.length() == 2);
10067 HandleScope scope;
10068 CONVERT_CHECKED(JSValue, script, args[0]);
10069 CONVERT_ARG_CHECKED(String, source, 1);
10070 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
10071
10072 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
10073
10074 if (Top::has_pending_exception()) {
10075 return Failure::Exception();
10076 }
10077
10078 return result;
10079}
10080
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010081// Changes the source of the script to a new_source.
10082// If old_script_name is provided (i.e. is a String), also creates a copy of
10083// the script with its original source and sends notification to debugger.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010084static Object* Runtime_LiveEditReplaceScript(Arguments args) {
10085 ASSERT(args.length() == 3);
10086 HandleScope scope;
10087 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
10088 CONVERT_ARG_CHECKED(String, new_source, 1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010089 Handle<Object> old_script_name(args[2]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010090
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010091 CONVERT_CHECKED(Script, original_script_pointer,
10092 original_script_value->value());
10093 Handle<Script> original_script(original_script_pointer);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010094
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010095 Object* old_script = LiveEdit::ChangeScriptSource(original_script,
10096 new_source,
10097 old_script_name);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010098
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010099 if (old_script->IsScript()) {
10100 Handle<Script> script_handle(Script::cast(old_script));
10101 return *(GetScriptWrapper(script_handle));
10102 } else {
10103 return Heap::null_value();
10104 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010105}
10106
10107// Replaces code of SharedFunctionInfo with a new one.
10108static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
10109 ASSERT(args.length() == 2);
10110 HandleScope scope;
10111 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
10112 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
10113
ager@chromium.orgac091b72010-05-05 07:34:42 +000010114 return LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010115}
10116
10117// Connects SharedFunctionInfo to another script.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010118static Object* Runtime_LiveEditFunctionSetScript(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010119 ASSERT(args.length() == 2);
10120 HandleScope scope;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010121 Handle<Object> function_object(args[0]);
10122 Handle<Object> script_object(args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010123
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010124 if (function_object->IsJSValue()) {
10125 Handle<JSValue> function_wrapper = Handle<JSValue>::cast(function_object);
10126 if (script_object->IsJSValue()) {
10127 CONVERT_CHECKED(Script, script, JSValue::cast(*script_object)->value());
10128 script_object = Handle<Object>(script);
10129 }
10130
10131 LiveEdit::SetFunctionScript(function_wrapper, script_object);
10132 } else {
10133 // Just ignore this. We may not have a SharedFunctionInfo for some functions
10134 // and we check it in this function.
10135 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010136
10137 return Heap::undefined_value();
10138}
10139
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010140
10141// In a code of a parent function replaces original function as embedded object
10142// with a substitution one.
10143static Object* Runtime_LiveEditReplaceRefToNestedFunction(Arguments args) {
10144 ASSERT(args.length() == 3);
10145 HandleScope scope;
10146
10147 CONVERT_ARG_CHECKED(JSValue, parent_wrapper, 0);
10148 CONVERT_ARG_CHECKED(JSValue, orig_wrapper, 1);
10149 CONVERT_ARG_CHECKED(JSValue, subst_wrapper, 2);
10150
10151 LiveEdit::ReplaceRefToNestedFunction(parent_wrapper, orig_wrapper,
10152 subst_wrapper);
10153
10154 return Heap::undefined_value();
10155}
10156
10157
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010158// Updates positions of a shared function info (first parameter) according
10159// to script source change. Text change is described in second parameter as
10160// array of groups of 3 numbers:
10161// (change_begin, change_end, change_end_new_position).
10162// Each group describes a change in text; groups are sorted by change_begin.
10163static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
10164 ASSERT(args.length() == 2);
10165 HandleScope scope;
10166 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
10167 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
10168
ager@chromium.orgac091b72010-05-05 07:34:42 +000010169 return LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010170}
10171
10172
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010173// For array of SharedFunctionInfo's (each wrapped in JSValue)
10174// checks that none of them have activations on stacks (of any thread).
10175// Returns array of the same length with corresponding results of
10176// LiveEdit::FunctionPatchabilityStatus type.
ager@chromium.org357bf652010-04-12 11:30:10 +000010177static Object* Runtime_LiveEditCheckAndDropActivations(Arguments args) {
10178 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010179 HandleScope scope;
10180 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +000010181 CONVERT_BOOLEAN_CHECKED(do_drop, args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010182
ager@chromium.org357bf652010-04-12 11:30:10 +000010183 return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010184}
10185
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010186// Compares 2 strings line-by-line and returns diff in form of JSArray of
fschneider@chromium.org013f3e12010-04-26 13:27:52 +000010187// triplets (pos1, pos1_end, pos2_end) describing list of diff chunks.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010188static Object* Runtime_LiveEditCompareStringsLinewise(Arguments args) {
10189 ASSERT(args.length() == 2);
10190 HandleScope scope;
10191 CONVERT_ARG_CHECKED(String, s1, 0);
10192 CONVERT_ARG_CHECKED(String, s2, 1);
10193
10194 return *LiveEdit::CompareStringsLinewise(s1, s2);
10195}
10196
10197
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010198
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010199// A testing entry. Returns statement position which is the closest to
10200// source_position.
10201static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
10202 ASSERT(args.length() == 2);
10203 HandleScope scope;
10204 CONVERT_ARG_CHECKED(JSFunction, function, 0);
10205 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
10206
10207 Handle<Code> code(function->code());
10208
10209 RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
10210 int closest_pc = 0;
10211 int distance = kMaxInt;
10212 while (!it.done()) {
10213 int statement_position = static_cast<int>(it.rinfo()->data());
10214 // Check if this break point is closer that what was previously found.
10215 if (source_position <= statement_position &&
10216 statement_position - source_position < distance) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +000010217 closest_pc =
10218 static_cast<int>(it.rinfo()->pc() - code->instruction_start());
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010219 distance = statement_position - source_position;
10220 // Check whether we can't get any closer.
10221 if (distance == 0) break;
10222 }
10223 it.next();
10224 }
10225
10226 return Smi::FromInt(closest_pc);
10227}
10228
10229
ager@chromium.org357bf652010-04-12 11:30:10 +000010230// Calls specified function with or without entering the debugger.
10231// This is used in unit tests to run code as if debugger is entered or simply
10232// to have a stack with C++ frame in the middle.
10233static Object* Runtime_ExecuteInDebugContext(Arguments args) {
10234 ASSERT(args.length() == 2);
10235 HandleScope scope;
10236 CONVERT_ARG_CHECKED(JSFunction, function, 0);
10237 CONVERT_BOOLEAN_CHECKED(without_debugger, args[1]);
10238
10239 Handle<Object> result;
10240 bool pending_exception;
10241 {
10242 if (without_debugger) {
10243 result = Execution::Call(function, Top::global(), 0, NULL,
10244 &pending_exception);
10245 } else {
10246 EnterDebugger enter_debugger;
10247 result = Execution::Call(function, Top::global(), 0, NULL,
10248 &pending_exception);
10249 }
10250 }
10251 if (!pending_exception) {
10252 return *result;
10253 } else {
10254 return Failure::Exception();
10255 }
10256}
10257
10258
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010259#endif // ENABLE_DEBUGGER_SUPPORT
10260
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010261#ifdef ENABLE_LOGGING_AND_PROFILING
10262
10263static Object* Runtime_ProfilerResume(Arguments args) {
10264 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010265 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010266
10267 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010268 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10269 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010270 return Heap::undefined_value();
10271}
10272
10273
10274static Object* Runtime_ProfilerPause(Arguments args) {
10275 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010276 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010277
10278 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010279 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10280 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010281 return Heap::undefined_value();
10282}
10283
10284#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010285
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010286// Finds the script object from the script data. NOTE: This operation uses
10287// heap traversal to find the function generated for the source position
10288// for the requested break point. For lazily compiled functions several heap
10289// traversals might be required rendering this operation as a rather slow
10290// operation. However for setting break points which is normally done through
10291// some kind of user interaction the performance is not crucial.
10292static Handle<Object> Runtime_GetScriptFromScriptName(
10293 Handle<String> script_name) {
10294 // Scan the heap for Script objects to find the script with the requested
10295 // script data.
10296 Handle<Script> script;
10297 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010298 HeapObject* obj = NULL;
10299 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010300 // If a script is found check if it has the script data requested.
10301 if (obj->IsScript()) {
10302 if (Script::cast(obj)->name()->IsString()) {
10303 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
10304 script = Handle<Script>(Script::cast(obj));
10305 }
10306 }
10307 }
10308 }
10309
10310 // If no script with the requested script data is found return undefined.
10311 if (script.is_null()) return Factory::undefined_value();
10312
10313 // Return the script found.
10314 return GetScriptWrapper(script);
10315}
10316
10317
10318// Get the script object from script data. NOTE: Regarding performance
10319// see the NOTE for GetScriptFromScriptData.
10320// args[0]: script data for the script to find the source for
10321static Object* Runtime_GetScript(Arguments args) {
10322 HandleScope scope;
10323
10324 ASSERT(args.length() == 1);
10325
10326 CONVERT_CHECKED(String, script_name, args[0]);
10327
10328 // Find the requested script.
10329 Handle<Object> result =
10330 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
10331 return *result;
10332}
10333
10334
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010335// Determines whether the given stack frame should be displayed in
10336// a stack trace. The caller is the error constructor that asked
10337// for the stack trace to be collected. The first time a construct
10338// call to this function is encountered it is skipped. The seen_caller
10339// in/out parameter is used to remember if the caller has been seen
10340// yet.
10341static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
10342 bool* seen_caller) {
10343 // Only display JS frames.
10344 if (!raw_frame->is_java_script())
10345 return false;
10346 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
10347 Object* raw_fun = frame->function();
10348 // Not sure when this can happen but skip it just in case.
10349 if (!raw_fun->IsJSFunction())
10350 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010351 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010352 *seen_caller = true;
10353 return false;
10354 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010355 // Skip all frames until we've seen the caller. Also, skip the most
10356 // obvious builtin calls. Some builtin calls (such as Number.ADD
10357 // which is invoked using 'call') are very difficult to recognize
10358 // so we're leaving them in for now.
10359 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010360}
10361
10362
10363// Collect the raw data for a stack trace. Returns an array of three
10364// element segments each containing a receiver, function and native
10365// code offset.
10366static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010367 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010368 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010369 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
10370
10371 HandleScope scope;
10372
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000010373 limit = Max(limit, 0); // Ensure that limit is not negative.
10374 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010375 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010376
10377 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010378 // If the caller parameter is a function we skip frames until we're
10379 // under it before starting to collect.
10380 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010381 int cursor = 0;
10382 int frames_seen = 0;
10383 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010384 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010385 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010386 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010387 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010388 Object* recv = frame->receiver();
10389 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010390 Address pc = frame->pc();
10391 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010392 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010393 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010394 if (cursor + 2 < elements->length()) {
10395 elements->set(cursor++, recv);
10396 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010397 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010398 } else {
10399 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010400 Handle<Object> recv_handle(recv);
10401 Handle<Object> fun_handle(fun);
10402 SetElement(result, cursor++, recv_handle);
10403 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010404 SetElement(result, cursor++, Handle<Smi>(offset));
10405 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010406 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010407 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010408 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010409
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010410 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010411 return *result;
10412}
10413
10414
ager@chromium.org3811b432009-10-28 14:53:37 +000010415// Returns V8 version as a string.
10416static Object* Runtime_GetV8Version(Arguments args) {
10417 ASSERT_EQ(args.length(), 0);
10418
10419 NoHandleAllocation ha;
10420
10421 const char* version_string = v8::V8::GetVersion();
10422
10423 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
10424}
10425
10426
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010427static Object* Runtime_Abort(Arguments args) {
10428 ASSERT(args.length() == 2);
10429 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
10430 Smi::cast(args[1])->value());
10431 Top::PrintStack();
10432 OS::Abort();
10433 UNREACHABLE();
10434 return NULL;
10435}
10436
10437
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010438static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
10439 ASSERT(args.length() == 0);
10440 HandleScope::DeleteExtensions();
10441 return Heap::undefined_value();
10442}
10443
10444
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010445static Object* CacheMiss(FixedArray* cache_obj, int index, Object* key_obj) {
10446 ASSERT(index % 2 == 0); // index of the key
10447 ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
10448 ASSERT(index < cache_obj->length());
10449
10450 HandleScope scope;
10451
10452 Handle<FixedArray> cache(cache_obj);
10453 Handle<Object> key(key_obj);
10454 Handle<JSFunction> factory(JSFunction::cast(
10455 cache->get(JSFunctionResultCache::kFactoryIndex)));
10456 // TODO(antonm): consider passing a receiver when constructing a cache.
10457 Handle<Object> receiver(Top::global_context()->global());
10458
10459 Handle<Object> value;
10460 {
10461 // This handle is nor shared, nor used later, so it's safe.
10462 Object** argv[] = { key.location() };
10463 bool pending_exception = false;
10464 value = Execution::Call(factory,
10465 receiver,
10466 1,
10467 argv,
10468 &pending_exception);
10469 if (pending_exception) return Failure::Exception();
10470 }
10471
10472 cache->set(index, *key);
10473 cache->set(index + 1, *value);
10474 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(index));
10475
10476 return *value;
10477}
10478
10479
10480static Object* Runtime_GetFromCache(Arguments args) {
10481 // This is only called from codegen, so checks might be more lax.
10482 CONVERT_CHECKED(FixedArray, cache, args[0]);
10483 Object* key = args[1];
10484
10485 const int finger_index =
10486 Smi::cast(cache->get(JSFunctionResultCache::kFingerIndex))->value();
10487
10488 Object* o = cache->get(finger_index);
10489 if (o == key) {
10490 // The fastest case: hit the same place again.
10491 return cache->get(finger_index + 1);
10492 }
10493
10494 for (int i = finger_index - 2;
10495 i >= JSFunctionResultCache::kEntriesIndex;
10496 i -= 2) {
10497 o = cache->get(i);
10498 if (o == key) {
10499 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10500 return cache->get(i + 1);
10501 }
10502 }
10503
10504 const int size =
10505 Smi::cast(cache->get(JSFunctionResultCache::kCacheSizeIndex))->value();
10506 ASSERT(size <= cache->length());
10507
10508 for (int i = size - 2; i > finger_index; i -= 2) {
10509 o = cache->get(i);
10510 if (o == key) {
10511 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10512 return cache->get(i + 1);
10513 }
10514 }
10515
10516 // Cache miss. If we have spare room, put new data into it, otherwise
10517 // evict post finger entry which must be least recently used.
10518 if (size < cache->length()) {
10519 cache->set(JSFunctionResultCache::kCacheSizeIndex, Smi::FromInt(size + 2));
10520 return CacheMiss(cache, size, key);
10521 } else {
antonm@chromium.org397e23c2010-04-21 12:00:05 +000010522 int target_index = finger_index + JSFunctionResultCache::kEntrySize;
10523 if (target_index == cache->length()) {
10524 target_index = JSFunctionResultCache::kEntriesIndex;
10525 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010526 return CacheMiss(cache, target_index, key);
10527 }
10528}
10529
kasper.lund44510672008-07-25 07:37:58 +000010530#ifdef DEBUG
10531// ListNatives is ONLY used by the fuzz-natives.js in debug mode
10532// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010533static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +000010534 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010535 HandleScope scope;
10536 Handle<JSArray> result = Factory::NewJSArray(0);
10537 int index = 0;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010538 bool inline_runtime_functions = false;
ager@chromium.orga1645e22009-09-09 19:27:10 +000010539#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010540 { \
10541 HandleScope inner; \
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010542 Handle<String> name; \
10543 /* Inline runtime functions have an underscore in front of the name. */ \
10544 if (inline_runtime_functions) { \
10545 name = Factory::NewStringFromAscii( \
10546 Vector<const char>("_" #Name, StrLength("_" #Name))); \
10547 } else { \
10548 name = Factory::NewStringFromAscii( \
10549 Vector<const char>(#Name, StrLength(#Name))); \
10550 } \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010551 Handle<JSArray> pair = Factory::NewJSArray(0); \
10552 SetElement(pair, 0, name); \
10553 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
10554 SetElement(result, index++, pair); \
10555 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010556 inline_runtime_functions = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010557 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010558 inline_runtime_functions = true;
10559 INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010560#undef ADD_ENTRY
10561 return *result;
10562}
kasper.lund44510672008-07-25 07:37:58 +000010563#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010564
10565
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010566static Object* Runtime_Log(Arguments args) {
10567 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +000010568 CONVERT_CHECKED(String, format, args[0]);
10569 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010570 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010571 Logger::LogRuntime(chars, elms);
10572 return Heap::undefined_value();
10573}
10574
10575
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010576static Object* Runtime_IS_VAR(Arguments args) {
10577 UNREACHABLE(); // implemented as macro in the parser
10578 return NULL;
10579}
10580
10581
10582// ----------------------------------------------------------------------------
10583// Implementation of Runtime
10584
ager@chromium.orga1645e22009-09-09 19:27:10 +000010585#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010586 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +000010587 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010588
10589static Runtime::Function Runtime_functions[] = {
10590 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010591 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010592};
10593
10594#undef F
10595
10596
10597Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
10598 ASSERT(0 <= fid && fid < kNofFunctions);
10599 return &Runtime_functions[fid];
10600}
10601
10602
10603Runtime::Function* Runtime::FunctionForName(const char* name) {
10604 for (Function* f = Runtime_functions; f->name != NULL; f++) {
10605 if (strcmp(f->name, name) == 0) {
10606 return f;
10607 }
10608 }
10609 return NULL;
10610}
10611
10612
10613void Runtime::PerformGC(Object* result) {
10614 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010615 if (failure->IsRetryAfterGC()) {
10616 // Try to do a garbage collection; ignore it if it fails. The C
10617 // entry stub will throw an out-of-memory exception in that case.
10618 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
10619 } else {
10620 // Handle last resort GC and make sure to allow future allocations
10621 // to grow the heap without causing GCs (if possible).
10622 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +000010623 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010624 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010625}
10626
10627
10628} } // namespace v8::internal